让我们使用JavaFX 8和FXML创建最简单的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,将所有内容编译到bin文件夹中:
$ find bin
bin
bin/application
bin/application/Main.class
bin/application/Sample.fxml

然后创建一个罐子:
$ 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)
    :

我究竟做错了什么?

最佳答案

问题

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

有一个已知的错误,已计划在Java 9中修复。该错误与您的问题有关,但与根本原因无关。

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

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

我试图摆脱它的尝试:

  • 像您一样做所有事情,以无法运行的相同jnlp结尾
  • 启用了JConsole和Tracing以获取更多详细信息,但
  • 再次失败
  • 尝试使用javapackager -signjar命令而不是像您这样的jarsigner,但再次失败
  • 在bugs.openjdk.java.net上搜索Bug数据库,这表明我在验证由javapackager -signjar创建的签名jar时存在问题。
  • 搜索有关applet,jnlp和签名的更多并发症,并找到以下内容:https://weblogs.java.net/blog/cayhorstmann/archive/2014/01/16/still-using-applets-sign-them-or-else
  • 尝试使用-allpermissions添加到javapackager的部署步骤来创建部署。这适用于对自签名证书的限制!
  • 尝试创建带有嵌入的“正常” signjar任务的javafx ant任务。 可行! (顺便说一句:如果您在项目属性下选中“请求不受限制的访问(启用签名)”,这就是Netbeans的做法。

    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安全提示,直到您将证书添加到操作系统中的受信任的根证书存储中后,您才能运行该应用程序。

    蚂蚁脚本解决方案

    首先我更改了deploy任务中的权限以将其设置为提升:
    <fx:permissions elevated="true" cacheCertificates="true"/>
    

    第二个我创建了以下build.xml文件,并将其放在项目的根目录中。您的jnlp将在文件夹dist中创建。而且,您必须为密钥库设置storepass。以及正确的别名和密钥库文件名。
    如果您不在Windows上,则“env”可能无法工作。很难设置您的jdk。

    只需在项目的根目录中使用“ant default”进行调用即可。

    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>
    

    清单文件

    一切完成后创建。请注意,出于调试目的,我将清单权限设置为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>
    

  • 07-24 13:15