この記事は「エムスリー Advent Calendar 2016」の 22日目の記事です。
ant や maven、その他様々なエコシステムがある今、JDK コマンドを直で使う機会というのはそうそう無いのではないかと思います。
(maven 無しで、javac
でプロジェクトのパッケージングをやれと言われたら結構大変そうです)
jvisualvm
, jstack
, jmap
などある程度メジャーなコマンドは、解析やトラブル対応などで使ったことがありますが、それ以外にも JDK には沢山の付属コマンドがあります。
今回は、${JAVA_HOME}/bin の下にあるマニアックなコマンドについて調べてみたので、それをいくつか紹介します。
ちなみに、${JAVA_HOME}/bin の下には以下のようなコマンドがありました。
appletviewer jarsigner javafxpackager jcmd jhat jmc jstack keytool policytool schemagen unpack200
extcheck java javah jconsole jinfo jps jstat native2ascii rmic serialver wsgen
idlj javac javap jdb jjs jrunscript jstatd orbd rmid servertool wsimport
jar javadoc javapackager jdeps jmap jsadebugd jvisualvm pack200 rmiregistry tnameserv xjc
※ 今回は Oracle JDK で調べてみましたが、OpenJDK 等、他の実装では違いがあるかもしれません。
serialver
serialverコマンドは、1つ以上のクラスのserialVersionUIDを、展開しているクラスにコピーするのに適した形式で返します。引数を指定しないでserialverコマンドを呼び出すと、使用法を示す行が出力されます。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/serialver.html
serialVersionUID
を返すコマンドのようです。実際に使ってみます。
package hoge;
public class TestModel {
private int hoge;
public int getHoge() {
return hoge;
}
public void setHoge(int hoge) {
this.hoge = hoge;
}
}
$ ${JAVA_HOME}/bin/serialver hoge.TestModel
クラスhoge.TestModelは直列化できません。
おっと、Serializable を implements してないのでエラーになってしまいました。
気を取り直して、Serializable を implements してみます。
public class TestModel implements Serializable {
private int hoge;
public int getHoge() {
return hoge;
}
public void setHoge(int hoge) {
this.hoge = hoge;
}
}
$ ${JAVA_HOME}/bin/serialver hoge.TestModel
hoge.TestModel: private static final long serialVersionUID = -2243945887951876522L;
クラスには serialVersionUID
の定義を書いてないですが、自動計算されたっぽい値が返ってきました。これはどういうことなのでしょうか。
答えは Javadoc にありました。
直列化可能クラスがserialVersionUIDを明示的に宣言しない場合、直列化ランタイムは「Java(TM)オブジェクト直列化仕様」で説明されているように、クラスのさまざまな側面に基づいて、クラスのserialVersionUIDのデフォルト値を計算します。
https://docs.oracle.com/javase/jp/8/docs/api/java/io/Serializable.html
最後に、自分で定義した値が返ってくるか確認します。
public class TestModel implements Serializable {
private static final long serialVersionUID = 1L;
private int hoge;
public int getHoge() {
return hoge;
}
public void setHoge(int hoge) {
this.hoge = hoge;
}
}
$ ${JAVA_HOME}/bin/serialver hoge.TestModel
hoge.TestModel: private static final long serialVersionUID = 1L;
よさそうです。
ところで、リファクタリングなどでクラス構造が変わった場合、serialVersionUID
も追随して変えないといけないと思いますが、これをちゃんとやってるのを見たことないですね。まぁそういうことが必要になるシステム(オブジェクトをシリアライズして、NW 転送して、別 VM で使うとか)が世の中にどれだけあるのか?という話ですね。
しかし、このコマンドはどういう場面で使うんでしょうかね?
jrunscript
jrunscriptコマンドは、言語に依存しないコマンド行スクリプト・シェルです。jrunscriptコマンドでは、対話型(読取り-評価-出力)モードとバッチ(-fオプション)モードの両方のスクリプト実行がサポートされています。デフォルトの使用言語はJavaScriptですが、-lオプションを使えば他の言語も指定できます。Javaとスクリプト言語との通信を使用することで、jrunscriptコマンドで探求的なプログラミング・スタイルがサポートされます。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/jrunscript.html
JVM 上で他の言語(デフォルトでは JavaScript)を実行できるようになるとのことです。
試してみます。
$ ${JAVA_HOME}/bin/jrunscript -e "print('hello world')"
hello world
Java の API を使ったり、組み込み関数もいくつかサポートされているようです。
java.lang.System.out.println(new java.util.Date())
date()
pwd()
$ ${JAVA_HOME}/bin/jrunscript -f sample.js
Mon Dec 12 12:40:49 JST 2016
Mon Dec 12 2016 12:40:49 GMT+0900 (JST)
/Users/sinsengumi/Documents/work/20161212/.
これを使えば、クロスプラットフォームなスクリプトが書けたりするのでしょうか・・・。
schemagen
スキーマ・ジェネレータは、Javaクラス内で参照されている名前空間ごとに1つのスキーマ・ファイルを作成します。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/schemagen.html
これは Java クラスから XML Schema を生成する時に使用するコマンドですね。
ただし、XML Schema では多彩な定義情報を持つことができますが、それをプレーンな Java だけでは表現できないので、アノテーションを駆使して表現してあげる必要があります。
package hoge;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class TestXml {
@XmlElement(name = "id")
private int id;
@XmlAttribute(name = "attr")
private String attr;
}
$ schemagen hoge/TestXml.java
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="testXml" type="testXml"/>
<xs:complexType name="testXml">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
<xs:attribute name="attr" type="xs:string"/>
</xs:complexType>
</xs:schema>
これとは逆に XML Schema から Java クラスを生成する xjc
コマンドというのもあります。
JSON 全盛の今の時代にこいつらを使う日が来るのだろうか・・・
jdeps
jdepsコマンドは、Javaクラス・ファイルの依存関係をパッケージ・レベルまたはクラス・レベルで表示します。入力クラスには、.classファイル、ディレクトリ、JARファイルへのパス名、またはすべてのクラス・ファイルが分析されるクラスの完全修飾名を指定できます。出力は、オプションによって決まります。デフォルトでは、jdepsはシステム出力に依存関係を出力します。依存関係はDOT言語で生成できます(-dotoutputオプションを参照)。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/jdeps.html
これは Java8 から入ったコマンドで、クラスや jar の依存関係を出力してくれるとのことです。
使ってみます(サンプルは皆大好き seasar2 だよ)
$ $JAVA_HOME/bin/jdeps s2-framework-2.4.48.jar
s2-framework-2.4.48.jar -> 見つかりません
s2-framework-2.4.48.jar -> /Library/Java/JavaVirtualMachines/jdk1.8.0_11.jdk/Contents/Home/jre/lib/rt.jar
org.seasar.framework.aop (s2-framework-2.4.48.jar)
-> java.lang
-> java.lang.reflect
-> javassist 見つかりません
-> org.aopalliance.intercept 見つかりません
org.seasar.framework.aop.impl (s2-framework-2.4.48.jar)
-> java.io
-> java.lang
-> java.lang.reflect
-> java.util
-> java.util.regex
-> org.aopalliance.intercept 見つかりません
org.seasar.framework.aop.interceptors (s2-framework-2.4.48.jar)
-> java.io
-> java.lang
-> java.lang.reflect
-> java.text
-> java.util
-> javax.servlet.http 見つかりません
-> org.aopalliance.intercept 見つかりません
... 省略 ...
org.seasar.framework.unit (s2-framework-2.4.48.jar)
-> java.io
-> java.lang
-> java.lang.reflect
-> java.util
-> javax.servlet 見つかりません
-> javax.servlet.http 見つかりません
-> junit.framework 見つかりません
org.seasar.framework.util (s2-framework-2.4.48.jar)
-> java.beans
-> java.io
-> java.lang
-> java.lang.reflect
-> java.math
-> java.net
-> java.security
-> java.sql
-> java.text
-> java.util
-> java.util.jar
-> java.util.zip
-> javassist 見つかりません
-> javax.naming
-> javax.transaction 見つかりません
-> javax.transaction.xa
-> javax.xml.parsers
-> ognl 見つかりません
-> org.w3c.dom
-> org.xml.sax
-> org.xml.sax.helpers
org.seasar.framework.xml (s2-framework-2.4.48.jar)
-> java.io
-> java.lang
-> java.net
-> java.util
-> javax.xml.parsers
-> org.xml.sax
-> org.xml.sax.helpers
「見つかりません」というのは、依存している jar をクラスパスに追加することで依存 jar のファイル名が表示されます。
また、dot 言語での出力もサポートされており、Graphviz というツールを使えばグラフ構造の画像にすることができます。
$ $JAVA_HOME/bin/jdeps -s -dotoutput dots/ -cp lib/* s2-framework-2.4.48.jar
今回は jar に対して使ってみましたが、class ファイルに対して実行すれば、このクラスを修正したらどこに影響するかを調べるのに使えるかもしれませんね。