JavaEEの開発に携わって、だいぶ時間が経ちましたが、Javaそのものの原理・原則を正しく理解できていない今日この頃です。
というわけで、Eclipseを使って(この時点でアウトでしょうか。コマンドでやりなさい!というのは無しでお願いします)、簡単なサンプルを使って、jar化し、それを実行する段階までテストしたいと思います。
今回は、一般的なJavaプロジェクトになります。
動作環境
- Window 7 64bit
- JDK
>java -version java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) >javac -version Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 javac 1.8.0_45
Eclipse Java EE IDE for Web Developers. Version: Luna Service Release 1 (4.4.1)
プロジェクト作成
下記のような設定で、プロジェクトを作成。
「guava-18.0.jar」を「/lib」を配置し、guavaを右クリックで「Build Path -> Add to Build Path」を選択。 テスト用クラスは、4つ作成しました。
Test01 - 1つのクラスで正常終了するもの。
package tigertaizo; public class Test01 { public static void main(String[] args) { System.out.println("run Test01#main."); System.out.println("Test01は、正常終了しました。"); System.exit(0); } }
Test02 - 1つのクラスで異常終了するもの。
package tigertaizo; public class Test02 { public static void main(String[] args) throws InterruptedException { System.out.println("run Test02#main."); Thread.sleep(1000); System.err.println("Test02は、異常終了しました。"); System.exit(1); } }
Test03 - 他のライブラリー(guava)を必要とするもの。
package tigertaizo; import com.google.common.base.Strings; public class Test03 { public static void main(String[] args) { System.out.println("run Test03#main."); String s = ""; if(Strings.isNullOrEmpty(s)) { System.out.println("文字列sは、null または 空白です。"); } //System.exit(0); } }
Test04 - 例外を吐き出すもの
package tigertaizo; public class Test04 { public static void main(String[] args) { System.out.println("run Test04#main."); throw new NullPointerException(); } }
完成したプロジェクトの構成は、こちら。
以上で、実行させたいJavaプロジェクトは完成です。
JARファイルの作成
次にJavaプロジェクトを1つのJarファイルにパッケージングします。
まずは、通常のJarファイルにまとめてみます。
Test01がMain Classとなる設定にしました。「Finish」ボタンでデスクトップにJarファイルが書き出しされました。
解凍した中身は、こんな感じになりました。
Test.jar │ .checkstyle │ .classpath │ .project │ tree.txt │ ├─lib │ guava-18.0.jar │ ├─META-INF │ MANIFEST.MF │ └─tigertaizo Test01.class Test02.class Test03.class Test04.class
MANIFEST.MF
マニフェストファイルは、Jarのエクスポート設定で、任意のファイルを読み込ませることができるようですが、今回は、マニフェストファイルを指定していないので、指定したMain Classのみが反映されています。
Manifest-Version: 1.0 Main-Class: tigertaizo.Test01
JARファイル実行テスト
「Write once, run anywhere」ということで、このJARファイルは、WindowsでもLinuxでもJVMの上でなら、どこでも動きますが、テストは、Linux上で行いました。OSはCentOS7.0で。
(1)JARファイルの配備
先のJARを次の場所に設置。
/home/tigertaizo/lib/Test.jar
(2)クラスパスの登録
自分のユーザー環境のプロファイルにクラスパスを登録します。
$ vim ~/.bash_profile CLASSPATH=/home/tigertaizo/lib/Test.jar:. export CLASSPATH=$CLASSPATH
即、設定を反映し、環境変数を確認します。
$ source .bash_profile $ echo $CLASSPATH /home/tigertaizo/lib/Test.jar:.
これで、パスが通ったことを確認できました!
(3)実行
まずは、Javaコマンドで3つのクラスを実行してみました。
$ java tigertaizo.Test01 run Test01#main. Test01は、正常終了しました。 $ java tigertaizo.Test02 run Test02#main. Test02は、異常終了しました。 $ java tigertaizo.Test03 run Test03#main. Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/base/Strings at tigertaizo.Test03.main(Test03.java:9) Caused by: java.lang.ClassNotFoundException: com.google.common.base.Strings at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 1 more $ java tigertaizo.Test04 run Test04#main. Exception in thread "main" java.lang.NullPointerException at tigertaizo.Test04.main(Test04.java:6)
4つ目は、故意的に例外を吐かせているので、問題ありませんが、3番目のクラスの実行では、NoClassDefFoundErrorが発生!
これは、クラスが見つからないというエラーになります。JARファイル内にguavaが存在するものの、そこへのパスが通っていないというわけですね。
「guava-18.0.jar」をLinuxに置いて、それをクラスパスに設定したうえで実行すると、正常終了しますが、これではJARの中に「guava-18.0.jar」が存在している意味が無いでしょう。
CLASSPATH=/home/tigertaizo/lib/Test.jar:/home/tigertaizo/lib/guava-18.0.jar:. export CLASSPATH=$CLASSPATH
クラスパスにguavaを追加して、実行した結果
$ java tigertaizo.Test03 run Test03#main. 文字列sは、null または 空白です。
さらに、-jarオプションで、JARファイルを直接実行してみました。
java -jar ~/lib/Test.jar
こちらの結果は、Mainクラスで指定されているクラスが実行されますので、Test01,Test02は無事に実行できましたが、Test03ではNoClassDefFoundErrorとなりました。
このパスの問題を解決する方法として、「実行可能なJAR」というものがあるのでしょうか。
次回、検証してみます!