0

I have an JAVAFX 11 FXML file describing a popup window I use in an application. After refactoring the project structure where I moved the FXML from the "app" project to another project, I get NullPointerException errors, because the XML loader fails to import java.lang.String (and others)

The FXML looks like this:

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

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<TitledPane fx:id="GeneticMain" 
animated="false"
maxHeight="-Infinity"
maxWidth="-Infinity"
minHeight="-Infinity" 
minWidth="-Infinity"
prefHeight="400.0"
prefWidth="400.0"
text="Genetic Scanner Config" 
xmlns="http://javafx.com/javafx/8.0.141"
xmlns:fx="http://javafx.com/fxml/1">
  <content>
    <AnchorPane minHeight="-Infinity" minWidth="-Infinity">
         <children>
            <GridPane layoutX="10.0"
...

Bottom of Stack trace:

Caused by: java.lang.NullPointerException
    at javafx.fxml.FXMLLoader.loadTypeForPackage(FXMLLoader.java:2931) ~[javafx-fxml-12.0.1-linux.jar:?]
    at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2920) ~[javafx-fxml-12.0.1-linux.jar:?]
    at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2861) ~[javafx-fxml-12.0.1-linux.jar:?]
    at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2707) ~[javafx-fxml-12.0.1-linux.jar:?]
    at javafx.fxml.FXMLLoader.processProcessingInstruction(FXMLLoader.java:2676) ~[javafx-fxml-12.0.1-linux.jar:?]
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2542) ~[javafx-fxml-12.0.1-linux.jar:?]

Effective POM-dependencies to Javafx look like this:

<properties>
   ...
   <dependencyversion.openjfx>12.0.1</dependencyversion.openjfx>
   ...
</properties>
...
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-base</artifactId>
    <version>12.0.2</version>
</dependency>
<dependency>
  <groupId>de.gsi.chart</groupId>
  <artifactId>chartfx-chart</artifactId>
  <version>11.0.3</version>
  <scope>compile</scope>
  <exclusions>
    <exclusion>
      <artifactId>commons-logging</artifactId>
      <groupId>commons-logging</groupId>
    </exclusion>
    <exclusion>
      <artifactId>javafx-base</artifactId>
      <groupId>org.openjfx</groupId>
    </exclusion>
    <exclusion>
      <artifactId>javafx-web</artifactId>
      <groupId>org.openjfx</groupId>
    </exclusion>
  </exclusions>
</dependency>

I'm happy for any suggestion...

WolfiG
  • 1,059
  • 14
  • 31
  • Show the full pom (at least _all_ the JavaFX dependencies, and the run plugins) – José Pereda Oct 09 '19 at 11:43
  • 1
    Why do you have only `javafx-base`? There are other JavaFX modules, including `javafx-fxml`... See https://openjfx.io/openjfx-docs/#maven – José Pereda Oct 09 '19 at 12:21
  • added `javafx-fxml` and `javafx-controls` to my POM. Still the same... – WolfiG Oct 09 '19 at 13:01
  • Did you check the docs I linked above? You need to use a plugin that manages for you the required VM arguments. If you run from Eclipse as Java application you will need to provide those arguments yourself. All is property documented here https://openjfx.io/openjfx-docs – José Pereda Oct 09 '19 at 14:16
  • 2
    Taking a look at the source code of `loadTypeForPackage` and `loadType` there is only one possible reason for this exception: `FXMLLoader` cannot get a `ClassLoader` that is != `null`... (Verify this by using `System.out.println(new FXMLLoader().getClassLoader());`; Any value but `null` and I'm wrong.) Do you use a security manager that does not allow access to the classloader? If so you need to set the `classLoader` property of the loader manually before loading the fxml... – fabian Oct 09 '19 at 17:40
  • @fabian: indeed, the `classLoader` was null. Hence, I set it manually: `FXMLLoader fxmlLoader = new FXMLLoader(url); fxmlLoader.setController(this); fxmlLoader.setClassLoader(ClassLoader.getPlatformClassLoader());` . Now I get across the Exception for `java.lang.String`but I get one at the next line: `javafx.collections.FXCollections` – WolfiG Oct 14 '19 at 16:26
  • 1
    You could try using the classloader used to load one of the javafx classes, e.g. `Node.class.getClassLoader()`... – fabian Oct 14 '19 at 17:08
  • Hi fabian. Again you were right. I passed the `rootStage` of my main Window to the controller of the popupWindow`` and explicitly set `fxmlLoader.setClassLoader(rootStage.getClass().getClassLoader());. Now it works. Thanks a lot – WolfiG Oct 15 '19 at 13:29

1 Answers1

1

Thanks to fabian I am able to provide a solution to my problem.

The root cause of my problem was that my popup window's class loader, which was called in PopupController.showPopupWindow() was null. Initial code:

 public void showPopupWindow() throws Exception {
        Parent popupWindowRoot = null;
        try {
            URL url = getClass().getResource(url.to.popupFXML.file);
            final FXMLLoader fxmlLoader = new FXMLLoader(url);
            fxmlLoader.setController(this);
            popupWindowRoot = (Parent) fxmlLoader.load();
        } catch (final IOException e) {
            MessageLogger.logError(getClass(), e);
            throw e;
        }
        ...
}

Which was called via an onAction method in the main window:

private void handle_onAction(event onAction) throws Exception {
    popupController = new PopupController();
    popupController.setParentController(this);
    try {
        popupController.showPopupWindow();
    } catch (final Exception ex) {
        APPLOGGER.error(true, true, "Error caught in method showPopupWindow(): ");
    }
}

By following Fabian's suggestion, I passed the stage of the main window to method showPopupWindow and used this stage to set explicitly the classLoader of the popup window. New code:

 public void showPopupWindow(Stage parentStage) throws Exception {
        Parent popupWindowRoot = null;
        try {
            URL url = getClass().getResource(url.to.popupFXML.file);
            final FXMLLoader fxmlLoader = new FXMLLoader(url);
            fxmlLoader.setController(this);
            fxmlLoader.setClassLoader(parentStage.getClass().getClassLoader());
            popupWindowRoot = (Parent) fxmlLoader.load();
        } catch (final IOException e) {
            MessageLogger.logError(getClass(), e);
            throw e;
        }
        ...
}

The calling method was adjusted accordingly.

WolfiG
  • 1,059
  • 14
  • 31