JavaFX + FXML + webstart: why doesn't it work?

Let's create a basic Hello World application using JavaFX 8 with FXML:

Files

src/application/Main.java

:

package application;

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

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        try {
            System.out.println("Main.start()");
            FXMLLoader fxml_loader = new FXMLLoader();
            fxml_loader.setLocation(getClass().getResource("Sample.fxml"));
            System.out.println("FXML resource URL = " + getClass().getResource("Sample.fxml"));
            Parent root = fxml_loader.load(); 
            Scene scene = new Scene(root, 300, 200);
            stage.setScene(scene);
            stage.setTitle("JFX HW");
            stage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

      

src/application/Sample.fxml

:

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

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

<StackPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
   <children>
      <Label text="Hello World" />
   </children>
</StackPane>

      

Workflow

Using your favorite IDE, compile everything into a folder bin

:

$ find bin
bin
bin/application
bin/application/Main.class
bin/application/Sample.fxml

      

then create a jar:

$ javapackager -createjar -appclass application.Main -srcdir bin -outdir compiled -outfile jfxhw -v -manifestAttrs "Application-Name=JFXHW,Permissions=sandbox,Codebase=*"

      

Here you can check that the jar file works correctly with java -jar jfxhw.jar

.

Let the sign:

$ jarsigner compiled/jfxhw.jar MYALIAS

      

Deploy:

$ javapackager -deploy -appclass application.Main -srcdir compiled -outdir deployed -outfile index -width 300 -height 200 -name JFXHW -v

$ find deployed
deployed/
deployed/jfxhw.jar
deployed/index.jnlp
deployed/index.html

      

Result

Command:

javaws index.jnlp

      

fails (you need to enable the console to see this):

Main.start()
FXML resource URL = null
java.lang.IllegalStateException: Location is not set.
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2438)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at application.Main.start(Main.java:18)
    :

      

What am I doing wrong?

+3


source to share


1 answer


Problem

The application needs sufficient permissions to call the fxml file. You haven't set any permissions in your deployment step, so the security part is omitted in the resulting jnlp file.

There is a known bug and it is planned to be installed on Java 9. This bug is related to your problem, but not to the root cause.

https://bugs.openjdk.java.net/browse/JDK-8088866

for inconsistency between jarsign and javapackager -signjar.

My attempts to get rid of it:

  • Doing everything like you ended up with the same jnlp as runnable
  • Switch to JConsole and trace for more details, but fail again
  • Try javapackager -signjar, not jarsigner like you, but again don't run
  • Looked for Bug-Database on bugs.openjdk.java.net which showed me there is a signed jar verification issue generated by javapackager -signjar
  • Was looking for more complications about applet, jnlp and signing and found this: https://weblogs.java.net/blog/cayhorstmann/archive/2014/01/16/still-using-applets-sign-them-or-else
  • Try to create a deployment with -allpermissions added to deploy the javapackager step. This works with restrictions on self-signed certificates!
  • Try creating a javafx ant task with a built-in "regular" signjar task. It works! (BTW: this is how Netbeans works if you check "Request Unlimited Access (Enable Signing)" in the project properties.

Solution with javapackager

Add the -allpermissions parameter to the command line:

javapackager -deploy -allpermissions -appclass application.Main -srcdir compiled -outdir deployed -outfile index -width 300 -height 200 -name JFXHW -v

      

But there is a small problem : your manifest.mf will not contain an empty Class-Path tag which is added by the ant script. The Java security prompt will appear and you will not be able to run the application until you add the certificate to the trusted root store in your os.

Solution with ant script



First I changed the permissions in the deployment task to set it elevated:

<fx:permissions elevated="true" cacheCertificates="true"/>

      

Second . I created the following build.xml file and placed it in the root of your project. Your jnlp will be created in the dist folder. And you have to set up a store for your keystore. And the correct alias and keystore file name. If you are not on windows, "env" will probably not work. Ask this for your jdk.

Just name it "ant by default" in your project root directory.

build.xml

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

<project name="App" default="default" basedir="."
         xmlns:fx="javafx:com.sun.javafx.tools.ant">

  <property environment="env"/>
  <property name="build.src.dir" value="src"/>
  <!-- <property name="build.resources.dir" value="${build.src.dir}/resources"/> -->
  <property name="build.classes.dir" value="classes"/>
  <property name="build.dist.dir" value="dist"/>


  <target name="default" depends="clean,compile">

    <!-- defines the classpath -->
    <path id="cp">
      <filelist>
        <file name="${env.JAVA_HOME}/lib/ant-javafx.jar"/>
        <file name="${basedir}" />
      </filelist>
    </path>

    <!-- defines the task with a reference to classpath -->
    <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
             uri="javafx:com.sun.javafx.tools.ant"
             classpathref="cp"/>


    <fx:application id="appId"
                    name="jfxHw"
                    mainClass="application.Main"/>


    <!-- Defines the resources needed by the application -->
    <fx:resources id="appRes">
      <fx:fileset dir="${build.dist.dir}" includes="jfxHw.jar"/>
      <!-- <fx:fileset dir="${build.dist.dir}" includes="bsigned_jfxHw.jar"/> -->
    </fx:resources>

    <!-- Create a jar file -->
    <fx:jar destfile="${build.dist.dir}/jfxHw.jar">
      <fx:application refid="appId"/>
      <fx:resources refid="appRes"/>
      <fileset dir="${build.classes.dir}"/>
      <fileset dir="${build.src.dir}">
        <exclude name="**/*.java"/>
      </fileset>
      <manifest>
        <attribute name="Permission" value="all-permissions"/>
      </manifest>
    </fx:jar>

    <signjar alias="myAlias" keystore="myKeystore.jks"
             storepass="******"
             preservelastmodified="true">
      <path>
        <fileset dir="${build.dist.dir}" includes="**/*.jar" />
      </path>
    </signjar>


    <fx:deploy width="600" height="400" outdir="${basedir}//${build.dist.dir}/"
               outfile="jfxhw" verbose="true">
      <fx:info title="jfxHw"/>
      <fx:application refid="appId"/>
      <fx:resources refid="appRes"/>
      <fx:permissions elevated="true" cacheCertificates="true"/>
    </fx:deploy>

  </target>

  <!-- Removes the folders of previous runs -->
  <target name="clean">
    <mkdir dir="${build.classes.dir}"/>
    <mkdir dir="${build.dist.dir}"/>

    <delete>
      <fileset dir="${build.classes.dir}" includes="**/*"/>
      <fileset dir="${build.dist.dir}" includes="**/*"/>
    </delete>
  </target>

  <!-- Compiles the sources -->
  <target name="compile" depends="clean">
    <javac includeantruntime="false"
           srcdir="${build.src.dir}"
           destdir="${build.classes.dir}"
           fork="yes"
           executable="${env.JAVA_HOME}/bin/javac"
           source="1.8"
           debug="on">
    </javac>
  </target>

</project>

      

MANIFEST.MF

This is created after everything is done. Note that I am setting the manifest permissions to all-permissios for debugging purposes. You can safely sandbox it.

Manifest-Version: 1.0
JavaFX-Version: 8.0
Permission: all-permissions
Class-Path: 
Created-By: JavaFX Packager
Main-Class: application.Main

Name: application/Sample.fxml
SHA-256-Digest: eT8+7c2XeVhURexj5X9Y1xAP2H8YIMcieeySOmgOPZw=

Name: application/Main.class
SHA-256-Digest: Md+alMOmoQpslZIgLwbmPFAI8axSKBVvReXZFgoKJ6A=

      

jfxhw.jnlp

Watch out for protection:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" xmlns:jfx="http://javafx.com" href="jfxhw.jnlp">
  <information>
    <title>jfxHw</title>
    <vendor>Unknown vendor</vendor>
    <description>Sample JavaFX 2.0 application.</description>
    <offline-allowed/>
  </information>
  <resources>
    <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se"/>
    <jar href="jfxHw.jar" size="3089" download="eager" />
  </resources>
<security>
  <all-permissions/>
</security>
  <jfx:javafx-desc  width="600" height="400" main-class="application.Main"  name="jfxHw" />
  <update check="background"/>
</jnlp>

      

+3


source







All Articles