JavaFX: Custom Component throws LoadException only in Runnable JAR

Ramon Dias :

I'm trying to generate a executable jar file from a JavaFX project I'm working on which is running ok when executing through the Eclipse IDE. But when executing from the generated jar file, a runtime error occurs when the FXML tries to load a custom component (class which extends the JFX component BorderPane). What's wrong?

Here's the FXML file, where a custom class, CardPane, which extends BorderPane is used.

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

<?import com.tsi.ui.CardPane?>
<?import java.lang.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<?import com.tsi.ui.CardPane?>

<BorderPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="400.0" style="-fx-background-color: #454449;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <GridPane fx:id="gridPane" opacity="0.99" prefHeight="386.0" prefWidth="380.0" BorderPane.alignment="CENTER">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
           <!-- 
              The below command (referenced in the stack trace as line 40, 
              which here is not precise, since I've deleted some useless 
              stuff for the post propose) is where the error occurs
            -->
            <CardPane fx:id="pane00" prefHeight="200.0" prefWidth="200.0" />
            <CardPane fx:id="pane01" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" />
            <CardPane fx:id="pane02" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" />
            <CardPane fx:id="pane10" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1" />
            <CardPane fx:id="pane11" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <CardPane fx:id="pane12" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1" />
            <CardPane fx:id="pane20" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2" />
            <CardPane fx:id="pane21" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
            <CardPane fx:id="pane22" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="2" />
         </children>

      </GridPane>
   </center>

<!-- I will show only the interesting point -->
...

</BorderPane>

The CardPane is being used just as a class that extends BorderPane with the layout positions already populated with some other nodes. In this code, we've some of them populating the content of the cells of a GridPane.

Here's a few lines to show how it's defined:

public class CardPane extends BorderPane {

    private Card card;
    private Label cardName;
    private Label cardValue;
    private ImageView valueIcon;
    private HBox hBox;
    private Node armaSprite;

    public CardPane() {
        hBox = new HBox();
        cardName = new Label();
        cardValue = new Label();
        valueIcon = new ImageView(new Image(Sprite.CAMINHO + File.separator + "CoracaoIcon.png", 27, 22, false, false));
    }

I honestly don't know if this approach (extending the BorderPane class, making my own component this way) is a good practice. If not, I will be glad if someone could let me know what should be avoided. But above all, the main point is, everything works fine inside Eclipse, but not when the project is exported. Why?

Here's the exception printed on Windows console. Again, it occurs only when the jar is executed with java.exe -jar '.\Dungeon Cards.jar'. Never on the IDE.

javafx.fxml.LoadException:
file:/C:/Users/ramon/Desktop/Dungeon%20Cards.jar!/com/tsi/ui/fxml/game.fxml:40

        at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
        at com.tsi.app.Jogo.<init>(Jogo.java:40)
        at com.tsi.app.DungeonCards.iniciarJogo(DungeonCards.java:138)
        at com.tsi.ui.Instrucoes.esconder(Instrucoes.java:72)
        at com.tsi.ui.Instrucoes$2.handle(Instrucoes.java:52)
        at com.tsi.ui.Instrucoes$2.handle(Instrucoes.java:1)
        at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.Scene$KeyHandler.process(Scene.java:3964)
        at javafx.scene.Scene$KeyHandler.access$1800(Scene.java:3910)
        at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2040)
        at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2501)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$352(GlassViewEventHandler.java:248)
        at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
        at com.sun.glass.ui.View.handleKeyEvent(View.java:546)
        at com.sun.glass.ui.View.notifyKey(View.java:966)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
        at javafx.scene.image.Image.validateUrl(Image.java:1118)
        at javafx.scene.image.Image.<init>(Image.java:659)
        at com.tsi.ui.CardPane.<init>(CardPane.java:42)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at java.lang.Class.newInstance(Unknown Source)
        at sun.reflect.misc.ReflectUtil.newInstance(Unknown Source)
        at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:1009)
        at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:746)
        at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
        ... 39 more
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
        at javafx.scene.image.Image.validateUrl(Image.java:1110)
        ... 51 more
Ramon Dias :

@Zephyr pointed how I've missed the "Caused By:" statement on the StackTrace when writing the post. It clearly indicates that the ImageView could not be instantiated due a malformed URL.

The trick is that I had the following constant pointing to the root path of where my image resources are:

public static final String CAMINHO = "/com/tsi/sprites"; 

The URL was being defined as CAMINHO + File.separator + [image_file].png

Resulting in: "/com/tsi/sprites\[image_file].png"

The File.separator used on the constructor concatenation is holding the Windows pattern of file separating (backslash), while the constant value CAMINHO uses normal slashes (Linux pattern). Somehow, Eclipse can abstract that misplaced combination, but while running outside it, Windows doesn't.

To solve it, I just changed File.separator to "/".

As it says in this article, while working with resources, File.separator should be avoided:

"[...] use system-specific file separator only when working with files and when displaying a path to the user. For all other cases use forward slash."

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=135477&siteId=1