Question

I've placed "styles.css" in the root of my bundle, and now trying to figure out how to link it in code. The problem is that @.getStylesheets().add(_) takes a String and not URL, so all approaches I know are failing here:

Take 1:

scene.getStylesheets().add("styles.css");

Nov 15, 2013 2:04:47 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
WARNING: Resource "styles.css" not found.

Take 2:

scene.getStylesheets().add(this.getClass().getResource("styles.css").toExternalForm());

NullPointerException

Take 3:

scene.getStylesheets().add(this.getClass().getClassLoader().getResource("styles.css").toExternalForm());

Nov 15, 2013 2:27:31 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
INFO: Could not load stylesheet: bundle://5.0:1/styles.css

Take 4:

scene.getStylesheets().add(myBundle.getEntry("styles.css").toExternalForm());

Nov 15, 2013 1:31:35 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
INFO: Could not load stylesheet: bundle://5.0:0/styles.css

I'm using Felix-4.2.1, Java-8 (build 1.8.0-ea-b115), JavaFX-8 (8.0.0-ea-b115).

Was it helpful?

Solution

I recently ran in the same error/troubles as the original poster of this question and I managed to solve it without making any changes to the css/fxml or extracting it to a temp file. I'm posting it here as I did not find any good answer online:

In my case the setup was:

Bundle0:

  • someview.fxml with a relative path reference to a css file
  • somestyle.css with relative path references to images
  • someimage.png
  • Resourceloader.java (returns URL objects from css and fxml files)

Bundle1:

  • basic javafx files to load the fxml with an fxml loader from bundle0.

The problem:

All css and css referenced images seemed to be ignored by the fxml, even though I set the classloader on the fxml loader. It only worked if I used an absolute file path for the css and the images referenced in the css. This is not what I want.

The solution:

There are 2 bugs/design issues in JavaFX that prohibit this setup from working with OSGi.

  • Loading the css from fxml
  • referencing images from a css

To fix the the first problem, you have to disable binary css file in javafx by setting the system property binary.css to false. There is a bug in javafx (maybe it's already fixed?) where javafx tries to be smart and tries to find the bss file, even though you say css. It flips because OSGi resource URLs have a 'bundle://' schema where it expects no schema. Solution:

  • -Dbinary.css=false

To fix the second problem, you have to set the thread context classloader to the classloader of the bundle where the css images are located and then restore the thread context classloader. The trick is to do this each time the css is interpreted by JavaFX, which is not always when you expect it to be...

ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        try {
            final ClassLoader resourcesClassLoader = Resourceloader.class.getClassLoader();
        //needed for css
        Thread.currentThread().setContextClassLoader(resourcesClassLoader);
        this.primaryStage.show();
    } finally {
        Thread.currentThread().setContextClassLoader(tccl);
    }

Unfortunately this is the best solution I could come up with. JavaFX was not made with class loader or dependency injection in mind, even though just about every corporate application uses those functionalities in some form. :(

//edit It looks like you don't need to set the thread context classloader to access the images from the css!

OTHER TIPS

The reason why this doesn't work is explained here: javafx jira RT-14177. Since you cannot use the "ExternalForm" of a ressource, I copied it in a temp file by reading the input stream directly.

        InputStream inputStream = MainApp.class.getResourceAsStream("/styles/styles.css");
        File tempStyleSheetDest = File.createTempFile("javafx_stylesheet", "");
        tempStyleSheetDest.deleteOnExit();
        Files.copy(inputStream, tempStyleSheetDest.toPath(), StandardCopyOption.REPLACE_EXISTING);
        scene.getStylesheets().add(tempStyleSheetDest.toURI().toString());

I really hope this helps you out.

Jonathan

  • Make sure you're using the Classloader of a class which has access to the bundle containing the css file
  • If you're using a relative path, make sure that css is located relative to that class' package

Here is a sample how I've loaded a css file in Drombler FX (Drombler FX is a modular Rich Client Platform for JavaFX based on OSGi and Maven (POM-first)):

http://sourceforge.net/p/drombler/drombler-fx/ci/default/tree/drombler-fx-core-docking/src/main/java/org/drombler/fx/core/docking/impl/skin/Stylesheets.java

Here: the Stylesheets class is in the same bundle as the css file.

getClass().getClassloader().getResource().toExternalForm()?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top