Byteman 使い方メモ＋α

この記事は、[Java Advent Calendar 2015](http://qiita.com/advent-calendar/2015/java) の 14 日目の記事です。
昨日は [@skrb](https://twitter.com/skrb) さんの [JRE をカスタマイズ - jlink ](http://www.javainthebox.com/2015/12/jre-jlin.html) でした。
明日は [@yukung](https://twitter.com/yukung) さんです。

最近存在を知ったので、使い方を調べてみました。

最後に [+α](#α) も用意してますので、よければ見てやってください。

# 環境
## OS
Windows 7 64bit SP1

## Java
1.8.0_65

# Byteman とは
[Byteman - JBoss Community](http://byteman.jboss.org/)

読みは、たぶん「ばいとまん」。

Java プログラムの任意の場所に任意のコードを差し込むことができるライブラリ（ツール）。
予め差し込むことも、動いているプログラムに後から差し込むこともできる。

今動いているプログラムを停止させることなく中の動作を検証できるので、再起動やデバッガで動作を止めたりできない環境の検証とかに使えるかもしれない。

公式ではテストで使う方法が紹介されており、 BMUnit という JUnit で Byteman を使うためのツールが提供されているっぽい（<del><small>jmockit があるからとりあえずはいいかな</small></del>）。

# Hello World
## インストール
[こちら](http://byteman.jboss.org/downloads) から zip ファイルをダウンロードする。

とりあえずソースは要らないのでバイナリのみのやつをダウンロードした。

zip を解凍してできたディレクトリを `BYTEMAN_HOME` という環境変数に割り当てる。
さらに、 `%BYTEMAN_HOME%\bin` を環境変数 `PATH` に追加する。

コマンドラインを立ち上げて、以下のコマンドを実行してインストールが完了していることを確認する。

```text:インストール完了確認
> bmjava
usage: bmjava  [-p port] [-h host] [-l rulescript | -b bootjar | -s sysjar | -nl | -nb | -nj ]* [--] javaargs
（以下略）
```

## 簡単な Java プログラムを作る
コードを差し込む対象となる、簡単な Java プログラムを作る。

```java:Main.java
public class Main {
    public static void main(String... args) {
        System.out.println("Hello World!!");
    }
}
```

Hello World するだけの簡単なプログラム。

## Byteman の設定ファイルを作る
差し込むコードを定義する、 Byteman の設定ファイルを作成する。

```text:hello.btm
RULE hello byteman
CLASS Main
METHOD main
AT ENTRY
IF true
DO traceln("Hello Byteman!!")
ENDRULE
```

## 実行する
まずは `Main.java` をコンパイルして。

```text:Main.javaのコンパイル
> javac Main.java
```

Byteman の設定ファイル（`hello.btm`）を指定して `Main` を実行する。

```text:Bytemanを使って実行
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:hello.btm Main
Hello Byteman!!
Hello World!!
```

`Hello Byteman!!` が呼びだされた！

## 説明
Byteman を使うときには、最低限 `byteman.jar` と設定ファイル（`*.btm`）が必要になる。

`byteman.jar` はクラスパスで指定するのではなく、 `-javaagent` オプションで指定する。
このとき、 `=script:<設定ファイル名>` という形で使用する設定ファイルを指定する。

あとは、普通に Java を実行するときと同じように、メインメソッドを持つクラスを指定する。

すると、 Byteman が設定ファイルを読み込んで、設定ファイルに記述された場所にコードを埋め込んだ状態でプログラムを実行してくれる。

設定ファイルの書き方については、おいおい。

# `-javaagent` の記述を省略する
コマンドラインから実行するときに、毎回 `-javaagent` を指定するのはしんどい。
そこで、 Byteman にはこれらの記述を省略して Java コマンドを実行できる起動用のスクリプトが用意されている。

`%BYTEMAN_HOME%\bin` の下に入っている `bmjava` というスクリプトがそれになる。

`bmjava` を使うと、設定ファイルの記述を `-l` オプションで指定できるようになる。

```text:bmjavaを使った場合
> bmjava -l hello.btm Main
Hello Byteman!!
Hello World!!
```

# 複数の設定ファイルを指定する
メインメソッドの後で `See you Byteman!!` と出力する `see-you.btm` を作成して、 `hello.btm` と合わせて実行してみる。

```text:see-you.btm
RULE see you byteman
CLASS Main
METHOD main
AT EXIT
IF true
DO traceln("See you Byteman!!")
ENDRULE
```

まずは、 `-javaagent` を明示する方法で。

```text:hello.btmとsee-you.btmを２つ指定する（javaコマンド）
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:hello.btm,script:see-you.btm Main
Hello Byteman!!
Hello World!!
See you Byteman!!
```

カンマ (`,`) 区切りで `script:<設定ファイル名>` の記述を続けることで複数の設定ファイルを読み込ませることができる。

次に `bmjava` を使った場合。

```text:hello.btmとsee-you.btmを２つ指定する（bmjavaコマンド）
> bmjava -l hello.btm -l see-you.btm Main
Hello Byteman!!
Hello World!!
See you Byteman!!
```

`-l` オプションを複数宣言すればいい。

# 動作中のプログラムにルールを動的に追加・削除する
Byteman によるコードの差し込みは、動作中のプログラムに対しても実行できる。

ここでは、以下のプログラムを走らせて、動的にルールを追加したり削除したりしてみる。

```java:Main.java
public class Main {
    public static void main(String... args) throws Exception {
        int i=0;
        while (true) {
            print(i++);
            Thread.sleep(1000);
        }
    }

    private static void print(int i) {
        System.out.println("i = " + i);
    }
}
```

無限ループしながら、 `i` をインクリメントしつつ標準出力するだけの実装。

## リスナーを指定してプログラムを起動する
実行中のプログラムにルールを動的に適用するには、プログラムと一緒にリスナーを動かす必要がある。

```text:リスナーを一緒に起動する
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=listener:true Main
```

リスナーを一緒に起動するには、 `-javaagent` の指定に `listener:true` を追加する。

なお、 `bmjava` コマンドを使用した場合は、デフォルトでリスナーが一緒に起動するようになっている。

### 現在適用されているルールを確認する
上述の方法でリスナーと一緒にプログラムを起動しておく。

その状態で別のコマンドラインを立ち上げ、以下のようにコマンドを実行する。

```text:
> bmsubmit -l
no rules installed
```

`bmsubmit` というのが、動作中のプログラムに対していろいろするためのコマンドになっている。

現在は何もルールを指定していないので、 `no rules installed` と出力される。

以下は実際に動いている時の様子。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/f3887771-47f1-f4c7-54b8-a5e333c9d3cb.gif)

ちなみに、 `bmsubmit` コマンドは、裏では以下の Java プログラムを実行している。

```text:bmsubmitの実体
> java -classpath %BYTEMAN_HOME%\lib\byteman-submit.jar org.jboss.byteman.agent.submit.Submit
```

### ルールを追加する
以下のルールを追加してみる。

```text:print.btm
RULE see you byteman
CLASS Main
METHOD print
AT ENTRY
IF true
DO traceln("print(" + $1 + ")")
ENDRULE
```

`print()` メソッドの実行前に差し込んで、 `print(<引数の値>)` を出力するようにしている。

先ほどと同じように `Main` クラスをリスナーと共に起動しておく。

そして、以下のように `bmsubmit` コマンドを実行する。

```text:
> bmsubmit -l hello.btm
```

実際に動かしたときの様子を見ると分かりやすい。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/6fd97ce5-b53f-a93c-eed7-ea4667976a96.gif)

`bmsubmit -l hello.btm` を実行した瞬間、 `print.btm` で記述した処理が差し込まれるようになったのがわかる（おもしろい）。

ルールを追加した状態で改めて `bmsubmit -l` を実行すると、追加したルールの情報が出力されるようになっている。

```text:追加されたルールの情報を出力した結果
> bmsubmit -l
# File print.btm line 5
RULE see you byteman
CLASS Main
METHOD print
AT ENTRY
IF true
DO traceln("print(" + $1 + ")")
ENDRULE
Transformed in:
loader: sun.misc.Launcher$AppClassLoader@14dad5dc
trigger method: Main.print(int) void
compiled successfully
```

### ルールを削除する
次は、逆にルールを削除してみる。

削除するときのコマンドは以下。

```text:
> bmsubmit -u hello.btm
```

実際に動いている様子は以下。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/c6e4f4af-49c1-846e-0fa3-a54d0d2c4666.gif)

コマンドを実行した直後に `print(...)` の出力が止まったのが分かる。

`bmsubmit -l` を再度実行すると、ルールが全てなくなり再び `no rules installed` と出力されている。

## 既に起動しているプログラムにリスナーを追加する
リスナーを指定せずに起動されたプログラムに対して、後からリスナーを追加することができる。

まずは普通にプログラムを起動する。

```text:Javaプログラムを起動する
> java Main
```

次に別のコマンドラインを立ち上げ、先ほど起動した Java プログラムのプロセス ID を調べる。

```text:JavaプログラムのプロセスIDを調査する
> jps
1584 Main
5292 Jps
```

プロセスID が分かったら、 `bminstall` コマンドでリスナーを追加する。

```text:リスナーを追加する
> bminstall 1584
```

`bminstall` の引数には、先ほど調べた Java プログラムのプロセス ID を指定する。

これでリスナーが登録できたので、あとは `bmsubmit` でルールの追加・削除ができるようになる。

実際に動かした様子が以下。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/09340fd3-eefc-158d-e323-4910de4c3dbb.gif)

# ブートストラップクラスローダに読み込まれるクラスにコードを差し込む
`java.lang.String` や `java.math.BigInteger` など、ブートストラップクラスローダによって読み込まれるクラスは、そのままではコードの差し込みができない。

```text:toString.btm
RULE hook toString
CLASS BigInteger
METHOD toString()
AT ENTRY
IF true
DO traceln("toString() is called")
ENDRULE
```

```java:Main.java
import java.math.BigInteger;

public class Main {
    public static void main(String... args) throws Exception {
        System.out.println(new BigInteger("123"));
    }
}
```

```text:普通に実行
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:toString.btm Main
Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/byteman/rule/exception/EarlyReturnException
        at java.math.BigInteger.toString(Unknown Source)
        at java.lang.String.valueOf(Unknown Source)
        at java.io.PrintStream.println(Unknown Source)
        at Main.main(Main.java:5)
```

エラーになる。

これは、 `BigInteger` と Byteman のコードがそれぞれブートストラップクラスローダとシステムクラスローダによってロードされているために起こってるらしい。

その辺の仕組み的な話は以下で解説されています。

- [java.lang.NoClassDefFoundError: org/jboss/bytem... | JBoss Developer](https://developer.jboss.org/thread/252119)
- [（補足）　クラスローダ | TECHSCORE(テックスコア)](http://www.techscore.com/tech/Java/JavaEE/Servlet/supplement-1/)

ブートストラップクラスローダがロードするクラスにもコードを差し込みたい場合は、以下のように `-javaagent` を設定する必要がある。

```text:ブートストラップクラスローダにBytemanを読み込ませる
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:toString.btm,boot:%BYTEMAN_HOME%\lib\byteman.jar  Main
toString() is called
123
```

`boot:%BYTEMAN_HOME%\lib\byteman.jar` というのを追加する。

## `java.lang` 以下のクラスにもコードを差し込む
これで全てのクラスにコードを差し込めるようになったかというと、そうでもない。

デフォルトでは、 `java.lang` 以下のクラスに対してはコードの差し込みが無視されるようになっている。

```text:toString.btm
RULE hook toString
CLASS String
METHOD toString()
AT ENTRY
IF true
DO traceln("toString() is called")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String... args) throws Exception {
        System.out.println("String Message".toString());
    }
}
```

```text:とりあえず実行してみる
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:toString.btm,boot:%BYTEMAN_HOME%\lib\byteman.jar Main
String Message
```

エラーにはならないが、コードの差し込みは実行されていない。

`java.lang` 以下のクラスも変更できるようにするには、更にシステムプロパティを明示しなければならない。

```text:システムプロパティを指定する
> java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:toString.btm,boot:%BYTEMAN_HOME%\lib\byteman.jar -Dorg.jboss.byteman.transform.all Main
toString() is called
String Message
```

システムプロパティとして `org.jboss.byteman.transform.all` を指定することで、 `java.lang` 以下のクラスにもコードを差し込めるようになる。

以上の追加の設定は、 `bmjava` コマンドを使うとデフォルトで追加してくれる。
なので、以下のように設定ファイルの指定だけで `java.lang` 以下のクラスにもコードを差し込めるようになっている。

```text:bmjavaを使った場合
> bmjava -l toString.btm Main
toString() is called
String Message
```

# ルールの記法
## コメント
```text:
# ★ コメント...
RULE hook toString
CLASS String
METHOD toString()
AT ENTRY
IF true
DO traceln("toString() is called")
ENDRULE
```

- `#` で始まる行はコメント扱いになる。
- `#` より後ろがコメントになるわけではない。
    - `CLASS String # comment...` のように各定義の後ろにコメントっぽくつけると、良くてエラー、悪くてルールが適用されず無視されることがある。

## ルール名
```text:
# ★↓ここ
RULE hook toString
CLASS String
METHOD toString()
AT ENTRY
IF true
DO traceln("toString() is called")
ENDRULE
```

- `RULE` で宣言する。
- そのルールの名前を定義する。
- 最低でも１つ、空白スペース以外の文字が含まれていれば、あとはどんな文字を使っても良い。
- ルールごとに一意になるようにつけたほうが良い。

## クラスを指定する
```text:
RULE hook toString
# ★↓ここ
CLASS String
METHOD toString()
AT ENTRY
IF true
DO traceln("toString() is called")
ENDRULE
```

- `CLASS` で宣言する。
- パッケージ名を含めてもいいし、含めなくてもいい。
- パッケージ名を含めなかった場合は、名前が一致する全てのクラスが対象になる。

## インナークラスを指定する
```text:
RULE sample
CLASS Main$InnerClass
METHOD method
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new InnerClass().method();
    }
    
    public static class InnerClass {
        public void method() {
            System.out.println("InnerClass.method()");
        }
    }
}
```

```text:実行結果
Byteman Sample!!
InnerClass.method()
```

- インナークラスを対象としたい場合は、 `<包含するクラス>$<インナークラス名>` とする。

## インターフェースを指定する
```text
RULE sample
# ★↓ここ
INTERFACE MyInterface
METHOD method
AT ENTRY
IF true
DO traceln("interface")
ENDRULE
```

```java:MyInterface.java
public interface MyInterface {
    void method();
}
```

```java:Hoge.java
public class Hoge implements MyInterface {
    public void method() {
        System.out.println("Hoge");
    }
}
```

```java:Fuga.java
public class Fuga implements MyInterface {
    public void method() {
        System.out.println("Fuga");
    }
}
```

```java:Main.java
public class Main {
    public static void main(String... args) throws Exception {
        new Hoge().method();
        new Fuga().method();
    }
}
```

```text:実行結果
interface
Hoge
interface
Fuga
```

- `INTERFACE` で宣言する。
- 指定したインターフェースを実装したクラスが全て対象になる。
- `CLASS` と同じく、パッケージは省略可能。

## メソッドを指定する
```text:
RULE sample
INTERFACE MyInterface
# ★↓ここ
METHOD method
AT ENTRY
IF true
DO traceln("interface")
ENDRULE
```

- `METHOD` で宣言する。
- 戻り値と引数の型は省略可能。
    - 戻り値を省略しないで書くと `void method` 。
    - 引数を省略しないで書くと `method()` になる。
        - 引数を受け取る場合は `method(String, int, double)` みたいな感じで型名をカンマ区切りで列挙する。

## コンストラクタを指定する
```text:
RULE sample
CLASS Main
# ★ <init> で指定
METHOD <init>
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

- コンストラクタを対象にする場合は、 `<init>` というふうに指定する。
- 引数も含めて指定する場合は `<init>(String)` という感じで記述する。

## サブクラスでオーバーライドされたメソッドも対象にする
デフォルトだと、 `CLASS` で指定したクラスにしかルールが適用されず、サブクラスでオーバーライドされたメソッドにはルールが適用されない。
このため、サブクラスでオーバーライドされたメソッドにもルールを適用したい場合は、サブクラスごとにルールを定義しないといけない。

それはあまりにも辛い。
なので、サブクラスでオーバーライドされたメソッドにも一括でルールを適用する特別な設定方法が用意されている。

```text:
RULE sample
# ★先頭に ^ をつける
CLASS ^Base
METHOD method
AT ENTRY
IF true
DO traceln("call method()")
ENDRULE
```

```java:Base.java
public class Base {
    public void method() {
        System.out.println("Base.method()");
    }
}
```

```java:Hoge.java
public class Hoge extends Base {
    @Override
    public void method() {
        System.out.println("Hoge.method()");
    }
}
```

```java:Fuga.java
public class Fuga extends Base {
    @Override
    public void method() {
        System.out.println("Fuga.method()");
    }
}
```

```java:Main.java
public class Main {
    
    public static void main(String[] args) {
        new Base().method();
        new Hoge().method();
        new Fuga().method();
    }
}
```

```text:実行結果
call method()
Base.method()
call method()
Hoge.method()
call method()
Fuga.method()
```

`CLASS` で指定するクラス名の先頭に `^` を付ける。
これで、そのクラスのサブクラスでオーバーライドされたメソッドにも、同じルールが適用されるようになる。

### 親メソッドを呼び出したときの二重適用を防ぐ
例えば `super` などで親のメソッドを明示的に呼び出している場合、ルールが二重で適用されることがある。
明示していなくても、デフォルトコンストラクタなどは暗黙的に呼び出されるので、意図せずルールが適用される可能性がある。

親クラスのメソッド呼び出し時はルールを適用させたくない場合は、以下のように呼び出し元のメソッドを検証する方法がある。

```text
RULE sample
CLASS ^Base
METHOD method
AT EXIT
# ★ callerEquals() で呼び出し元のメソッドを検証する
IF NOT callerEquals("method")
DO traceln("call method()")
ENDRULE
```

```java:Hoge.java
public class Hoge extends Base {
    @Override
    public void method() {
        super.method();
        System.out.println("Hoge.method()");
    }
}
```

```java:Main.java
public class Main {
    
    public static void main(String[] args) {
        new Hoge().method();
    }
}
```

```text:実行結果
Base.method()
call method()
Hoge.method()
```

`callerEquals()` メソッドで、呼び出し元のメソッドが対象のメソッドと同じかどうかを検証する。
同じ場合は親メソッドの呼び出しと判断して対象外にする（`NOT` で否定している）。

## インターフェースに `^` を指定する
`INTERFACE` でインターフェースを指定した場合、そのインターフェースを実装したクラス全てが対象になる。
しかし、インターフェースを実装したあるクラスがメソッドを実装していて、さらにそのサブクラスがメソッドをオーバーライドしている場合、サブクラスのメソッドはルール適用の対象外になる。

```text:
RULE sample
INTERFACE MyInterface
METHOD method
AT EXIT
IF true
DO traceln("call method()")
ENDRULE
```

```java:MyInterface.java
public interface MyInterface {
    public void method();
}
```

```java:AbstractClass.java
public abstract class AbstractClass implements MyInterface {
    @Override
    public void method() {
        System.out.println("AbstractClass.method()");
    }
}
```

```java:SubClass.java
public class SubClass extends AbstractClass {
    @Override
    public void method() {
        super.method();
        System.out.println("SubClass.method()");
    }
}
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new SubClass().method();
    }
}
```

```text:実行結果
AbstractClass.method()
call method()
SubClass.method()
```

`MyInterface` を直接実装した `AbstractClass` の `method()` メソッドにはルールが適用されているが、 `SubClass` でオーバーライドした `method()` メソッドにはルールが適用されていない。
標準 API の中では、 `List`, `AbstractList`, `ArrayList` の３つがまさにこの関係になっている。

`SubClass` の `method()` にもルールを適用せたい場合は、 `^` をインターフェース名の前に付ける。

```text:
RULE sample
# ★ 先頭に ^ を追加
INTERFACE ^MyInterface
METHOD method
AT EXIT
IF true
DO traceln("call method()")
ENDRULE
```

```text:実行結果
AbstractClass.method()
call method()
SubClass.method()
call method()
```

`SubClass.method()` も対象になっている。

## コードを差し込む場所を指定する
### デフォルト
```text
RULE sample
CLASS Main
METHOD main
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("main method.");
    }
}
```

```text:実行結果
Byteman Sample!!
main method.
```

- 何も指定しない場合、メソッドの先頭に処理が差し込まれる。
- `AT ENTRY` を指定した場合も同様の動作になる。
- 親を持つクラスのコンストラクタの場合、親のコンストラクタが暗黙的に呼ばれた後に実行される。

### メソッドの最後に処理を差し込む
```text:
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AT EXIT
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```text:実行結果
main method.
Byteman Sample!!
```

- `AT EXIT` と指定すると、メソッドの最後に処理を差し込める。

### 行番号を指定して差し込む
```text:
RULE sample1
CLASS Main
METHOD main
# ★↓ここ
AT LINE 4
IF true
DO traceln("at line 4")
ENDRULE

RULE sample2
CLASS Main
METHOD main
# ★↓ここ
AT LINE 5
IF true
DO traceln("at line 5")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("hoge"); // 3 行目
        System.out.println("fuga"); // 4 行目
        System.out.println("piyo"); // 5 行目
    }
}
```

```text:実行結果
hoge
at line 4
fuga
at line 5
piyo
```

- `AT LINE <行番号>` で、行番号指定の差し込みができる。
- コードを差し込んでも、後ろの行番号がずれることはない。
- メソッドの外になる行番号を指定した場合、エラーにはならず無視される。
    - ただし、 `0` や `-1` のように上方向にズレた位置を指定した場合は、メソッドの先頭でコードが差し込まれる模様。

### メンバー変数が読み取られる直前にコードを差し込む
```text:
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AT READ value
IF true
DO traceln("value is read (x = " + $x + ")")
ENDRULE
```

```java:Main.java
public class Main {

    private String value = "main value";

    public static void main(String[] args) {
        String x = null;
        Main main = new Main();
        
        System.out.println("1");
        x = main.value + " 1";
        System.out.println("2");
        x = main.value + " 2";
    }
}
```

```text:実行結果
1
value is read (x = null)
2
```

- `AT READ <メンバー変数名>` で、指定したメンバー変数が**最初に**読み取られたときの直前に処理を差し込める。
- `static` な変数も対象になる。
- `$x` はローカル変数 `x` を参照している。

#### n 回目に読み取られる直前に処理を差し込む
```text:
RULE sample
CLASS Main
METHOD main
# ★ 2 を指定
AT READ value 2
IF true
DO traceln("value is read (x = " + $x + ")")
ENDRULE
```

```text:実行結果
1
2
value is read (x = main value 1)
```

- 変数名の後ろに数値を指定することで、指定した回数目に変数が読み取られる直前に処理を差し込める。
- 常に処理を差し込みたい場合は、数値の代わりに `ALL` と指定する。

```text:常に処理を差し込みたい場合
RULE sample
CLASS Main
METHOD main
# ★ ALL を設定する
AT READ value ALL
IF true
DO traceln("value is read")
ENDRULE
```

#### 特定のクラスのフィールドに絞り込む
```text:
RULE sample
CLASS Main
METHOD main
# ★ 変数名の前にクラス名を追加
AT READ Main$Fuga.value
IF true
DO traceln("Fuga.value is read")
ENDRULE
```

```java:Main.java
public class Main {

    public static void main(String[] args) {
        System.out.println("Hoge");
        String xxx = new Hoge().value;
        System.out.println("Fuga");
        int yyy = Fuga.value;
    }
    
    public static class Hoge {
        public String value;
    }
    
    public static class Fuga {
        public static int value;
    }
}
```

```text:実行結果
Hoge
Fuga
Fuga.value is read
```

- 変数名の前に `<クラス名>.` を追加することで、特定のクラスのメンバー変数を読み込んだときだけに限定できる。

### ローカル変数が読み取られたときの直前にコードを差し込む
```text:
RULE sample
CLASS Main
METHOD main
# ★ 変数名の前に $ をつける
AT READ $value
IF true
DO traceln("xxxx = " + $xxxx)
ENDRULE
```

```java:Main.java
public class Main {

    public static void main(String[] args) {
        String xxxx = "hoge";
        String value = "fuga";
        
        System.out.println("read value");
        xxxx = value;
    }
}
```

```text:実行結果
read value
xxxx = hoge
```

- 変数名の前に `$` をつけると、ローカル変数が対象になる。
- メソッド引数も対象になる。
- 回数の指定などはメンバー変数を指定した場合と同じ。

### メンバー変数に書き込まれる直前に処理を差し込む
```text
RULE sample
CLASS Main
METHOD main
# ★ ここ
AT WRITE value
IF true
DO traceln("write value")
ENDRULE
```

```java:Main.java
public class Main {

    private String value = "hoge";

    public static void main(String[] args) {
        System.out.println("write to Main.value");
        new Main().value = "fuga";
    }
}
```

```text:実行結果
write to Main.value
write value
```

- `AT WRITE` で、メンバー変数に書き込まれる直前に処理を差し込める。
- その他の使い方は `AT READ` のときと同じ。

### メソッドが呼ばれる直前に処理を差し込む
```text
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AT INVOKE method
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {

    public static void main(String[] args) {
        System.out.println("start");
        method();
        method();
        System.out.println("end");
    }
    
    public static void method() {
        System.out.println("method()");
    }
}
```

```text:実行結果
start
Byteman Sample!!
method()
method()
end
```

- `AT INVOKE` で、指定したメソッドが呼び出される直前に処理を差し込むことができる。
- 変数の参照のときと同じで、デフォルトは最初の一回目の呼び出しにだけ適用される。
    - メソッド名の後ろに数値や `ALL` を指定することで、同じように調整可能。
    - メソッド名の前にクラス名を入れることで、クラスの限定も可能。

### メソッドが呼ばれた直後に処理を差し込む
```text
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AFTER INVOKE method
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```text:実行結果
start
method()
Byteman Sample!!
method()
end
```

- `AFTER INVOKE` にすれば、メソッド呼び出しの直後に処理を差し込める。
- その他の使い方は、 `AT INVOKE` の場合と同じ。

### `synchronized` ブロックの前後に処理を差し込む
```text
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AT SYNCHRONIZE
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {

    public static void main(String[] args) {
        System.out.println("start");
        
        synchronized(Main.class) {
            System.out.println("in synchronized");
        }
        
        System.out.println("end");
    }
}
```

```text:実行結果
start
Byteman Sample!!
in synchronized
end
```

- `AT SYNCHRONIZE` で、 `synchronized` ブロックの前に処理を挟める。
    - デフォルトでは、変数の場合と同じで最初のブロックだけが対象になる。
    - 数値または `ALL` で、同じように調整可能。
- 後に処理を挟む場合は、 `AFTER SYNCHRONIZE` を使う。
- 差し込まれたコードが `synchronized` ブロックの中で実行されるのか外で実行されるのかは、僕の力量では読み取れませんでした。。。

### 例外がスローされるときにコードを差し込む
```text
RULE sample
CLASS Main
METHOD main
# ★↓ここ
AT THROW
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {

    public static void main(String[] args) {
        try {
            throw new Exception("test");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
```

```text:実行結果
Byteman Sample!!
test
```

- `AT THROW` で、例外がスローされるときに処理を差し込める。
    - デフォルトでは、変数の場合と同じで最初の `throw` だけが対象になる。
    - 数値または `ALL` で、同じように調整可能。

## 変数
### 変数を宣言する
```text
RULE sample
CLASS Main
METHOD method
# ★↓ここ
BIND hoge = "HOGE";
     mainCount = $0.count;
IF true
DO traceln("hoge=" + hoge + ", mainCount=" + mainCount)
ENDRULE
```

```java:Main.java
public class Main {
    public int count;

    public static void main(String[] args) {
        Main m = new Main();
        
        m.method();
        m.method();
    }
    
    public void method() {
        this.count++;
    }
}
```

```text:実行結果
hoge=HOGE, mainCount=0
hoge=HOGE, mainCount=1
```

- `BIND` 句を使って変数を定義できる。
- 変数の宣言は `<変数名> = <値>;` で定義できる。
    - セミコロンは複数行宣言するときの区切りとして必要なので、１行だけの場合や最後の変数宣言では省略が可能。
    - 型を明示することもでき、その場合は `<変数名>:<型名> = <値>` とする。
- 変数宣言はルールで指定したコードが実行されるたびに初期化される。
- `$0` は、対象のメソッド (`method()`) を持つインスタンスそのもの (`Main` のインスタンス) を参照している。
- 宣言した変数は、処理の本体や条件文などで使用することができるようになる。

### 暗黙的に使用できる変数
`BIND` を使って宣言する変数以外に、 `$0` のように暗黙的に使用できる変数が用意されている。

|   変数名   |                        参照できる値                        |
|------------|------------------------------------------------------------|
| `$0`       | 実行されたメソッドを持つインスタンス。                     |
| `$n`       | メソッドの n 番目の引数。                                  |
| `$*`, `$@` | 実行されたメソッドを持つインスタンスと、メソッドの全引数。 |
| `$#`       | メソッド引数の数。                                         |
| `$!`       | メソッドの戻り値。                                         |
| `$^`       | スローされた例外。                                         |
| `$CLASS`   | 現在のクラス名。                                           |
| `$METHOD`  | 現在のメソッド名。                                         |

#### 実行されたメソッドを持つインスタンスを参照する
```text
RULE sample
CLASS Main
METHOD method
IF true
DO traceln("$0 = " + $0);
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new Main().method();
    }
    
    public void method() {}
}
```

```text:実行結果
$0 = Main@4b1210ee
```

- `$0` で、実行されているメソッドを持つインスタンスを参照できる。
- `static` メソッドが実行されているときに参照した場合はルールが無効になり何も実行されなくなる。
- `$this` でも同じ値を参照できる。

#### メソッドの引数を参照する
```text
RULE sample
CLASS Main
METHOD method
IF true
DO traceln("$1 = " + $1 + ", $2 = " + $2);
ENDRULE
```

<span id="ref_method_params_code"></span>

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new Main().method("hoge", 21);
    }
    
    public void method(String a, int b) {}
}
```

```text:実行結果
$1 = hoge, $2 = 21
```

- `$<１以上の数値>` で、メソッドの引数を参照できる。
- 引数の数よりも大きい値を参照しようとすると、ルールが無効になって何も実行されなくなる。

#### メソッドを持つインスタンスと引数を全て参照する
```text
RULE sample
CLASS Main
METHOD method
IF true
DO traceln("$* = " + java.util.Arrays.toString($*));
ENDRULE
```

[Java 実装](#ref_method_params_code)

```text:実行結果
$* = [Main@4d7e1886, hoge, 21]
```

- `$*` で `$<0 以上の数値>` で取得できる全ての変数を格納した配列を参照できる。
- `static` メソッド参照した場合は、インデックス `0` の部分に `null` が格納される。

##### `AT INVOKE` のときに参照する
```text
RULE sample
CLASS Main
METHOD main
AT INVOKE method
IF true
DO traceln("$@ = " + java.util.Arrays.toString($@));
ENDRULE
```

[Java 実装](#ref_method_params_code)

```text:実行結果
$@ = [Main@3cd1a2f1, hoge, 21]
```

- `AT INVOKE` で指定したメソッドの引数を参照する場合は `$@` を使用する。
- こちらも、 `static` メソッドを対象にした場合はインデックス `0` に `null` が設定される。

#### メソッド引数の数を参照する
```text
RULE sample
CLASS Main
METHOD method
IF true
DO traceln("$# = " + $#);
ENDRULE
```

[Java 実装](#ref_method_params_code)

```text:実行結果
$# = 2
```

#### メソッドの戻り値を参照する
```text
RULE sample
CLASS Main
METHOD method
AT EXIT
IF true
DO traceln("$! = " + $!);
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new Main().method();
    }
    
    public String method() {
        return "RESULT";
    }
}
```

```text:実行結果
$! = RESULT
```

- `$!` でメソッドの戻り値を参照できる。
- これが使えるのは `AT EXIT` か `AFTER INVOKE` のときだけ。

#### スローされた例外を参照する
```text
RULE sample
CLASS Main
METHOD main
AT THROW
IF true
DO traceln("$^.message = " + $^.getMessage());
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        throw new Exception("test exception");
    }
}
```

```text:実行結果
$^.message = test exception
```

- `$^` で、スローされた例外を参照できる。
- これが使えるのは、 `AT THROW` のときだけ。

#### 現在のクラス名を参照する
```text
RULE sample
CLASS Main
METHOD main
IF true
DO traceln("$CLASS = " + $CLASS);
ENDRULE
```

```java:Main.java
package sample.byteman;

public class Main {
    
    public static void main(String[] args) {
    }
}
```

```text:実行結果
$CLASS = sample.byteman.Main
```

- `$CLASS` で、現在のクラスの FQCN を文字列で参照できる。

#### 現在のメソッド名を参照する
```text
RULE sample
CLASS Main
METHOD main
IF true
DO traceln("$METHOD = " + $METHOD);
ENDRULE
```

```text:実行結果
$METHOD = main(java.lang.String[]) void
```

- `$METHOD` で、現在のメソッドの名前を文字列で参照できる。

## 条件
```text
RULE sample
CLASS Main
METHOD method
# ★↓ここ
IF $this.flag
DO traceln("Byteman Sample!!");
ENDRULE
```

```java:Main.java
public class Main {
    
    private boolean flag;

    public static void main(String[] args) throws Exception {
        Main main = new Main();
        
        main.method();
        main.flag = true;
        main.method();
    }
    
    public void method() {
        System.out.println("Main.method()");
    }
}
```

```text:実行結果
Main.method()
Byteman Sample!!
Main.method()
```

- `IF` で指定した条件が `true` のときだけ処理の差し込みが実行される。
- 常に実行したい場合は `IF true` とすればいい。

### 論理演算子を英単語で記述する
```text
RULE sample
CLASS Main
METHOD main
IF true AND (false OR true)
DO traceln("Byteman Sample!!");
ENDRULE
```

```text:実行結果
Byteman Sample!!
```

- `&&` は `AND`、 `||` は `OR` のように、論理演算子を英単語に置き換えることができる。
- 論理演算子以外にも、比較演算子・算術演算子も英単語に置き換えられる（論理演算子以外は記号の方が可読性高いと思うけど）。

|     演算子     |  英単語  |
|----------------|----------|
| <code>&#124;&#124;</code> | `OR`     |
| `&&`           | `AND`    |
| `!`            | `NOT`    |
| `<=`           | `LE`     |
| `<`            | `LT`     |
| `==`           | `EQ`     |
| `!=`           | `NE`     |
| `>=`           | `GE`     |
| `>`            | `GT`     |
| `*`            | `TIMES`  |
| `/`            | `DIVIDE` |
| `+`            | `PLUS`   |
| `-`            | `MINUS`  |
| `%`            | `MOD`    |

## 処理
### 基本
```text
RULE sample
CLASS Main
METHOD main
IF true
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
    }
}
```

```text:実行結果
Byteman Sample!!
```

- `DO` 句の中で差し込む処理を記述する。
- 処理はセミコロンで区切ることで、複数行にわたって記述することができる。
- `traceln()` は組み込みのメソッドで、標準出力に文字を出力する。

### 変数の値を変更する
```text
RULE sample
CLASS Main
METHOD main
AFTER READ $value
IF true
DO $value = "byteman"
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        String value = "hoge";
        
        System.out.println("value = " + value);
        System.out.println("value = " + value);
    }
}
```

```text:実行結果
value = hoge
value = byteman
```

- `<変数名> = <値>` とすることで、普通にコード上の変数の値を書き変えられる（なにこれこわい）。
    - 今回の例ではローカル変数を対象にしているので `$` を先頭につけている。
    - この辺は `AT READ` のときと同じ要領になる。

### メソッドの戻り値を差し替える
```text
RULE sample
CLASS Main
METHOD method
AT EXIT
IF true
DO $! = "byteman"
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("method() = " + method());
    }
    
    public static String method() {
        return "RESULT";
    }
}
```

```text:実行結果
method() = byteman
```

- `$!` に値を代入すれば、戻り値を差し替えられる（なにこれすごくこわい）。

また、 `return` 文を記述することでも戻り値を差し替えられる。

```text
RULE sample
CLASS Main
METHOD method
IF true
DO return "byteman"
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("method() = " + method());
    }
    
    public static String method() {
        System.out.println("Main.method()");
        return "RESULT";
    }
}
```

```text:実行結果
method() = byteman
```

- `method()` メソッドの中は実行されず、値が強制的返されている。

## 組み込みのヘルパークラス
`traceln()` などの暗黙的に利用できるメソッドの実体は、 `org.jboss.byteman.rule.helper.Helper` に定義されている。

全部調べるのは大変そうなので、目についたやつの使い方を調べる。

### カウントダウン
```text
RULE sample 1
CLASS Main
METHOD main
IF true
DO createCountDown("hoge", 3)
ENDRULE

RULE sample 2
CLASS Main
METHOD method
IF countDown("hoge")
DO traceln("Count ZERO")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        for (int i=0; i<5; i++) {
            method();
        }
    }
    
    public static void method() {
        System.out.println("Main.method()");
    }
}
```

```text:実行結果
Main.method()
Main.method()
Main.method()
Count ZERO
Main.method()
Main.method()
```

- `createCountDown()` で、カウントダウンするための初期化を行う。
    - 第一引数に、カウントダウンを識別するための値（`Object`）を渡す。
    - 第二引数に、カウントの初期値を設定する。
- `countDown()` で、カウントを１つ減らす。
    - 引数には、 `createCountDown()` のときに渡した識別子を渡す。
    - カウントが `0` になったタイミングで `true` を返す。
    - それ以外は常に `false` を返す。

### フラグ
```text
RULE sample 1
CLASS Main
METHOD flagOn
IF true
DO flag("hoge")
ENDRULE

RULE sample 2
CLASS Main
METHOD testFlagged
IF flagged("hoge")
DO traceln("Flagged!!")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        testFlagged();
        flagOn();
        testFlagged();
    }
    
    public static void flagOn() {
        System.out.println("Main.flagOn()");
    }
    
    public static void testFlagged() {
        System.out.println("Main.testFlagged()");
    }
}
```

```text:実行結果
Main.testFlagged()
Main.flagOn()
Flagged!!
Main.testFlagged()
```

- `flag()` で、フラグを立てる。
    - 引数には識別するための値を渡す。
- `flagged()` で、フラグが立っているか確認する。
    - 引数には `flag()` で渡した識別子を渡す。
    - フラグが立てられていたら `true` を返す。
    - それ以外は `false` を返す。
- `clear()` でフラグをクリアできる。

### カウンター
```text
RULE sample 1
CLASS Main
METHOD main
IF true
DO createCounter("hoge")
ENDRULE

RULE sample 2
CLASS Main
METHOD increment
IF true
DO traceln("counter = " + incrementCounter("hoge"))
ENDRULE

RULE sample 3
CLASS Main
METHOD decrement
IF true
DO traceln("counter = " + decrementCounter("hoge"))
ENDRULE

RULE sample 4
CLASS Main
METHOD main
AT EXIT
IF true
DO traceln("counter = " + readCounter("hoge"))
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        increment();
        increment();
        decrement();
    }
    
    public static void increment() {}
    public static void decrement() {}
}
```

```text:実行結果
counter = 2
counter = 3
counter = 2
counter = 2
```

- `createCounter()` で、カウンターを作成する。
    - 第一引数で、カウンターを識別するための値を渡す。
    - 第二引数で、カウンターの初期値を渡す（省略した場合は `0`）
- `incrementCounter()` で、カウンターを１インクリメントする。
    - 引数には識別子を渡す。
- `decrementCounter()` で、カウンターを１デクリメントする。
- `readCounter()` で、カウンターの現在の値を取得する。

### タイマー
```text
RULE sample 1
CLASS Main
METHOD main
IF true
DO createTimer("hoge")
ENDRULE

RULE sample 2
CLASS Main
METHOD sleep
AT EXIT
IF true
DO traceln("time = " + getElapsedTimeFromTimer("hoge"));
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        for (int i=1; i<=3; i++) {
            sleep(i);
        }
    }
    
    public static void sleep(int time) throws Exception {
        Thread.sleep(time * 100);
    }
}
```

```text:実行結果
time = 101
time = 301
time = 601
```

- `createTimer()` で、タイマーを作成する。
    - 第一引数にはタイマーを識別するための値を渡す。
- `getElapsedTimeFromTimer()` で、現在の経過時間をミリ秒で取得する。
- `resetTimer()` で時間をリセットできる。

### 再帰的にルールが適用されないようにする
```text
RULE sample 1
CLASS Main
METHOD method
IF true
DO traceln("Byteman Sample!!");
   $this.method()
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        new Main().method();
    }
    
    public void method() {}
}
```

```text:実行結果
※なにも出力されずプログラムが終了しなくなる
```

やや極端だが、ルールの処理のなかでルールの適用対象である `method()` が再度呼びだされている。
この場合、ルールの処理内で呼びだされた `method()` が再びルールの適用対象となり、無限ループが発生する。

これは意図的なものだが、意図せずこのような状態になることも、なきにしもあらず。
（ユーザーガイドでは、 `FileOutputStream` の `open()` メソッドを対象にすると `traceln()` 内で同じメソッドを使っているためこの状態が発生すると説明している）

このような場合に、ルールの処理中で呼びだされたメソッドを対象外にする方法として `setTriggering()` というメソッドが用意されている。

```text
RULE sample 1
CLASS Main
METHOD method
IF true
DO setTriggering(false);
   traceln("Byteman Sample!!");
   $this.method()
ENDRULE
```

```text:実行結果
Byteman Sample!!
```

`setTriggering(false)` とすることで、以降に記述した処理はルール適用の対象外になる。
なお、 `true` を渡せば再び適用対象に戻るが、ルールの処理が終了すれば勝手に `true` に戻るので明示的に `true` を設定する必要はない。

### デバッグ
```text
RULE sample
CLASS Main
METHOD main
IF true
DO debug("debug message")
ENDRULE
```

```test:実行
> bmjava -l sample.btm -Dorg.jboss.byteman.debug Main
Default helper activated
Installed rule using default helper : sample
rule.debug{sample_0} : debug message
```

- `debug()` で、デバッグ用のメッセージ出力ができる。
- デフォルトではメッセージは出力されない。
- メッセージを出力させるには、システムプロパティとして `org.jboss.byteman.debug` を起動時に宣言する必要がある。

### トレース
```text
RULE sample
CLASS Main
METHOD main
IF true
DO traceln("trace message")
ENDRULE
```

```text:実行結果
trace message
```

- `traceln()` で、標準出力にメッセージを出力できる。
    - 改行を入れない `trace()` メソッドもある。
- こちらは何もしなくても常にメッセージが出力される。

#### ファイルに出力する
```text
RULE sample 1
CLASS Main
METHOD main
IF true
DO traceOpen("hoge")
ENDRULE

RULE sample 2
CLASS Main
METHOD method
IF true
DO traceln("hoge", "arg=" + $1)
ENDRULE

RULE sample 3
CLASS Main
METHOD main
AT EXIT
IF true
DO traceClose("hoge")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        method("foo");
        method("bar");
    }
    
    public static void  method(String value) {}
}
```

実行すると、標準出力には何も出力されずカレントディレクトリに `byteman.log` というファイルが出力される。

```text:byteman.log
arg=foo
arg=bar
```

- `traceOpen()` で出力ファイルを開く。
    - 第一引数にファイルを識別するための値を渡す。
    - 第二引数にファイル名を渡す。
- `traceln()` の第一引数にファイルを識別するための値を渡すことで、ファイルに出力できる。
- `traceClose()` で開いていたファイルを閉じる。
- `Helper` クラスの実装を覗いたけど、残念ながら文字コードの指定はできない模様。

### 呼び出し元のメソッド名を検証する
```text
RULE sample
CLASS Main
METHOD method
IF callerEquals("hoge")
DO traceln("Byteman Sample!!")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("call method() from main()");
        method();
        hoge();
    }
    
    public static void hoge() {
        System.out.println("call method() from hoge()");
        method();
    }
    
    public static void method() {
        System.out.println("Main.method()");
    }
}
```

```text:実行結果
call method() from main()
Main.method()
call method() from hoge()
Byteman Sample!!
Main.method()
```

- `callerEquals()` で、呼び出し元のメソッド名を検証できる。
- 呼び出し元のメソッドの名前が、引数で渡した文字列と一致する場合に `true` を返す。
- `callerEquals()` にはオーバーロードされたメソッドが多数用意されており、いろいろな条件で検証ができる模様。
- `callerMaches()` というメソッドもあり、こちらは正規表現で検証ができる。

### スタックトレースを出力する
```text
RULE sample
CLASS Main
METHOD method
IF true
DO traceStack()
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        method();
    }
    
    public static void method() {}
}
```

```text:実行結果
Stack trace for thread main
Main.method(Main.java:-1)
Main.main(Main.java:3)
```

- `traceStack()` で現在のスタックトレースを出力できる。
- `formatStack()` ならスタックトレースを文字列で取得できる。

#### 正規表現で一致するものだけ出力する
```text
RULE sample
CLASS Main
METHOD five
IF true
DO traceStackMatching(".*e$")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        one();
    }
    
    public static void one() {two();}
    public static void two() {three();}
    public static void three() {four();}
    public static void four() {five();}
    public static void five() {}
}
```

```text:実行結果
Stack trace for thread main matching .*e$
Main.five(Main.java:-1)
Main.three(Main.java:8)
Main.one(Main.java:6)
```

- `traceStackMatching()` で、指定した正規表現に一致するメソッド名のスタックだけを出力できる。
- こちらも、 `formatStackMatching()` という文字列を返すメソッドが用意されている。

## ヘルパークラスを自作する
ヘルパークラスは自作することができる。

### 基本
```text
RULE sample
# ★↓ここ
HELPER sample.byteman.MyHelper
CLASS Main
METHOD main
IF true
DO hello()
ENDRULE
```

```java:MyHelper.java
package sample.byteman;

public class MyHelper {
    
    public void hello() {
        System.out.println("MyHelper.hello()");
    }
}
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
    }
}
```

```text:実行結果
MyHelper.hello()
```

- `HELPER` で自作のヘルパークラスを指定できる。
- ヘルパークラスは POJO で実装できる。
- 自作のヘルパーを読み込むと、デフォルトのヘルパークラスは利用できなくなる。
    - なので、 POJO で実装できるとはいっても、事実上デフォルトの `Helper` を継承して作らないと辛い気がする。

### １つのファイルの中で複数のルールに同じヘルパーを利用できるようにする
ルールごとに `HELPER` でヘルパークラスを宣言するのは大変なので、ファイルの先頭で１回だけ宣言する方法も用意されている。

```text
# ★ ここで宣言する
HELPER sample.byteman.MyHelper

RULE rule 1
CLASS Main
METHOD main
IF true
DO println("rule 1")
ENDRULE

RULE rule 2
CLASS Main
METHOD main
IF true
DO println("rule 2")
ENDRULE
```

```java:MyHelper.java
package sample.byteman;

public class MyHelper {
    
    public void println(String message) {
        System.out.println(message);
    }
}
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
    }
}
```

```text:実行結果
rule 1
rule 2
```

ファイルをまたがるのは無理なので、それぞれのファイルの先頭で宣言する必要がある。

### ライフサイクルメソッド
ヘルパークラスにはライフサイクルのイベントごとにコールバックされるメソッドを定義することができる。

```java:MyHelper.java
package sample.byteman;

public class MyHelper {
    
    public void println(String message) {
        System.out.printf("[%10d] %s%n", this.hashCode(), message);
    }
    
    public static void activated() {
        System.out.println("activated");
    }
    
    public static void installed(String ruleName) {
        System.out.println("installed : " + ruleName);
    }
    
    public static void uninstalled(String ruleName) {
        System.out.println("uninstalled : " + ruleName);
    }
    
    public static void deactivated() {
        System.out.println("deactivated");
    }
}
```

```text:rule1.btm
RULE rule 1
HELPER sample.byteman.MyHelper
CLASS Main
METHOD method
IF true
DO println("rule 1")
ENDRULE
```

```text:rule2.btm
RULE rule 2
HELPER sample.byteman.MyHelper
CLASS Main
METHOD method
IF true
DO println("rule 2")
ENDRULE
```

```java:Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        int i = 0;
        while (true) {
            method();
            Thread.sleep(3000);
            System.out.println(++i);
        }
    }
    
    public static void method() {}
}
```

↓動作検証している様子。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/71fd1145-0d9e-c2aa-8e11-52bd9c08f841.gif)

- ヘルパーが最初に読み込まれた時点で `activated()` メソッドがコールバックされる。
- ルールに最初に読み込まれるたびに `installed()` メソッドがコールバックされる。
- ルールがアンインストールされるときに `uninstalled()` メソッドがコールバックされる。
- 全てのルールから利用されなくなった時点で `deactivated()` メソッドがコールバックされる。
- 各メソッドは、 `static` メソッドとして定義する。
- ヘルパークラスのインスタンス自体は、ルールで呼ばれるたびに再作成されている。
    - よって、複数のルールにまたがって値を保存したい場合は `static` 変数で宣言する必要がある。

# +α
<span id="byteman-puzzle">これで終わったら、いつもの記事と変わらなくてつまらないですよね。</span>

せっかくのアドベントカレンダーなので、ちょっと趣向を凝らして Byteman を使ったゲームを考えてみました。

GitHub に上げています。

https://github.com/opengl-8080/byteman-puzzle

## 環境条件
JDK は 8 でお願いします。

一応、 Window と CentOS で動作検証をして動くことは確認してます。

## インストール方法
上記 GitHub のページから `git clone` するか zip を落とすかしてください。

完了したら、プロジェクトのトップディレクトリで以下のコマンドを実行してください。

```text:Windowsの場合
> gradlew.bat installDist
```

```bash:Linuxの場合
$ chmod +x gradlew

$ ./gradlew installDist
```

## 実行
```text:Windowsの場合
> cd build\install\byteman-game\bin

> puzzle.bat 0
```

```bash:Linuxの場合
> cd build/install/byteman-game/bin

> ./puzzle 0
```

ゲームが起動します。
そうしたら、新しくコマンドラインを立ち上げ、今度は `build/install/byteman-game/rules` に移動し、以下のようにコマンドを実行してください。

```text:Windowsの場合
> cd build\install\byteman-game\rules

> install.bat level0\1.btm
```

```bash:Linuxの場合
$ cd build/install/byteman-game/rules

$ chmod +x install uninstall

$ ./install level0/1.btm
```

コマンドを実行すると、最初に起動しておいた方が `SUCCESS` という文字を出力して終了すると思います。

以下は、実際に動かしている様子です。

![byteman.gif](https://qiita-image-store.s3.amazonaws.com/0/28302/a16f2180-16d5-4b91-4117-fc9d4a797774.gif)

## 説明
Byteman を利用したパズルゲームです。

ゲームを起動すると無限ループが始まります。
プレイヤーは、あらかじめ用意された Byteman の設定ファイルを使い、無限ループがうまく抜けられるように設定をインストールし、処理を制御します。

`SUCCESS!!` と出力されて終了すれば成功。
例外がスローされて終了した場合は失敗です。

### レベル
ゲームはレベルで分けられています。

`puzzle` コマンドでゲームを起動するときに、引数でレベルを指定します。

```text:レベル１で起動する
> puzzle.bat 1
```

とりあえず全部で０～４の５つを用意してみました。

以下が、レベルごとに対応する実装クラスです。

| レベル |            対応するクラス             |
|--------|---------------------------------------|
|      0 | `gl8080.byteman.puzzle.level0.Level0` |
|      1 | `gl8080.byteman.puzzle.level1.Level1` |
|      2 | `gl8080.byteman.puzzle.level2.Level2` |
|      3 | `gl8080.byteman.puzzle.level3.Level3` |
|      4 | `gl8080.byteman.puzzle.level4.Level4` |


### 設定ファイル
設定ファイルはレベルごとにあらかじめ用意されたものを `rules` の下に配置しているので、それを使用してください。

また、 `rules` ディレクトリの下には設定ファイルをインストール・アンインストールするためのコマンドを用意していますので、それを利用してください。
（Byteman を別途インストールしていて `bmsubmit` が使えるのであれば、そちらを使っても構いません）

```text:
rules/
  |-install       - Linux 用のインストールコマンド
  |-uninstall     - Linux 用のアンインストールコマンド
  |-install.bat   - Windows 用のインストールコマンド
  |-uninstall.bat - Windows 用のアンインストールコマンド
  |-level0/       - level0 用の設定ファイル
  |-level1/       - level1 用の設定ファイル
  |-level2/       - level2 用の設定ファイル
  |-level3/       - level3 用の設定ファイル
  `-level4/       - level4 用の設定ファイル
```

### 中身の説明

`puzzle` コマンドでゲームを開始すると、 `Main` クラス内で無限ループが開始されます。

```java:Main.java
package gl8080.byteman.puzzle;

...

public class Main {
    ...
    
    public void start() throws IllegalParameterException {
        PuzzleGame game = this.createGame();
        ...
        
        boolean flag = true;
        
        while (flag) {
            flag = game.step(); // ★ game.step() が false を返すまで無限ループ
            this.sleep();
        }
        ...
        System.out.println("SUCCESS!! (time = " + this.calcTime() + ")");
    }

    ...
}
```

この `PuzzleGame` はインターフェースで、このインターフェースを実装したクラスがレベルごとに用意されています。

```java:Level0.java
package gl8080.byteman.puzzle.level0;

import gl8080.byteman.puzzle.PuzzleGame;

public class Level0 implements PuzzleGame {
    
    private boolean flag = true;
    
    @Override
    public boolean step() {
        return this.flag;
    }

    @Override
    public int getLevel() {
        return 0;
    }
}
```

これはレベル０の実装です。
単純にインスタンス変数の `flag` を返しています。

これに対して用意されている Byteman の設定ファイルが以下です。

```text:level0/1.btm
RULE level0-1
CLASS Level0
METHOD step
IF true
DO return false
ENDRULE
```

`Level0` クラスの `step()` メソッドに入ったら、即座に `false` を返却するように実装を書き換える設定になっています。

従って、この設定ファイルをインストールすることで無限ループを終了させることができるわけです。

レベル１以降も、基本的な仕組みは同じです。

各レベルの実装と画面の出力から現在の処理がどう流れているかを考え、用意された設定ファイルにあるルールを適用することでどのように処理が変化するかを想像し、適切な順序でルールのインストールを行っていってください。


以上です、せっかくのアドベントカレンダーなので何か一捻り入れたいなーと思って捻りだした結果がこれです。
なんだか退屈そうなゲームかもしれませんが、楽しんでいただければ幸いです。
（一応解答例を `doc/answer.md` に載せてます）

# 参考
- [Documentation - JBoss Community](http://byteman.jboss.org/documentation)
- [A Byteman Tutorial | JBoss Developer](https://developer.jboss.org/wiki/ABytemanTutorial#top)