タイガー!タイガー!じれったいぞー!(SE編)

AS400,WAS,GlassFish,Java,JavaEE,JSF等の開発における日々の気づきをまとめたブログ(備忘録)。

【Eclipse】JARファイルのエクスポート/実行確認(Javaプロジェクト編)

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)

プロジェクト作成

下記のような設定で、プロジェクトを作成。

f:id:no14141:20150530145936j:plain

「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();
    }   
}

完成したプロジェクトの構成は、こちら。

f:id:no14141:20150530161628j:plain

以上で、実行させたいJavaプロジェクトは完成です。

JARファイルの作成

次にJavaプロジェクトを1つのJarファイルにパッケージングします。
まずは、通常のJarファイルにまとめてみます。

f:id:no14141:20150530153034j:plain

f:id:no14141:20150530153041j:plain

f:id:no14141:20150530153047j:plain

f:id:no14141:20150530153053j:plain

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」というものがあるのでしょうか。

次回、検証してみます!