IllegalAccessException для JFXTextField с java sdk 12

Я добавил JFXTextField в свое приложение javafx, но я получил эту ошибку, не зная, как ее решить.

класс com.jfoenix.skins.JFXTextFieldSkin (в модуле com.jfoenix) не может получить доступ к члену класса javafx.scene.control.skin.TextFieldSkin (в модуле javafx.controls) с модификаторами "private"

Контроллер:

package sample;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable {

@FXML
private Button clickMe;


@FXML
private JFXButton materialButton;

@FXML
private JFXTextField textField;


@Override
public void initialize(URL url, ResourceBundle resourceBundle) {

    materialButton.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent actionEvent)
        {
            String text = textField.getText().trim();
            System.out.println(text);
        }
    });

}
}

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXTextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: #fcda;" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Button fx:id="clickMe" layoutX="198.0" layoutY="188.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="225.0" style="-fx-background-color: #fffe;" text="Click Me" textFill="#722929" />
      <JFXButton fx:id="materialButton" layoutX="231.0" layoutY="82.0" prefHeight="54.0" prefWidth="139.0" style="-fx-background-color: #ffff;" textFill="#280c0c">
         <font>
            <Font size="25.0" />
         </font></JFXButton>
      <JFXTextField fx:id="textField" layoutX="233.0" layoutY="24.0" promptText="Enter UserName" />
   </children>
</AnchorPane>

person Mohammad Ismail    schedule 28.04.2019    source источник
comment
Какую версию jfoenix вы используете?   -  person José Pereda    schedule 28.04.2019
comment
я использую версию 9.0.8 @ JoséPereda   -  person Mohammad Ismail    schedule 28.04.2019
comment
У вас должна быть возможность обойти этот конкретный случай с помощью --add-opens javafx.controls/javafx.scene.control.skin=com.jfoenix, чтобы открыть пакет javafx.scene.control.skin для глубокого отражения с помощью кода в модуле com.jfoenix. Однако это всего лишь обходной путь, пока разработчики этой библиотеки не исправят свои проблемы.   -  person Alan Bateman    schedule 29.04.2019


Ответы (1)


Об этой проблеме уже сообщалось в системе отслеживания проблем JFoenix:

Во-первых, JFoenix не совсем готов к Java 11+. Выпущенная версия предназначена для Java 9, но по-прежнему работает с Java 11 и JavaFX 11, при условии добавления зависимостей JavaFX.

Однако под JDK 12 он не запускается, и проблема не связана с JavaFX: даже с JavaFX 11.0.2 он все равно не работает.

Проблема связана с использованием отражение для доступа к Text узлу TextFieldSkin:

textNode = ReflectionHelper.getFieldContent(TextFieldSkin.class, this, "textNode");
java.lang.IllegalAccessException: class com.jfoenix.adapters.ReflectionHelper (in module com.jfoenix) cannot access a member of class javafx.scene.control.skin.TextFieldSkin (in module javafx.controls) with modifiers "private"
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:355)
        at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639)
        at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
        at java.base/java.lang.reflect.Field.get(Field.java:416)
        at com.jfoenix/com.jfoenix.adapters.ReflectionHelper.getFieldContent(ReflectionHelper.java:98)
        at com.jfoenix/com.jfoenix.skins.JFXTextFieldSkin.<init>(JFXTextFieldSkin.java:59)

Хотя это работало нормально до Java 11.0.2, с Java 12 регрессией недавние изменения в unsafe препятствуют этому и вызывают textNode = null.

Как отмечает @AlanBateman в своих комментариях ниже:

[Сопровождающие JFoenix] должны заменить свой setAccessible метод на вызов obj.setAccessible(true), чтобы пользователь получал правильные исключения, когда библиотека пытается взломать внутренние компоненты, которые для него недоступны. Если вы это сделаете, то пользователь сможет обойти эти проблемы с помощью параметров --add-exports или --add-opens, пока сопровождающие библиотеки не исправят свои проблемы.

На данный момент это будет означать придерживаться JDK 11.

В качестве альтернативы вы можете попытаться создать свою собственную версию JFoenix, клонируя репо (ветка 9.0.0) и внося необходимые изменения, чтобы он работал с JavaFX 11+ (вне области этого ответа ...), и удалив использование отражения там, где это возможно.

Например, textNode можно напрямую получить с помощью:

textNode = textPane.getChildren().get(1);

или по-прежнему полагаться на размышления, но с указанием соответствующих изменений:

try {
    Field field = cls.getDeclaredField(fieldName);
    field.setAccessible(true); // <-- Use this.
    return (T) field.get(obj);
} catch (Throwable ex) { }

в сочетании с:

--add-exports=javafx.controls/javafx.scene.control.skin=$moduleName
person José Pereda    schedule 28.04.2019
comment
Непонятно, почему здесь упоминается JDK-8221530 - эта проблема связана с кодом JNI без java-фреймов в стеке, пытающимся вызвать методы, чувствительные к вызывающему. В приведенном выше примере, похоже, есть фреймы стека Java. - person Alan Bateman; 28.04.2019
comment
@AlanBateman Я пытаюсь найти примечание к выпуску, в котором объясняется разница между JDK 11.0.2 и 12.0.1, связанная с reflection и / или unsafe. Это строка, которая сейчас не работает. Намек? - person José Pereda; 28.04.2019
comment
Если я правильно прочитал этот код, он пытается использовать Unsafe для взлома частного / недокументированного поля в java.lang.reflect.Field. Этот прием не работает с последними выпусками JDK. Они должны заменить свой метод setAccessible на вызов obj.setAccessible (true), чтобы пользователь получал правильные исключения, когда библиотека пытается взломать внутренние компоненты, которые для него недоступны. Если вы это сделаете, то пользователь сможет обойти эти проблемы с помощью параметров --add-exports или --add-opens, пока сопровождающие библиотеки не исправят свои проблемы. - person Alan Bateman; 29.04.2019
comment
Да, именно это они и делают, и ваш комментарий имеет смысл, но это их дело. Вы упомянули недавние выпуски JDK, но взлом все еще работает с 11.0.2, поэтому мне было интересно, что изменилось (чтобы я мог добавить это к своему ответу). - person José Pereda; 29.04.2019
comment
JDK скрывает ряд очень чувствительных к безопасности частных полей от отражения ядра. Частные поля в jlr.Field были добавлены к этому списку в JDK 12. Это то, что специалисты по сопровождению JFoenix должны исправить, поскольку нелепо зависеть от подобных хаков. - person Alan Bateman; 29.04.2019
comment
@AlanBateman Спасибо, я отредактировал свой ответ в соответствии с вашими комментариями. - person José Pereda; 29.04.2019
comment
Это уже было исправлено в JFoenix 9.0.10, поэтому теперь оно совместимо с Java 11+. - person Tech Expert Wizard; 18.11.2020