Recoveryアドバイスをもつ アスペクト指向システム

アスペクト指向言語による
例外処理の記述方法の改善
数理・計算科学専攻 千葉研究室
熊原奈津子
指導教員:千葉滋
例外処理の分離の重要性
例外処理とは
異常時の処理をまとめて
記述したもの
正常時には実行されない
コード
ロジックとは分離して
書くべき
Chiba Shigeru Group
try {
//ファイルへの操作
File file = new File();
file.open();
file.read();
file.write();
:
} catch(IOException e){
IOException が発生した
Javaにはtry-catchがある
場合の例外処理内容
分離はできるが正常時の
}
処理のすぐ下に書かなけ
ればならない
修士論文発表会
2
頻繁に例外処理を差し替えたい
- 実験プログラム
Chiba Shigeru Group
分散環境で動くサーバの性能をテスト
最初は小規模なので例外処理なし
問題(故障)が起きたら必要に応じて追加
故障
命令
コンソール
マシンの絵
命令
負荷
コンソールマシン
制御
プログラム
命令
負荷
サーバマシン
クライアントマシン
修士論文発表会
3
元のプログラムを編集
Chiba Shigeru Group
class Sender{
public void sendCommand(String host, String command) throws Exception{
try{ :
クライアントに
Socket s = new Socket(host, port);
ソケットを張る
DataOutputStream out
= new DataOutputStream(s.getOutputStream());
out.write(command);
命令を
out.close();
送信
s.close();
} catch(SocketException e){
// 故障を見つけたらログを出力
}
System.out.println(command + “ has been sent to ” + host);
}
複数のホストに
for (int i = 0; i < hostName.length; i++){
new Sender().sendCommand(hostName[i], “./client.sh”);
}
修士論文発表会
命令を送信
4
Chiba Shigeru Group
他のマシンを使って再試行(リカバリ)
catch 節の中から try ブロックの先頭へ戻る
ホスト故障時には、自動的に復旧処理
try{
Socket s = new Socket(host, port);
DataOutputStream out = new DataOutputStream(
s.getOutputStream());
out.write(command);
out.close();
Java には再試行を直接実現する
s.close();
構文はない
} catch(SocketException e) {
ホストを変更して再試行
}
修士論文発表会
5
GluonJ/Rの提案
Chiba Shigeru Group
- Recover アドバイスをもつ AOP System
例外処理をアスペクトとして記述
例外処理を分離
もとのロジックを壊さずに追加・削除が可能
例外処理に特化した pointcut 指定子を提供
Java バイトコード変換で実現
再試行するための特殊メソッド(retry)を用意
アドバイス内で利用可能
リカバリ処理を容易に記述できる
修士論文発表会
6
Chiba Shigeru Group
GluonJ/Rの特徴:アスペクトとして分離
block ポイントカット指定子
範囲をポイントカットできる
ジョインポイントのペアを指定(try ブロック指定に相当)
recover アドバイス
catch 節に相当
Socket s = new Socket(
host, port);
DataOutputStream out
= newDataOutputStream(
s.getOutputStream());
out.write(command);
out.close();
s.close();
System.out.println(“done”);
元のプログラム
分離して
記述
@Glue class SenderRecovery {
@Recover(
etype = “SocketException”,
advice = “{
$1 = getAnotherHost();
GluonJR.retry();
}”)
Pointcut p = Pcd
.block(Pcd.call(“Socket#new(..)”),
Pcd.call(“PrintStream#plintln()”));}
修士論文発表会
7
アスペクト
Chiba Shigeru Group
GluonJ/R のプログラム例
@Glue class SenderRecovery {
@Refine
static class Diff extends Sender{
public String getAnotherHost(){
// 他のホスト名を返す
}
}
@Recover( etype = “SocketException”,
advice = “{ $1 = getAnotherHost();
アドバイス
GluonJR.retry(); }”)
Pointcut p = Pcd.block(
例外処理を
Pcd.call(“Socket#new(..)”),
追加したい
Pcd.call(“PrintStream#plintln()”)) 範囲を指定
8
修士論文発表会
}
Chiba Shigeru Group
GluonJ/Rの特徴:再試行
特殊メソッド GluonJR.retry()
アドバイスの中で利用可能
block で指定した範囲の先頭に戻る
Socket s = new Socket(host, port);
DataOutputStream out
= newDataOutputStream(
s.getOutputStream());
out.write(command);
out.close();
s.close();
System.out.println(“done”);
元のプログラム
@Glue class SenderRecovery {
@Recover(
etype = “SocketException”,
advice = “{
$1 = getAnotherHost();
GluonJR.retry();
}”)
Pointcut p = Pcd
.block(Pcd.call(“Socket#new(..)”),
Pcd.call(“PrintStream#println()”));}
修士論文発表会
9
アスペクト
Chiba Shigeru Group
行アノテーション
if(・・・){
:
} else {
:
}
@Line(begin)
for(・・; ・・; ・・){
:
}
@Line(end)
:
ジョインポイントの一種
GluonJ/Rの文法拡張
block による範囲指定に利用
将来指定されそうな場所にジョ
インポイントがないとき、あらかじ
め書いておく
自由に名前が
自由に名前
がつけれる
つけられる
Pointcut p =
Pcd.block(Pcd.line(“begin”),
Pcd.line(“end”));
修士論文発表会
10
GluonJ/R の実装
Chiba Shigeru Group
GluonJ 1.3 を拡張して実装
拡張はGluonJに対するアスペクトのみで記述
GluonJ 自体は全く変更していない
プラグイン同様、追加削除が容易
GluonJ の総コード数5000行に対して
拡張部分は400行程度
バイトコード変換には Javassist を利用
行アノテーションのプリプロセッサを用意
修士論文発表会
11
例外ハンドラの追加
メソッドを実装している
バイトコード
クラスファイル
block で指定した範囲
・
・
・
メソッドの情報
メソッドの属性
・
・
・
Chiba Shigeru Group
ユーザから与えられる情報
1. 始点・終点(ソースコード)
←始点
2. 処理したい例外の型
3. 例外が生じた場合に
←終点
実行したいコード
←例外ハンドラの先頭
アドバイス
Exception Table
範囲・例外の種類・飛び先
・
・
修士論文発表会
Exception Table に含まれる情報
1. 例外ハンドラがアクティブとなる
バイトコードの範囲(始点・終点)
2. 例外ハンドラがキャッチする
例外のクラス
3. 例外ハンドラの先頭
12
Chiba Shigeru Group
GluonJR.retry() の実装
1. GluonJ/R 内でソースコードをコンパイルし、
バイトコードへ変換
2. invokestatic 命令を命令長が同じ3バイトの
goto 命令に置換
メソッドの
バイトコード
←始点
block
範囲の
始点に戻る
アドバイス
:
GluonJR.retry();
コンパイル
:
:
invokestatic
:
修士論文発表会
変換
:
goto
:
13
Chiba Shigeru Group
範囲を選択するアルゴリズム
Pointcut p = Pcd.block(
Pcd.call(“foo(..)”),
Pcd.call(“bar(..)”));
始点候補
foo();
:
foo();
:
hoge();
bar();
:
bar();
block
終点候補
始点・終点が同一メソッド内
に存在
最も近いペア(始点・終点)
を選択
始点となるソースコードが
実行される直前から終点とな
るソースコードが実行される
直前までを選択
修士論文発表会
14
実装の要点:
blockで指定する範囲の始点
Chiba Shigeru Group
ジョインポイント・シャドーではない
ジョインポイントに直接対応するバイトコード命令ではない
Invokevirtual, getfield 等
その命令を含むソースコード行の最初のバイトコード命令
Socket s = new Socket(host, port);
コンパイル
retry() の実現のため
new Socket
dup
aload_1
iload 4
invokespecial Socket()
astore 5
先頭の命令
JP Shadow
アドバイス内から goto で戻っても bytecode verifier をパスする
修士論文発表会
15
Chiba Shigeru Group
JVMのスタックの状態遷移図
例
Socket s = new
Socket(host, port);
new
コンパイル
・・・・・
dup
ref
new Socket
dup
aload_1
iload 4
invokespecial Socket()
astore 5
int
str
ref
ref
ref
ref
○
修士論文発表会
invokespecial
×
スタックは
空の状態
retry() で戻ってくる
16
Chiba Shigeru Group
行アノテーションのプリプロセッサ
プリプロセッサで空の
スタティックメソッド呼び
出しに変換
プログラムの挙動には
影響なし
性能に対する影響に
ついては後述
class LineAnnotation{
public static void begin(){}
public static void end(){}
}
修士論文発表会
if(・・・){
:
} else {
:
}
@Line(begin)
LineAnnotation.begin();
for(・・; ・・; ・・){
:
}
@Line(end)
LineAnnotation.end();
:
17
現在の実装の限界
Chiba Shigeru Group
異なるブロックをまたぐ範囲の選択
メソッドをまたぐ範囲は選択できない
for、while 文等のブロックをまたいでも選択可能
finally 節の扱い
選択範囲外の finally 節が範囲に含まれてしまう可
能性がある(コンパイラによる)
Recover アドバイス内で例外を投げると、元の
finally 節が無視される
修士論文発表会
18
Chiba Shigeru Group
finally 節に関する問題
例1
try {
:
例外発生
} catch (){
:
} finally {
:
}
アドバイス
追加した方の優先順位が
高いため finally 節が
実行されない
例2 try {
:
if (・・){
return;
}
:
} catch (){
:
} finally {
(finally 節)
}
展開された finally 節も
含んでしまう
修士論文発表会
例3 try {
:
:
アドバイス
throw e;
} catch (){
:
} finally {
:
}
finally 節が実行されない
19
実験について
Chiba Shigeru Group
実験環境
CPU : Intel Pentium 4 CPU 2.8GHz
メモリ : 1GB
OS : Microsoft Windows XP Professional SP2
JVM バージョン : 1.5.0_06
目的
GluonJ/Rで例外処理をアスペクトとして追加した
場合のオーバーヘッドを測定
修士論文発表会
20
Chiba Shigeru Group
実験:try-catch文との実行速度の比較
public class Test {
public void m() throws Exception{
try{
a();
} catch(Exception e){}
b();
}
public void a() throws Exception{}
public void b() {}
}
メソッド m() を10億回
try-catch文を追加
アスペクトをウィーブ
実行時間(秒)
例外処理なし
2.6
try-catch文
17.0
GluonJ/R
17.0
@Glue class InsertTryCatch {
@Recover(etype = "java.lang.Exception",
advice = "")
Pointcut p = Pcd.block(Pcd.call("test.Test#a(..)"),
Pcd.call("test.Test#b(..)"));
}
修士論文発表会
21
Chiba Shigeru Group
実験:行アノテーションの性能実験
public class Test {
public void m() throws Exception{
@Line(begin)
a();
@Line(end)
b();
}
public void a() throws Exception{}
public void b() {}
}
実行時間(秒)
行アノテーションなし
2.6
行アノテーション追加
(例外処理なし)
2.6
行アノテーション追加
(例外処理追加)
17.1
@Glue class InsertTryCatch {
@Recover(etype = "java.lang.Exception",
advice = "")
Pointcut p = Pcd.block(Pcd.line("begin"),
Pcd.line("end"));
}
修士論文発表会
22
AspectJ との比較
Chiba Shigeru Group
handler ポイントカット
既に try-catch 文がプログラムに含まれている場合、
catch 節の実行時をジョインポイントとして選択
after throwing アドバイス
選択されたジョインポイントが例外を投げて異常終
了した場合に実行される
例外を補足する範囲の粒度がメソッドボディ
Java の throws も同様の問題
リカバリ処理の実装には使えない
最後に必ず例外を投げなければならない
修士論文発表会
23
Chiba Shigeru Group
関連研究
Eiffel や Ruby の retry 機構
Java には存在しなかった
範囲を選択するポイントカット
ループ処理のためのジョインポイント
[Harbulot ら ‘06]
for 文等のブロック全体をポイントカット
ポイントカットできないループも数多く存在
block ポイントカットと行アノテーションは
任意の範囲を選択可能
修士論文発表会
24
まとめ・今後の課題
Chiba Shigeru Group
GluonJ/R
例外処理をロジックと分離して記述可能
範囲をポイントカットできる pointcut 指定子
容易にリカバリできる
アドバイスの中で再試行可能なメソッド
オーバーヘッドはほとんどなし
今後の課題
現実のアプリケーションに適用
うまく記述できるか
コードサイズをどのくらい減らすことができるか
修士論文発表会
25