[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;
  }
}