9

I added JFXTextField in my javafx application but I got this error with no clue how to solve it

class com.jfoenix.skins.JFXTextFieldSkin (in module com.jfoenix) cannot access a member of class javafx.scene.control.skin.TextFieldSkin (in module javafx.controls) with modifiers "private"

Controller :

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>
Mohammad Ismail
  • 103
  • 2
  • 8
  • Which jfoenix version are you using? – José Pereda Apr 28 '19 at 12:39
  • 1
    i'm using version 9.0.8 @JoséPereda – Mohammad Ismail Apr 28 '19 at 12:41
  • 1
    You should be able to workaround this specific case with `--add-opens javafx.controls/javafx.scene.control.skin=com.jfoenix` to open the javafx.scene.control.skin package for deep reflection by code in module com.jfoenix. However, that's just a workaround until the maintainers of this library fix their issues. – Alan Bateman Apr 29 '19 at 07:41

1 Answers1

13

This issue has already been reported at the JFoenix's issue tracker:

For starters, JFoenix is not really ready for Java 11+. The released version is intended for Java 9, but it still works with Java 11 and JavaFX 11, providing you add the JavaFX dependencies.

However, under JDK 12 it fails to run, and the issue is not JavaFX related: even with JavaFX 11.0.2 it still fails.

The issue is related to the use of reflection to access the Text node of 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)

While this worked fine up until Java 11.0.2, with Java 12 a regression recent changes in unsafe prevents this from working, and causes textNode = null.

As @AlanBateman mentions in his comments below:

[The JFoenix maintainers] should replace their setAccessible method to call obj.setAccessible(true) so that the user gets the right exceptions when the library tries to hack internals that are not accessible to it. If you do that then the user can workaround those issues with --add-exports or --add-opens options until the maintainers of the library fix their issues.

For now this will mean sticking to JDK 11.

Alternatively, you could try to build your own JFoenix version, cloning the repo (branch 9.0.0) and making the necessary changes to make it work with JavaFX 11+ (out of scope of this answer...), and removing the use of reflection where possible.

For instance, the textNode can be directly retrieved with:

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

or, still rely on reflection, but with the proper changes mentioned:

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

combined with:

--add-exports=javafx.controls/javafx.scene.control.skin=$moduleName
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Not clear why JDK-8221530 is referenced here - that issue is about JNI code with no java frames on the stack trying to invoke caller sensitive methods. In the above, it looks like there are java stack frames. – Alan Bateman Apr 28 '19 at 18:40
  • @AlanBateman I'm trying to find a release note that explains the difference between JDK 11.0.2 and 12.0.1, related to `reflection` and/or `unsafe`. This is the [line](https://github.com/jfoenixadmin/JFoenix/blob/JFoenix-9.0.0/jfoenix/src/main/java/com/jfoenix/adapters/ReflectionHelper.java#L53) that doesn't work now. Any hint? – José Pereda Apr 28 '19 at 19:57
  • If I read that code correctly then it's trying to use Unsafe to hack a private/undocumented field in java.lang.reflect.Field. That hack does not work on recent JDK releases. They should replace their setAccessible method to call obj.setAccessible(true) so that the user gets the right exceptions when the library tries to hack internals that are not accessible to it. If you do that then the user can workaround those issues with --add-exports or --add-opens options until the maintainers of the library fix their issues. – Alan Bateman Apr 29 '19 at 06:29
  • 1
    Yes, that's exactly what they are doing, and your comment makes total sense, but that's up to them. You mentioned "recent JDK releases", but the hack still works on 11.0.2, so I was wondering what did change (so I can add it to my answer). – José Pereda Apr 29 '19 at 07:50
  • 6
    The JDK hides a number of very security sensitive private fields from core reflection. The private fields in jlr.Field were added to that list in JDK 12. This is something the JFoenix maintainers need to fix as it's ridiculous to be depending on hacks like this. – Alan Bateman Apr 29 '19 at 08:02
  • @AlanBateman Thanks, I've edited my answer according to your comments. – José Pereda Apr 29 '19 at 08:34
  • It's already been fixed with JFoenix 9.0.10 so that it is now compatible with Java 11+. – Tech Expert Wizard Nov 18 '20 at 19:42