JavaFX + FXML + webstart:为什么这不起作用?

时间:2015-07-20 18:01:43

标签: java javafx java-web-start fxmlloader

让我们使用带有FXML的JavaFX 8创建最简单的Hello World应用程序:

文件

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>

工作流

使用您喜欢的IDE,将所有IDE编译到bin文件夹中:

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

然后创建一个jar:

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

可以在此验证jar文件与java -jar jfxhw.jar一起正常工作。

让我们签名:

$ jarsigner compiled/jfxhw.jar MYALIAS

部署:

$ 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

结果

命令:

javaws index.jnlp

失败(您需要启用控制台才能看到这个):

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)
    :

我做错了什么?

1 个答案:

答案 0 :(得分:5)

问题

应用程序需要足够的权限来调用fxml文件。您尚未在部署步骤中设置任何权限,因此在生成的jnlp文件中省略了安全性部分。

有一个已知的错误,它计划在Java 9中修复。这个错误与你的问题有关,但不是根本原因。

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

对于jarsign和javapackager -signjar的不一致。

我试图摆脱它:

  1. 做与你一样的事情,以不同的jnlp结束
  2. 在JConsole和Tracing上切换以获取更多详细信息,但再次失败
  3. 尝试javapackager -signjar命令,而不是像你这样的jarsigner,但再次失败
  4. 在bugs.openjdk.java.net上搜索了Bug-Database,它向我展示了验证由javapackager -signjar创建的签名jar的问题
  5. 搜索了有关applet,jnlp和签名的更多复杂情况,发现:https://weblogs.java.net/blog/cayhorstmann/archive/2014/01/16/still-using-applets-sign-them-or-else
  6. 尝试使用-allpermissions创建部署,以部署javapackager的步骤。这适用于对自签名证书的限制!
  7. 尝试使用嵌入式&#34;普通&#34;创建一个javafx ant任务。 signjar任务。 这有效!(顺便说一下:如果你检查&#34;请求不受限制的访问(启用签名)&#34;在项目属性下,这就是Netbeans的工作方式。
  8. 使用javapackager

    的解决方案

    -allpermissions 参数添加到命令行:

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

    但是有一点问题:您的manifest.mf将不包含空类Class-Path标记,因为这是由ant脚本添加的。将弹出Java安全提示,并且在将证书添加到操作系统中受信任的根证书存储区之前,您无法运行该应用程序。

    使用ant脚本

    的解决方案

    首先我更改了部署任务中的权限以将其设置为提升:

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

    第二次我创建了以下build.xml文件并将其放在项目的根目录中。你的jnlp将在文件夹dist中创建。您必须为密钥库设置storepass。和正确的别名和密钥库文件名。 如果你不在窗户上,那么&#34; env&#34;可能不会工作。把它设置为你的jdk。

    只需使用&#34; ant default&#34;在项目的根目录中。

    的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

    这是在完成所有操作后创建的。请注意,我将清单权限设置为all-permissios以进行调试。您可以安全地将其设置为沙箱。

    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

    观察安全部分:

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