JavaFX Advent Calendar 12月3日担当です。
11/30開催の「第8回JavaFX勉強会」(日本JavaFXユーザーグループ主催)で、「ダブルクリックで起動する JavaFX アプリケーション JAR の仕組み 」を発表をしてきました。
この発表では、作成するプログラムがJava SE/JavaFX以外の他のJARに依存しない単純な形式を対象にしていました。
発表の質疑応答にて、依存するJARがあり複数のJARで構成されるアプリケーションの場合について質問を受けました。事前に調査はできておらず、宿題ということで本日「(補遺)依存するJARがあるJavaFXアプリケーションの実行可能JAR作成」を紹介します。
- 注記)本記事でのコマンド実行例、ディレクトリパス記述表記は、Windows OS(Windows 7)環境での操作・記述を前提としています。Linux、Solaris、Mac OS Xではそれぞれコマンドプロンプト、ディレクトリ表記に違いがありますので適宜読み替えてください。
JavaFXの実行可能JARのおさらい
Java SE 7 Update6からJavaFXのランタイム(実行に必要なファイル群)を同じディレクトリに含むようになりました。しかし、JavaFXはJava SE 7の標準ライブラリではないため、実行にあたってはJavaFXのランタイムライブラリにクラスパスを通すことが必要です。
では、JavaFXアプリケーション実行可能JARを実行するときに、-classpathオプションでJavaFXランタイムを指定すれば?と思いますが、それはjavaコマンドの仕様でできません。
JDK 6のjavaコマンドのドキュメントにある-jarオプションの説明から抜粋します。
このオプションを使用すると、指定した JAR ファイルがすべてのユーザークラスのソースになり、ユーザークラスパスのほかの設定は無視されます。
つまり、実行可能JARに必要なアプリケーションをすべて含めるか、実行可能JARのマニフェストファイル(MANIFEST.MF)に記述する、相対パスでで指定できる場所に置いたJARファイルしか利用できません。
そこで、JavaFXアプリケーションの場合は、アプリケーションのmainを呼ぶ前に、ブートストラップとしてJavaFXランタイムを探してクラスパスに追加するためのクラス群を実行可能JARに組み込み、ここからアプリケーションを実行するような特別な実行可能JARを作る仕掛けを提供しています。この仕掛けを利用して実行可能JARを作成するコマンドがjavafxpackagerです。また、Antから利用しやすいようAntタスクも提供されています。
依存するJARを持つJavaFXアプリケーション
検証のために、Java SEランタイム/JavaFXラインタイム以外のライブラリ(JARファイル)に依存を持つアプリケーションを作成します。今回は、Twitter4jを使ってみました*1。
まず、NetBeans 7.3 Beta2で(7.2でもいいです)、[ファイル]メニュー>[新規プロジェクト]>[JavaFX]>[JavaFX FXMLアプリケーション]を選択します*2。
JavaFX FXMLアプリケーションで生成される雛形は、Applicationのサブクラス、コントローラクラス、そしてFXMLファイルと3つから構成されます。画面はボタンとラベルからなる単純なものです。
プロジェクトのプロパティで、ライブラリにTwitter4jを追加します。単純なサンプルならtwitter4j-core-3.0.1.jarだけで十分なようです。
これで、アプリケーションのJAR以外にtwitter4j-core-3.0.1.jarを必要とするJavaFXアプリケーションが出来上がります。
NetBeansでビルドすると、ビルド成果物が置かれるディレクトリは次のようになります。
TwitterFX +-- build +-- dist | +-- lib | | +-- twitter4j-core-3.0.1.jar | +-- web-files | | +-- dtjava.js | | : | | +-- upgrade_javafx.png | +-- TwitterFx.html | +-- TwitterFx.jar | +-- TwitterFx.jnlp
生成されたJAR(TwitterFx.jar)のマニフェストファイルを調べてみると、
Manifest-Version: 1.0 implementation-vendor: torutk JavaFX-Version: 2.2 implementation-title: TwitterFx implementation-version: 1.0 JavaFX-Application-Class: twitterfx.TwitterFx JavaFX-Class-Path: TwitterFx.jar lib/twitter4j-core-3.0.1.jar Created-By: JavaFX Packager Main-Class: com/javafx/main/Main :(後略)
となっています。
JavaFX-Class-Pathという項目に、依存するJAR(twitter4j-core-3.0.1.jar)の相対パスが定義されています。
配置されたJARのダブルクリック起動
TwitterFx/dist/TwitterFx.jar をエクスプローラでダブルクリックすると起動します。
TwitterFx.jarだけ別ディレクトリにコピーし実行すると、エラーダイアログが表示されます。
コマンドプロンプトから、このコピーしたJARを実行してみると、エラー詳細が分かります。
Caused by: java.lang.ClassNotFoundException: twitter4j.TwitterException at java.net.URLClassLoader$1.run(Unknown Source)
と、twitter4jのクラスが見つからないというものです。
そこで、先のdistディレクトリの下、libディレクトリとその中に含まれているtwitter4j-core-3.0.1.jarを一緒にコピーします。
work +-- TwitterFx.jar +-- lib +-- twitter4j-core-3.0.1.jar
となるように置き、実行します。
すると、エクスプローラ上からダブルクリックして実行できるようになります。もちろん、コマンドプロンプトからでも実行可能です。
javafxpackagerコマンドで依存JARを持つアプリケーションの実行可能JARを生成する
OracleのJavaFXドキュメント(deploy)でjavapackagerのコマンドラインオプションを見てみると、
-classpath <files> List of dependent JAR file names.
というのがあります。
これを使って、次のように実行可能JavaFXアプリケーションJARを作成します。
c:\work\TwitterFx> javafxpackager -createjar -appclass twitterfx.TwitterFx -srcdir build\classes -outdir dist -outfile TwitterFx-hand.jar -classpath lib\twitter4j-core-3.0.1.jar
生成されたTwitterFx-hand.jarと、そのJARファイルがある場所を基点に相対パスでlib\twitter4j-core-3.0.1.jarを用意して実行すると、無事起動に成功しました。
javafxpackagerで生成したJARに含まれるマニフェストファイルを見ると、
JavaFX-Class-Path: lib/twitter4j-core-3.0.1.jar
と、-classpathで指定したJARだけ記載されています。NetBeansで生成したときとちょっと記述が違っています。NetBeansで生成した場合、依存するJARの他、アプリケーション自身のJARも記載されています。javafxpackagerの動作の方が正解かと思いますが。
java.io.IOException: ファイル名、ディレクトリ名、またはボリューム ラベルの構文が 間違っています。 at java.io.WinNTFileSystem.canonicalize0(Native Method) at java.io.Win32FileSystem.canonicalize(Win32FileSystem.java:414) at java.io.File.getCanonicalPath(File.java:589) at java.io.File.getCanonicalFile(File.java:614) at com.javafx.main.Main.fileToURL(Main.java:97)