Download presentation
Presentation is loading. Please wait.
1
Recoveryアドバイスをもつ アスペクト指向システム
熊原奈津子 石川零 西澤無我 光来健一 千葉滋 (東京工業大学)
2
例外処理の分離の重要性 例外処理とは ロジックとは分離して 書くべき 異常時の処理をまとめて記述したもの 正常時には実行されないコード
Javaにはtry-catchがある 分離はできるが正常時の処理のすぐ下に書かなければならない try { //ファイルへの操作 File file = new File(); file.open(); file.read(); file.write(); : }catch(IOException e){ IOException が発生した 場合の例外処理内容 }
3
後から例外処理を記述したい(1) - 実験プログラム
分散環境で動くサーバの性能をテスト 故障マシンを発見した場合 ログを出力 他のマシンを使って再試行 故障 負荷 命令 コンソール マシンの絵 命令 負荷 コンソールマシン 命令 負荷 制御 プログラム サーバマシン クライアントマシン
4
後から例外処理を記述したい(2) 実験の途中で例外処理を変えたい 例外処理はロジックから離して記述すべき 最初は小規模なので例外処理なし
問題(故障)が起きたら必要に応じて追加 実験データが変だと思ったら例外を無視したせいだった 例外処理はロジックから離して記述すべき 元のプログラムを編集しないで、追加可能にしたい try-catch ロジックの近くに書くので、元のプログラムの編集が必要 データが変だと思ったら、実は故障していた。例外処理がなかったので。。。。 実験プログラムなので適当に(アジャイルに)書いてしまった。
5
元のプログラムを編集 class Sender{
public void sendCommand(String host, String command) throws Exception{ : Socket s = new Socket(host, port); DataOutputStream out = new DataOutputStream(s.getOutputStream()); out.write(command); out.close(); s.close(); : // 続きの処理 } try{ } catch(SocketException e){ // 故障を見つけたらログを出力 } クライアントに ソケットを張る 命令を 送信 複数のホストに 命令を送信 for (int i = 0; i < hostName.length; i++){ new Sender().sendCommand(hostName[i], “./client.sh”); }
6
他のマシンを使って再試行(リカバリ) catch 節の中から try ブロックの先頭へ戻る try{
ホスト故障時には、自動的に復旧処理 try{ Socket s = new Socket(host, port); DataOutputStream out = newDataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); } catch(SocketException e) { ホストを変更して再試行 } Java には再試行を直接実現する 構文はない
7
GluonJR - Recovery アドバイスをもつ AOP System
例外処理をアスペクトとして記述 例外処理を分離 もとのロジックを壊さずに追加・削除が可能 例外処理に特化した pointcut 指定子を提供 Java バイトコード変換で実現 再試行するための特殊メソッド(retry)を用意 アドバイス内で利用可能 リカバリ処理を容易に記述できる
8
アスペクトとして分離 block ポイントカット指定子 recovery アドバイス
ジョインポイントのペアを指定(try ブロック指定に相当) recovery アドバイス catch 節に相当 aspect FileSenderRecovery { pointcut region(): block (call(Socket.new(..)), call(* Socket.close())) && withincode(void Sender.sendCommand(..)); recovery() throwing (SocketException e): region() { host = getAnotherHost(host); retry(); } Socket s = new Socket( host, port); DataOutputStream out = newDataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); アスペクトの単純な例と、適用されるプログラムの例 アスペクトで記述することにより、 もとのロジックを壊さず、例外処理を分離 分離して記述 元のプログラム アスペクト
9
再試行 特殊メソッド retry アドバイスの中で利用可能 block で指定した範囲の先頭に戻る 元のプログラム アスペクト
aspect FileSenderRecovery { pointcut region(): block (call(Socket.new(..)), call(* Socket.close())) && withincode(void Sender.sendCommand(..)); recovery() throwing (SocketException e): region() { host = getAnotherHost(host); retry(); } Socket s = new Socket( host, port); DataOutputStream out = newDataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); アスペクトの単純な例と、適用されるプログラムの例を載せる (先のスライドの例をそのまま載せるのが望ましい) リトライしやすい 元のプログラム アスペクト
10
GluonJR のプログラム例 Pointcut pc = Pointcut.and(new Pointcut()
例外処理を 追加したい 範囲を指定 Pointcut pc = Pointcut.and(new Pointcut() .block(new Pointcut().call("Socket", "<init>"), new Pointcut().call("Socket", "close")), new Pointcut().withincode("Sender", "sendCommand")); RecoveryAdvice ra = new RecoveryAdvice(); ra.setPointcut(pc); ra.setThrowingException(“SocketException", "e"); ra.setBody("e.printStackTrace();"); new Weaver(ra).weave(); アドバイス ウィーブ
11
バイトコード変換 メソッドを実装している バイトコード ユーザから与えられる情報 始点・終点(ソースコード) 処理したい例外の型
例外が生じた場合に 実行したいコード ←始点 クラスファイル block で指定した範囲 ←終点 ・・・ メソッドの情報 ←例外ハンドラの先頭 アドバイス メソッドの属性 retry(); retry() は範囲の始点に戻る Exception Table に含まれる情報 Exception Table ・・・ 例外ハンドラがアクティブとなる バイトコードの範囲(始点・終点) 例外ハンドラがキャッチする 例外のクラス 例外ハンドラの先頭 ・・ 範囲・例外の種類・飛び先
12
実装の要点1: blockで指定する範囲の始点
ジョインポイント・シャドーではない ジョインポイントに直接対応するバイトコード命令ではない Invokevirtual, getfield 等 その命令を含むソースコード行の最初のバイトコード命令 retry() の実現のため アドバイス内から goto で戻っても bytecode verifier をパスする new Socket dup aload_1 iload 4 invokespecial Socket() astore 5 先頭の命令 Socket s = new Socket(host, port); Try ブロックの始点と終点には、スタックが空の状態で実行される命令の直前が選択される。 これは、recovery アドバイス内で記述できる retry() を実現するためである。 GluonJR での retry() は、bytecode 変換により、try ブロックの始点への goto 命令に置き換えられている。 もし try ブロックの始点のスタックの状態が空でなかった場合、 Retry() の直後に、スタック上にオペランドが積まれていることを想定した命令が実行されてしまう。 Retry() の実行後は、スタックの状態は空であるはずなので、 ここでスタックの整合性が保たれず、 Verify error が起きてしまう。 コンパイル JP Shadow
13
× ○ JVMのスタックの状態遷移図 ・・・・・ 例 Socket s = new Socket(host, port); コンパイル
new Socket dup aload_1 iload 4 invokespecial Socket() astore 5 Socket s = new Socket(host, port); コンパイル new dup ・・・・・ int invokespecial str スタックは 空の状態 ref ref ref ref ref × ○ retry() で戻ってくる
14
実装の要点2: 動的なジョインポイントへの対応
if ポイントカット 実行時の式の計算結果で アドバイスの実行を制御 Recovery アドバイスの実装 アドバイス(catch 節)の冒頭で if の式を実行 false なら例外を投げなおす recovery() throwing (IOException e): region() && if(---){ //body of this advice } 変換 try{ : }catch (IOException e){ if (!---){throw e;} //body of this advice } False なら catch 節(アドバイス)を実行しないようにする
15
行アノテーション ジョインポイントの一種 if(・・・){ : }else { } @line(position = "begin")
for(・・; ・・; ・・){ @line(position = "end") ジョインポイントの一種 GluonJRの文法拡張 block による範囲指定に利用 将来指定されそうな場所にジョインポイントがないとき、あらかじめ書いておく プリプロセッサで空のメソッド呼び出しに変換 通常の場合、性能に影響はない 自由に名前がつけられる 自由に名前がつけれる 行あのてーしょんは、ポイントカットできる Pointcut region(): @line(“end”))
16
関連研究 AspectJ handler ポイントカット after throwing アドバイス
既に try-catch 文がプログラムに含まれている場合、 catch 節の実行時をジョインポイントとして選択 after throwing アドバイス 選択されたジョインポイントが例外を投げて異常終了した場合に実行される 例外を補足する範囲の粒度がメソッドボディ Java の throws も同様の問題 リカバリ処理の実装には使えない 最後に必ず例外を投げなければならない Handler は、try-catch が含まれていない場合に、無力 Javaのthrowsを使って、wrapper をしかけても、粒度がメソッド本体
17
まとめ・今後の課題 GluonJRを提案 課題 例外処理をロジックと分離して記述可能 容易にリカバリできる Javaのライブラリとして実装
実装の完成度をあげる 行アノテーションの実装 ポイントカットの実装
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.