Prometheus
Prometheus

Reputation: 1015

How to specify resources in fx:include?

I am trying to load an FXML file (Test1.fxml) that includes another FXML file (Test2.fxml). The included file is supposed to use its own resource bundle (test2.properties), which is specified by the "resources" attribute inside the fx:include tag.

When I run the application, FXMLLoader throws a NullPointerException, caused by the following piece of code.

resources = ResourceBundle.getBundle(value, Locale.getDefault(),
FXMLLoader.this.resources.getClass().getClassLoader());

Through debugging I found that FXMLLoader.this.resources is null, which causes the NPE. At first I thought I had simply given a wrong file path, but note that this happens before ResourceBundle even tries to load the properties file!

Any idea how to fix this so the resources can be loaded?

Below are my files. They are all in the same directory (default package for simplicity).

TestApp.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApp extends Application
{
    @Override
    public void start(Stage primaryStage) throws Exception
    {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getClassLoader().getResource("Test1.fxml"));
        loader.load();

        Scene scene = new Scene((Parent) loader.getRoot());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}

Test1.fxml

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <fx:include source="Test2.fxml" resources="test2" />
   </center>
</BorderPane>

Test2.fxml

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <Label text="%test" BorderPane.alignment="CENTER" />
   </center>
</BorderPane>

test2.properties

test=Test 2

Full stacktrace:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
    at com.sun.javafx.application.LauncherImpl$$Lambda$50/1645995473.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: 
/C:/Eclipse/fx/bin/Test1.fxml:9

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at TestApp.start(TestApp.java:16)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$53/1370226249.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/186276003.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/1026297962.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/237061348.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101)
    at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source)
    ... 1 more
Caused by: java.lang.NullPointerException
    at javafx.fxml.FXMLLoader$IncludeElement.processAttribute(FXMLLoader.java:1088)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:216)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:740)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    ... 17 more
Exception running application TestApp

Upvotes: 3

Views: 2055

Answers (1)

Manuel Mauky
Manuel Mauky

Reputation: 2193

There is a bug in the FXMLLoader. To workaround the bug:

  1. You have to define a root ResourceBundle for the FXMLLoader instance when you load your root fxml file.
  2. The root resourceBundle has to be loaded from another Classloader then the bootstrap classloader.

A possible workaround can be found in the oracle forum: https://community.oracle.com/message/11240449

If you don't need a resourceBundle for the root fxml file (like in your example) you can use this simple workaround:

TestApp.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApp extends Application
{
    @Override
    public void start(Stage primaryStage) throws Exception
    {
        FXMLLoader loader = new FXMLLoader();

        // add the resource wrapper
        loader.setResources(new ResourceWrapper());

        loader.setLocation(getClass().getClassLoader().getResource("Test1.fxml"));
        loader.load();

        Scene scene = new Scene((Parent) loader.getRoot());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args)
    {
        launch(args);
    }

    // an empty resource bundle
    private static class ResourceWrapper extends ListResourceBundle {
        @Override
        protected Object[][] getContents() {
            return new Object[0][];
        }
    }
}

Upvotes: 3

Related Questions