[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[jfriends-ml 1616] Re: AspectJ 小サンプル (was 読書会)
秋元@PFUです
akky wrote:
> > ---本書とは直接関係ないがその他の話題----
> > *AspectJをつかって任意の場所で例外を出すテストをする話
> > 秋元さんがまとめて報告してくれる予定。
>
> ご指名にあずかりました。
>
> 任意の場所で例外を出すやつは、まだまとめきれてません。
まとめたものを、末尾につけます。前回のとアスペクト部分はほぼ
同じようなものなんですが。
もともと、あるパターンの名前を持つメソッド群のところで、
NullPointerExceptionを無理矢理発生させるテスト、というのを
やりたくなったんですが、元のソースに手は入れたくないし、手
作業でやることじゃないよなぁ、と悩んでいたわけです。
読書会でぽろっと話したところ、高橋徹さんにAspectJを薦めら
れまして、使ってみるとあっという間に解決しました。
--
Akky (AKIMOTO, Hiroki)
2nd Development Dept. Software-Product Div. PFU Ltd. Tokyo
/*
AspectJの使い方の一例を示すサンプル
元々の課題 (ちょっと長いです。すいません)
1. 発生したRuntimeExceptionを拾っていない(==VMまであげてしまう)アプリ
ケーションがある。ソースはあるので、RuntimeExceptionが起こったら自前
でハンドルするようにしてほしい。
2. RuntimeExceptionを拾うように各所を改造した。さてこの改造のテストは
どう行う?
3. アプリケーションの各所で、人為的にRuntimeExceptionを起こせばよい。
でも、ソースの各所を手で書き換えるのは良案ではないな。
という状況
このアスペクトで行っていること
「actionPerformedという名前を持つメソッドが呼ばれたら、NullPointerException
を発生させる。ただし一つのactionPerformedに対し一回だけ」
以下はWindowsの場合で示すので、他の環境の人は適宜読み替えてください
[JDKをインストール]
例: c:\jdk1.3へ
set JAVA_HOME=c:\jdk1.3
[AspectJを展開] (ver0.8が出たみたいですが、まだそっちは試してません)
(http://aspectj.org/servlets/AJSiteのdownloadからjarファイルを入手)
> d:
> cd \
> jar xvf jaraspectj07beta12-tools.jar
[環境変数]
> set ASPECTJ_HOME=d:\aspectj0.7
> set ASPECTJC="%ASPECTJ_HOME%\bin\ajc"
> set ASPECTJ_LIB="%ASPECTJ_HOME%\lib\aspectjrt.jar"
[AspectJでコンパイル]
(適当なGUIアプリのソースを用意する。例えば、JDKのdemo/jfc/SimpleExample)
> %ASPECTJC% -classpath %ASPECTJ_LIB% *.java
(*.javaには、対象のアプリケーションのソースと、自分で書いたアスペクト
を指定する)
[実行]
> %JAVA_HOME%\bin\java -classpath %ASPECTJ_LIB% Test
[動作確認]
メソッド名が actionPerformed であるようなメソッドが呼ばれる操作を
行うと(よくあるGUIアプリケーションの場合、画面の適当な操作で起こる
でしょう)、その一回目に、NullPointerExceptionがthrowされる。
[参考]
AspectJ http://aspectj.org/
「梅澤 真史のOOPSLA’2000レポート」
http://www.ogis-ri.co.jp/otc/hiroba/Report/oopsla/oopsla.html
AspectJを紹介してくださった高橋徹さんのウェブページ
http://homepage2.nifty.com/torutk/
*/
/**
* 特定箇所でNullPointerExceptionを1回だけ起こすアスペクト
*
* @author AKIMOTO, Hiroki
*/
aspect NpeInvokerAspect of eachobject(instanceof(*)) {
/**
* receptionで示されるクラス-メソッド条件にあったポイントを、
* interruptingPointという名前で切り出す
*/
pointcut interruptingPoint(): receptions(* actionPerformed(..));
/**
* 介入を行うジョインポイント
* interruptingPointで切り出されたメソッドの処理後に実施される
* ただし、そのポイントについての記録が既にある場合は、何もしない
*/
after(): interruptingPoint() {
synchronized (TextDb.class) {
boolean isFirstTime =
TextDb.recordIfFirstTime(thisJoinPoint.toString());
if (isFirstTime) {
throw new NullPointerException(thisJoinPoint.toString());
}
}
}
}
/**
* 出現文字列を記録するクラス
*
* 既出文字列かどうかを判定する
* ファイルを使って、既出文字列リストをpersistentにする
*
* 記録毎にファイルを開閉したり、StringキーでHashSetに格納したりと、手抜きをして
るので、
* このままテストコード以外に流用してはいけない
*
*/
class TextDb {
private static java.util.HashSet joinPointSet = new java.util.HashSet();
private static final String DB_FILE_NAME = "db.txt";
/**
* テキストDBの内容をオンメモリに引っ張ってくる
*/
static {
java.io.BufferedReader reader = null;
try {
String line;
reader
= new java.io.BufferedReader(
new java.io.FileReader(DB_FILE_NAME)
);
while ((line = reader.readLine()) != null) {
joinPointSet.add(line);
}
} catch (java.io.FileNotFoundException ex) {
// ファイルがなければ、なしで良い
} catch (java.io.IOException ex) {
ex.printStackTrace();
} finally {
try {
if (reader != null) { reader.close(); }
} catch (java.io.IOException ex) {
// closeでの失敗は無視
} finally {
reader = null;
}
}
}
/**
* パラメータlineの内容が、既知であれば追加登録する。
* オンメモリのSetに加えると同時に、ファイルも更新している
*
* @return true if it was the first time
*/
public static boolean recordIfFirstTime(String line) {
if (joinPointSet.contains(line)) {
return false; // already recorded
}
joinPointSet.add(line);
java.io.BufferedWriter writer = null;
try {
writer
= new java.io.BufferedWriter(
new java.io.FileWriter(DB_FILE_NAME, /* append mode */true)
);
writer.write(line);
writer.newLine();
} catch (java.io.IOException ex) {
ex.printStackTrace();
} finally {
try {
if (writer != null) { writer.close(); }
} catch (java.io.IOException ex) {
// closeでの失敗は無視
} finally {
writer = null;
}
}
return true;
}
}