タツノオトシゴのブログ

主にJavaに関するものです。

JavaFX2のパッケージング

JavaFXはjarをクリックすると起動するようにする方法とjnlpの形式でWebブラウザで実行できる形式にまとめる方法があります。
デスクトップで実行する場合jarの場合は単にjarで固めただけでも動作しますが、jnlpの場合は専用のjarにする必要があります。

JavaFXのjarの形式

http://docs.oracle.com/javafx/2/deployment/deploy_user_experience.htm#BABFIECI

通常のjarファイルと何が違うかというと、主に次の項目が異なります。

  1. 「MANIFEST.MF」
    • JavaFX用の自作のApplicationクラス「JavaFX-Application-Class」、関連するjarファイル(クラスパス)「JavaFX-Class-Path」があります。
    • また、後述しますがメインクラスは、「com/javafx/main/Main」とJavaFXの専用のクラスを指定する必要があります。
    • http://d.hatena.ne.jp/torutk/20120830/p1
  2. jarファイルには、「com.javafx.main.Main」を含むサブクラスを同梱する必要があります。
    • JDKに付属している「%JAVA_HOME%bin\%javafxpackager.exe」を利用することで作成することができます。
    • オプションがたくさんあるので、実際にはこれもJDKに付属しているJavaFX用のAntタスク「「%JAVA_HOME%\lib\ant-javafx.jar」を利用します。
=== MANIFEST.MF === のサンプル
Manifest-Version: 1.0
JavaFX-Version: 2.1
implementation-vendor: nhildebr
implementation-title: AnimatedCircles
implementation-version: 1.0
============== ▼▼JavaFX特有の項目▼▼
JavaFX-Preloader-Class: firstpreloader.FirstPreloader
JavaFX-Application-Class: animatedcircles.AnimatedCircles
JavaFX-Class-Path: lib/FirstPreloader.jar
JavaFX-Fallback-Class: com.javafx.main.NoJavaFXFallback
Created-By: JavaFX Packager
Main-Class: com/javafx/main/Main
============== ▲▲JavaFX特有の項目▲▲

JavaFXのjarをMaven+Antで作成する

Oracleが出しているOSSIDENetBeansは、基本的にビルドするときにAntタスクを使用しているので、プロジェクトをJavaFXの形式にすれば、自動的にビルドされます。
Eclipseの場合は、プラグインなどあまり充実していないので、Antファイルを自作することになります。

JavaFXのAntタスクは、下記の公式サイトにリファレンスが載っています。

JavaFX用のJarファイルを作成する(タスク)

build.xmlファイルの作成
  1. Antタスク用のbuild.xmlを作成します。
  2. ルート要素にJavaFX用の名前空間「xmlns:fx="javafx:com.sun.javafx.tools.ant"」を追加します。
    • これで、要素が利用できるようになります。
  3. JavaFX用のAntタスク用のjar「ant-javafx.jar」のパスを取得します。
    • 環境変数JAVA_HOME」を基準にするので、JDKにPATHを通しておきます。
    • で、JavaFX用のタスクを定義します。
  4. でアプリケーションの情報を定義します。
    • 属性「mainClass」で、自作したjavafx.application.Applicationを継承したクラスを指定します。
  5. で、jarファイルを作成します。
    • 属性「destfile」で出力先を指定します。maven形式なので、「target/-.jar」に出力します。
    • 子要素でJVMの引数や前提のJavaFXのバージョンを定義します。省略可能です。
    • 子要素でMANIFEST.MFを定義します。省略可能です。
    • 子要素で、自作したコンパイル済みのクラスファイルが格納されているフォルダを指定します。mavenの場合「target/classes」になります。
    • 子要素でクラスパスに通すjarファイルやプロパティファイルを指定します。
      • MANIFESTに追加されることで、起動時の引数でクラスパス(-cp, --classpath)を指定する必要がなくなります。
      • ただし、本体のjarと同じ場所に配置しないといけないので、注意が必要です。NetBeansだと任意のフォルダが指定できる。
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===============================================
JavaFXのパッケージングをするAntXMLファイル

====================================================-->
<project name="sample-app" basedir="." default="jfx-package" xmlns:fx="javafx:com.sun.javafx.tools.ant">
    
    <!-- JavaFXのアプリケーション用のAnt用のjarファイルのパスの取得 -->
    <property environment="env" />
    <property name="javafx.tools.ant.jar" value="${env.JAVA_HOME}/lib/ant-javafx.jar" />

    <!-- コンパイル済みのclassファイルの格納場所:mavenの形式 -->
    <property name="dest" location="target/classes" />
    
    <!-- 実行可能な Jar ファイルが作成されるフォルダ:mavenの形式 -->
    <property name="jardest" location="target" />

    <!-- アプリケーションの設定 -->
    <property name="app.vendor" value="javafx.sample" />
    
    <!-- 実行可能 Jar ファイルの名前(の一部となる)。mavenから実行する場合pom.xmlから引き継ぐ。 -->
    <property name="app.id" value="sample-app" />
    <property name="app.name" value="sample-app" />
    <property name="app.version" value="2.0" />

    <!-- メインクラス -->
    <property name="app.main-class" value="sample.gui.SampleApplication" />
    
    <!-- JavaFX専用の形式にパッケージングするタスク -->
    <target name="jfx-package" description="packaging JavaFX archtecture.">
        
        <echo>JavaFX用のjarをパッケージングしています。</echo>
        
        <!-- タスク定義 -->
        <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"      
                 uri="javafx:com.sun.javafx.tools.ant"
                 classpath="${javafx.tools.ant.jar}"/>
        
        <!-- JavaFXのアプリケーション定義 -->
        <fx:application id="app-info" 
                  name="${app.name}"
                  mainClass="${app.main-class}">
        </fx:application>
        
        <!-- JavaFXのパッケージ作成 -->
        <fx:jar destfile="${jardest}/${app.id}-${app.version}.jar">
            <fx:application refid="app-info"/>
            
            <!-- アプリケーションの起動パラメータなど:オプション -->
            <fx:platform javafx="2.2+">
                <fx:jvmarg value="-Xms64m" />
                <fx:jvmarg value="-Xmx128m" />
            </fx:platform>
            
            <!-- MANIFEST.MFファイルの内容 :オプション-->
            <manifest>
                <attribute name="Implementation-Vendor" value="${app.vendor}"/>
                <attribute name="Implementation-Title" value="${app.name}"/>
                <attribute name="Implementation-Version" value="${app.version}"/>
            </manifest>
            
            <!-- パッケージング対象のファイル -->
            <fileset dir="${dest}"/>
            
            <!-- JavaFX-Class-Pathに追加される値:依存するライブラリを定義 -->
            <fx:resources>
                <fx:fileset dir="target/dependency" includes="*.jar"/>
            </fx:resources>
            
        </fx:jar>
        
    </target>

</project>

これで、コンパイル済みのファイルをパッケージングできます。

===== JavaFX用のjarファイルの作成
> mvn dependency:copy-dependencies
> mvn -Dmaven.test.skip=true compile
> ant -f javafx_build.xml jfx-package

作成したjarファイルのMANIFEST.MFファイルの中身を確認して、JavaFX形式になっているか確認します。

> jar -xvf target\<作成したjarファイルのパス> META-INF/MANIFEST.MF
> type target\META-INF\MANIFEST.MF
・・・(MANIFEST.MFファイルの中身)・・・
mavenと連携する

JavaFX用のAntファイルをMavenから呼ぶ場合は、Ant用のプラグイン「antrun」を使用します。

  1. Mavenのpom.xmlにantタスクを追加します。
    • 追加する際には、「package」フェーズに追加します。
    • antrunプラグインでフェーズ指定する際には、に定義すると実行されないので注意。
  2. これで、Mavenの「package」オプションを実行したときにAntタスクも呼ばれます。
> mvn dependency:copy-dependencies
> mvn mvn -Dmaven.test.skip=true package
<?xml version="1.0" encoding="UTF-8"?>
<!-- pom.xmlファイルのサンプル -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
        http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>sample.javafx</groupId>
    <artifactId>javafx-sample</artifactId>
    <packaging>jar</packaging>
    <name>javafx-sample</name>
    <version>2.0</version>

    <build>
    ・・・(省略)・・・
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.7</version>
                    <inherited>true</inherited>
                </plugin>
            </plusins>
        </pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <inherited>true</inherited>
                <executions>
                    <execution>
                        <id>JavaFX-package</id>
                        
                        <phase>package</phase>
                        <configuration>
                            <tasks>
                                <!-- APP名やバージョンなどを、pom.xmlの定義からAntファイルに渡す。 -->
                                <property name="app.id" value="${project.artifactId}" />
                                <property name="app.name" value="${project.name}" />
                                <property name="app.version" value="${project.version}" />
                                
                                <!-- 作成したAntタスクを呼ぶ -->
                                <ant antfile="javafx_build.xml" target="jfx-package" inheritRefs="false"></ant>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
起動する際の小ネタ

JavaFXの形式でパッケージングすると、メインクラスが「com.javafx.main.Main」となるので、通常の起動方法でも実行可能です。

そのため、デスクトップアプリケーションなど、クラスパスなど自由な構成にしたい場合は専用のbatファイルを作成して、
そこから起動するようにもできます。

> java -cp .;sample-javafx.jar com.javafx.main.Main

WebStart用のJNLP、HTMLファイルを作成する(タスク)

jarファイルが出来上がったので、さらにJNLPファイルも作成します。

  1. 先ほど作成したAnt用のbuild.xmlに、deploy用のタスクを追加します。
    • 依存関係として、jarを作成するタスクに依存しているので、属性「depends」で先ほど作成したタスク「jfx-package」を指定します。
    • デプロイ用のタスクの形式は、パッケージング用とほとんど同じです。
    • などは共通です。
  2. 出力先フォルダを「target/web-dist」とした場合、前回のフォルダを標準タスクので削除します。
  3. で、デプロイタスクを呼び出します。
    • 属性「width」「height」で、HTML内の画面の大きさを定義します。
    • 属性「outfir」で、出力先フォルダを定義します。
    • 属性「outfile」で、jnlp、htmlファイルの名称を定義します。
    • 子要素でHTMLのタイトルを定義します。省略可能です。
    • 子要素で依存するファイルを定義します。
      • で作成したアプリの本体と依存するjarファイルなどを定義します。
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===============================================
JavaFXのパッケージングをするAntXMLファイル

====================================================-->
<project name="sample-app" basedir="." default="jfx-package" xmlns:fx="javafx:com.sun.javafx.tools.ant">
    
    <!-- JavaFXのアプリケーション用のAnt用のjarファイルのパスの取得 -->
    <property environment="env" />
    <property name="javafx.tools.ant.jar" value="${env.JAVA_HOME}/lib/ant-javafx.jar" />

    <!-- コンパイル済みのclassファイルの格納場所:mavenの形式 -->
    <property name="dest" location="target/classes" />
    
    <!-- 実行可能な Jar ファイルが作成されるフォルダ:mavenの形式 -->
    <property name="jardest" location="target" />

    <!-- アプリケーションの設定 -->
    <property name="app.vendor" value="javafx.sample" />
    
    <!-- 実行可能 Jar ファイルの名前(の一部となる)。mavenから実行する場合pom.xmlから引き継ぐ。 -->
    <property name="app.id" value="sample-app" />
    <property name="app.name" value="sample-app" />
    <property name="app.version" value="2.0" />

    <!-- メインクラス -->
    <property name="app.main-class" value="sample.gui.SampleApplication" />
    
    <!-- JavaFX専用の形式にパッケージングするタスク -->
    <target name="jfx-package" description="packaging JavaFX archtecture.">
        ・・・(省略)・・・
    </target>
    
    <!-- JavaFXのデプロイするパッケージ -->
    <target name="jfx-deploy" description="deploy JavaFX archtecture." depends="jfx-package">
        
        <echo>JavaFX用のデプロイ(jar、jnlp、HTMLファイルの作成)</echo>
        
        <!-- 以前のフォルダを削除する -->
        <delete dir="target/web-dist/"></delete>
        
        <!-- タスク定義 -->
        <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
                 uri="javafx:com.sun.javafx.tools.ant"
                 classpath="${javafx.tools.ant.jar}"/>
        
        <!-- JavaFXのアプリケーション定義 -->
        <fx:application id="app-info" 
                  name="${app.name}"
                  mainClass="${app.main-class}">
        </fx:application>
        
        <!-- アプリケーションの起動パラメータなど:省略可能 -->
        <fx:platform javafx="2.2+">
            <fx:jvmarg value="-Xms64m" />
            <fx:jvmarg value="-Xmx128m" />
        </fx:platform>
        
        <!-- JavaFXのデプロイタスクの実行 -->
        <fx:deploy width="600" height="400" outdir="target/web-dist" outfile="app">
            
            <fx:info title="JavaFX Web" />
            <fx:application refid="app-info"/>
            
            <!-- 依存するファイル -->
            <fx:resources>
                <!-- 自身のライブラリファイル(<fx:jar>で作成したファイル) -->
                <fx:fileset dir="target" includes="${app.id}-${app.version}.jar"/>
                
                <!-- 依存するライブラリ -->
                <fx:fileset dir="target/dependency" includes="*.jar"/>
            </fx:resources>
        </fx:deploy>
    </target>
</project>

これで、JNLPファイルなどをが作成されます。。

===== JavaFX用のjarファイルの作成
> mvn dependency:copy-dependencies
> mvn -Dmaven.test.skip=true compile
> ant -f javafx_build.xml jfx-deploy
Mavenとの連携

mavenと連携する際は、実行するタスクを先ほど作成した「jfx-deploy」に変更するだけです。

  • packageオプションを実行したときに、jnlpも作成されます。
===== JavaFX用のjarファイルの作成
> mvn dependency:copy-dependencies
> mvn -Dmaven.test.skip=true package
<?xml version="1.0" encoding="UTF-8"?>
<!-- pom.xmlファイルのサンプル -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
        http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>sample.javafx</groupId>
    <artifactId>javafx-sample</artifactId>
    <packaging>jar</packaging>
    <name>javafx-sample</name>
    <version>2.0</version>

    <build>
    ・・・(省略)・・・
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.7</version>
                    <inherited>true</inherited>
                </plugin>
            </plusins>
        </pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <inherited>true</inherited>
                <executions>
                    <execution>
                        <id>JavaFX-package</id>
                        
                        <phase>package</phase>
                        <configuration>
                            <tasks>
                                <!-- APP名やバージョンなどを、pom.xmlの定義からAntファイルに渡す。 -->
                                <property name="app.id" value="${project.artifactId}" />
                                <property name="app.name" value="${project.name}" />
                                <property name="app.version" value="${project.version}" />
                                
                                <!-- 作成したAntタスクを呼ぶ -->
                                <ant antfile="javafx_build.xml" target="jfx-deploy" inheritRefs="false"></ant>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
JNLPの形式の注意点

JNLPで実行する場合、クラスパスに注意する必要があります。
また、Log4jやSLF4Jなどのロガーなどを設定している場合、注意する必要があります。