4

让我们使用带有 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,将所有内容编译到一个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)
    :

我究竟做错了什么?

4

1 回答 1

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 搜索错误数据库,这表明存在验证由 javapackager -signjar 创建的签名 jar 的问题
  5. 搜索有关小程序、jnlp 和签名的更多复杂情况,发现:https ://weblogs.java.net/blog/cayhorstmann/archive/2014/01/16/still-using-applets-sign-them-or-else
  6. 尝试使用添加到 javapackager 的部署步骤的 -allpermissions 创建部署。这适用于自签名证书的限制!
  7. 尝试使用嵌入的“正常”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 将不包含像这样由 ant 脚本添加的空 Class-Path 标记。将弹出 Java 安全提示,在您将证书添加到操作系统中受信任的根证书存储之前,您无法运行该应用程序。

用蚂蚁脚本解决

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

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

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

只需在项目的根目录中使用“ant default”调用它。

构建.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>
于 2015-07-20T20:06:56.297 回答