Jboss CommunityのBytemanを利用してみる



昨年末に「BytemanによるJava黒魔術」という記事をみて、どうやって実現しているのだろうかと興味津々でした。

「BytemanによるJava黒魔術」では、Jboss Communityの「Byteman」というツールを紹介しています。

WindowsEclipseで試したことと、さくらVPSで動かしているCassandraで実行した内容を書いていきたいと
思います。
 

Bytemanをインストールする

bytemanのDownloadsページからbyteman-1.5.1.zip(byteman binary + docs)をダウンロードします。

まずは、ローカルのWindows環境にダウンロードしたbyteman-1.5.1.zipを任意のパスに解凍しました。

 drive:\usr\local\byteman_1_5_1

docs\ProgrammersGuide.pdfにガイドがあったので、流し読みでながめてみました。
 

EclipseでBytemanを試してみる

試しにBytemanで利用してみるクラスは、下記の通りです。
 
BytemanSample.java

public class BytemanSample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ
		CoordinatorEngine ce = new CoordinatorEngine();
		ce.commit();
		String value = ce.commit("ce");
		
		ParticipantEngine pe = new ParticipantEngine();
		String value2 = pe.commit("pe");
		
	}

}

 
Engine.java

public interface Engine {
	public void commit();
}

 
CoordinatorEngine.java

public class CoordinatorEngine implements Engine {

	@Override
	public void commit() {
		System.out.println("CoordinatorEngine#commit() do ");
	}
	
	public String commit(String value) {
		System.out.println("CoordinatorEngine#<String>commit(String) do ");
		return new String("bk ce");
	}
}

 
ParticipantEngine.java

public class ParticipantEngine implements Engine {

	@Override
	public void commit() {
		System.out.println("ParticipantEngine#commit() do ");

	}

	public String commit(String value) {
		System.out.println("ParticipantEngine#<String>commit(String) do ");
		return new String("bk pe");
	}
}

 
BytemanSampleクラスのmainメソッドを実行する時にVM引数として以下を付与します。

-javaagent:drive:\usr\local\byteman_1_5_1\lib\byteman.jar=listener:true,script:drive:\usr\local\byteman_1_5_1\rule\rule.txt,boot:drive:\usr\local\byteman_1_5_1\lib\byteman.jar -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.debug

 
rule.txtでは、Bytemanで挿入したい処理(ルール)を記述します。

RULE BytemanSample debug
CLASS jp.co.sample.common.sandbox.byteman.CoordinatorEngine
METHOD commit
AT EXIT
IF TRUE
DO traceln("bytemanDebug()"), traceStack()
ENDRULE

 
実行すると下記の様なログを出力します。

CoordinatorEngine#commit() do 
Default helper activated
Installed rule using default helper : BytemanSample debug
bytemanDebug()
Stack trace for thread main
jp.co.sample.common.sandbox.byteman.CoordinatorEngine.commit(CoordinatorEngine.java:8)
jp.co.sample.common.sandbox.byteman.BytemanSample.main(BytemanSample.java:11)
CoordinatorEngine#<String>commit(String) do 
Installed rule using default helper : BytemanSample debug
bytemanDebug()
Stack trace for thread main
jp.co.sample.common.sandbox.byteman.CoordinatorEngine.commit(CoordinatorEngine.java:12)
jp.co.sample.common.sandbox.byteman.BytemanSample.main(BytemanSample.java:12)
ParticipantEngine#<String>commit(String) do

 
Bytemanでは、下記の様なルールを設定することができます。(# rule skeleton)

No ルール 概要
1 RULE
2 CLASS
3 METHOD
4 BIND
5 IF
6 DO

 
rule2.txtでは、INTERFACEに対して処理を追加します。

RULE BytemanSample debug
INTERFACE jp.co.sample.common.sandbox.byteman.Engine
METHOD commit
AT EXIT
IF TRUE
DO traceln("bytemanDebug()"), traceStack()
ENDRULE

Bytemanの詳しいルールに関しては、まだためし中で良くわかりません。
(いろいろできるみたいですが、読みきれて、試しきれていません)
 

さくらVPSで動かしているCassandraにbytemanを試してみる

wgetでbyteman-1.5.1.zipを取得します。

wget http://downloads.jboss.org/byteman/1.5.1/byteman-1.5.1.zip

 
任意のパスに展開し、Cassandraのenvスクリプトに追加します。
byteman-1.5.1.zipを展開する

unzip byteman-1.5.1.zip
/usr/local/byteman/

 
Cassandraのenvスクリプト(cassandra-env.sh)に追加する

JVM_OPTS="$JVM_OPTS -javaagent:/usr/local/byteman/lib/byteman.jar=listener:true,script:/usr/local/byteman/rule/rule.txt,boot:/usr/local/byteman/lib/byteman.jar -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.debug"

 
rule.txt

 RULE cassandraDebug
 CLASS org.apache.cassandra.thrift.CassandraDaemon
 METHOD setup()
 AT EXIT
 IF TRUE
 DO traceln("bytemanDebug()"), traceStack()
 ENDRULE

 
起動ログ

 INFO 16:02:26,053 Using TFramedTransport with a max frame size of 15728640 bytes.
Default helper activated
Installed rule using default helper : cassandraDebug
bytemanDebug()
Stack trace for thread main
org.apache.cassandra.thrift.CassandraDaemon.setup(CassandraDaemon.java:114)
org.apache.cassandra.service.AbstractCassandraDaemon.activate(AbstractCassandraDaemon.java:217)
org.apache.cassandra.thrift.CassandraDaemon.main(CassandraDaemon.java:134)

上記の例では、「org.apache.cassandra.thrift.CassandraDaemon#setup()」メソッド実行時にbytemanに
処理をトレースさせています。
 

Bytemanの仕組み

Bytemanは、JDK(5以降)の-javaagentというJVMオプションを利用して、処理を実施しているようです。

Instrumentationという処理を利用して実施しているようです。

JavaDocにそれらしい説明がないか探してみましたがみつけられませんでした。
かわりに、「java.lang.instrument」というインターフェイスがあることをみつけました。

IBMのdeveloperworksに「エージェントを作成する」という部分で使い方を説明しているようです。

機会をみていろいろ引き続きいろいろ試してみたいと思います。