JavaEEの開発に携わって、だいぶ時間が経ちましたが、Javaそのものの原理・原則を正しく理解できていない今日この頃です。
というわけで、Eclipseを使って(この時点でアウトでしょうか。コマンドでやりなさい!というのは無しでお願いします)、簡単なサンプルを使って、jar化し、それを実行する段階までテストしたいと思います。
今回は、一般的なJavaプロジェクトになります。
動作環境
>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 または 空白です。");
}
}
}
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」というものがあるのでしょうか。
次回、検証してみます!