Search code examples
javafxloadjavafx-11openjfx

import of java.lang.String fails in JAVAFX 11 FXML


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...


Solution

  • 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.