chofutaroメモ

ソフトウェアエンジニアによるソフトウェアエンジニアのためのメモ書き

仮想環境を使い始めました

Debianを比較的サクサク動かせる無料の仮想化環境としてVirtualBoxを使い始めました。VirtualPCVMWareと使ってみましたが、もうVirtualBox以外考えられません。

なぜ仮想化環境を使うか?

これまであまり仮想化環境には興味がありませんでした。用途が分かっていなかったためだと思います。しかし一旦使い始めるとその可能性の大きさに驚いています。既にお使いの方には”何をいまさら”だと思いますが、そうでもない方もいると思うので、仮想化環境初心者の私から見て”これだから止められない”というものを数点挙げておきます。

以下、仮想化環境=VirtualBoxです。

実マシンを何台も準備する必要が無い

仮想化環境が実用に耐えなかった時代は、サーバ=Solaris*1&RedHad Linux、クライアント=WindowsXP〜Windows7という開発環境を構築する場合、それぞれマシンを準備して環境構築していたと思います。仮想化環境の場合は物理的なマシンは不要です。VirtualBoxの場合はホストオンリー・ネットワーク・アダプタという仮想ネットワークがあるため、ネットワークさえも実物を準備する必要がありません。クライアント・サーバ・システムや複数のコンピュータからなるシステムを構築する場合は重宝するはずです。

仮想とは言えゲストOSは必要なだけメモリを使用しますから、ホストOSにはそれを上回るメモリが搭載されている必要があります。最近のメモリはずいぶん安価なのでメモリ容量はあまり問題にならないでしょう。問題になるとすればホストOSのメモリ空間サイズです。32ビットWindowsの場合は4GB弱が限界だったはずなので、仮想化環境に割り当てられるメモリサイズは限られます。64ビットOSがお薦めです。

お気に入り環境・状態を何台でも複製できる/やり直せる

VirtualBoxはゲストOSに与えるHDDに対いて”可変長ディスク”という方式を割り当てることができます。これは実際に使用している領域分しかホストOSのHDDを使わないという方式です。つまり、ゲストOSインストール直後のHDD使用量はせいぜい数GBだと思います。250GBのHDDを搭載した仮想化環境であってもVirtualBoxであれば、インストール直後はホストOSのHDDのうち数GBしか使用しません。

この方式のおかげでインストール直後の仮想化環境をバックアップしてもLinuxであれば2〜3GB程度です。Windowsであればもうちょっと必要ですがDVDやBDに保存しておくことができます。ちなみにVirtualBoxでは仮想化環境のバックアップイメージのことを”アプライアンス”と呼びます。

VirtualBoxアプライアンスのインポート機能を使ってバックアップしていた仮想化環境を再現することができます。いろいろ試した後インストール直後の状態に戻すことも、テストが終わったらまっさらなテスト環境で始めることも簡単にできてしまいます。アプライアンスを戻すのはちょっと時間がかかりますがそれでも10分程度です。

必要なもの

VirtualBoxを使う際に必要なものはハイスペックなPCだけです。同時実行するゲストOS(CPU)の数だけのコア(スレッド)数を持つプロセッサと、ゲストOS全てに必要なメモリを割り当てられるだけのメモリ容量があれば十分です。一般的な用途ではそれ以外は必要無いでしょう。

後はOracle VirtualBoxのサイトからVirtualBoxをダウンロードしてインストールするだけです。VirtualBoxGNU General Public License (GPL) Version2でライセンスされているようです。いわゆるフリーソフトウェアですので無料で使わせていただくことが可能です。

所感

WindowsやLinuxの開発ではVirtualBoxがとても有効であると考えられます。特にいろんな環境をターゲットとされている方々には欠かせないツールとなるのではないでしょうか。

*1:Solarisの場合はIntel CPU版ならばいいのですがSPARC CPUならばQEMU等を使わないと目的を達成できません。

setterの書き方ではまりました

Scalaのプロパティ構文、特にsetterの書き方ではまりました。

Javaは使いたいけどプロパティ構文が使えないことと、最近よく効く関数型プログラミング言語に興味があってScalaを勉強し始めました。当然のごとく、プロパティ構文を確認することから始めたのですが、いきなりsetterの書き方で躓いてしまいました。

プロパティ構文*1setterの書き方

以下のようにコーディングではエラー*2になります。A Scala Tutorialのコード例をそのままタイプしたつもりなのですがコンパイルエラーです。

class Employee {
  private var name_ :String = _
  def name: String = name_  
  def name _= (aName: String) {
    name_ = aName
  }
}

keisukenのたわ言で教えてもらったのですが、setterはメンバ変数名に"_="を付けた名前になるそうです。”name_=”が名称ということでしょうか。

修正結果は以下の通りです。

  def name_= (aName: String) {  // nameと_=の間の空白を削除した。

ちなみに、Scalaのプロパティ構文ではgetter必須で省略できないようです。

Scalaでは'_'(アンダースコア)を使うと'+'という文字が使える

私の手元にある入門書にはエラーの原因を探る情報がなかったため、少しネットを調べ回ったところ、おもしろい文法を見つけることができました。それは、Scalaはアンダースコア('_')を使えば'+'等演算子に使う文字が使えるという点です。

例えば、

private var t_++= = 1;

のように、"t_++"という変数名が使えるんですね。

*1:Scalaでは統一アクセスとか統一アクセスの原則というらしい。

*2:eclipse上では"no value: value aName”

cofojaを使ってみました

DbC(Design by Contract)をJavaで実現できるcofojaを使ってみました。

セットアップ

cofojaのページから辿れる”Using Contracts with Eclipse”のとおりにセットアップすれば良いと思うのですが、私の環境では少しだけそのままではダメな点がありました。念のためセットアップ一連のステップを記載しておきます。

ビルドパスにcofojaに必要なライブラリを追加する

プロジェクト・プロパティの中でJavaのビルド・パスにcofoja-1.0-r126.jarとasm-all-3.3.1.jarを追加する。

プロジェクト固有のアノテーション設定を有効にする

プロジェクト・プロパティの中のJavaコンパイラー/アノテーション処理*1で、プロジェクト固有の設定を可能にチェックする。さらにアノテーション処理を使用可能にするにチェック、エディターでの処理を使用可能にするにチェックして、以下のプロセッサ・オプションを設定する。

アノテーション処理のファクトリ・パスを追加する

プロジェクト・プロパティの中のJavaコンパイラ/アノテーション処理/ファクトリ・パスにて、cofoja-1.0-r126.jar絶対パスを指定する。

これでcofojaをエディタ上で使えるようになっているはずです。

VM引数にcofoja-1.0-r126.jarを指定する

VM引数として、-javaagent:<絶対パス>\cofoja-1.0-r126.jar

これでcofojaを実行時に使えるようになっているはずです。

所感

以前([id:hatenadiary:20110319])、Contract4J5を使ってみましたが、エディタ上の支援がある点、セットアップが楽な点、エラーメッセージが簡潔である点を勘案するとcofojaに部があるような気がします。

*1:pleadesの日本語訳は”注釈処理”ですが

eclipseでHibernateを使ってみました

O/Rマッピングの王道と思われるHibernateを使ってみました。

セットアップ

MySQLをセットアップする

私の開発環境にはMySQLが無かったため、ローカルPCにMySQL5をインストールしました。インストールは、MySQL初心者入門講座を参考にさせていただきました。結果から言うと、このページに書かれてあるとおりセットアップして問題ありませんでした。

  • データベースのタイプ : Multifunctional Database
  • Decision Support(DSS)/OLAP
  • Manual Selected Default Character Set /Collation : データベースの文字セットはShift-JIS

サンプルプログラム用のユーザを登録する際、チュートリアルに書いてある通りにユーザを作成しようとしてもエラーでダメでした。

ERROR 1133 (42000): Can't find any matching row inthe user table

どうやらWindowsではパスワードを設定してgrantしなければならないようで、

grant all privileges on hibernatetutorial hibernate@localhost identified by 'hibernate';

とすれば成功しました。

Hibernate tutorial with Eclipseに従ってサンプルを実行

ほとんどチュートリアルどおりで良いのですが、私の環境では3点ほど問題がありましたので列挙しておきます。

hibernate.cfg.xmlの修整

eclipseにはHibernateのプラグインを入れてあるので、hibernate.cfg.xmlは自動で作成されます。ただ、自動で作成されたhibernate.cfg.xmlhibernate.connection.driver_classがcom.mysql.jdbc.Driverでは無かったので、念のため手で修整しておきました。

加えて自動ではcurrent_session_context_class=threadが作成されないようなので、これは直接入力しました。

slf4jでエラーが発生

配布されていたHibernate Core 3.6.6.Final Releaseにはslf4j-api-1.6.1.jarが含まれているのですがこれでは''ダメ''で、slf4jを別途ダウンロードしてその中に含まれているslf4j-api-1.5.2.jarを使いました。

SessionFactoryの生成でなぞのエラー

サンプルプログラムのSessionFactoryUtilクラスの中のConfiguration().configure().buildSessionFactory()を呼び出すところで、

Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: javax/persistence/EntityListeners

とエラーになりました。
原因はわからないのですが、参照ライブラリーにHibernate配布ファイルにあるhibernate-jpa-2.0-api-1.0.0.Final.jarを加えたら解決しました。

所感

オブジェクト指向DBMSという言葉が出始めた頃から、RDBMSの取り扱いを特別扱いしなくて良い方法はないものか考えていました。所謂インピーダンス・ミスマッチという問題です。JavaだけでなくC#でも今主流のプログラミング言語でデータベースを扱う場合、O/Rマッピングが必要になります。一手間必要ということです。

小さなプログラムでデータベースを使う場合、直接SQLを駆使してJDBC経由でデータベースを使えばいいのでしょうが、仕事で開発するシステムの場合そう簡単ではないことが多いのではないでしょうか。テーブル数やカラムが多いため、データベースチームとかDAOチームというデータベースを扱う専門の開発要員を配置することが多いと思われます。

今回使用してみたHibernateO/Rマッピングを簡単にするツールであることは感じ取れました。RDBMSのことが判っていなければならないことに変わりはないのですが、面倒な作業のうちかなりの部分をHibernateがやってくれます。Java Bean、Java Beanとテーブルのマッピングを自分で作成する必要はありますが、コードを自動生成*1する仕組みを併用すればかなり簡単にO/Rマッピングができそうです。

*1:例えばComponentAA/Adjuster

例外について考えてみました

例外について考えてみました。例外を文法として持つ言語を使って開発する場合、例外をどのように取り扱うかがいつも議論になっているように感じるため、自分の考えを改めて整理しておいた方が良いと感じました。

最初に

私が師匠と思っている人に”Javaを復習するにはどんな本がよいか”という観点で「Java: The Good Parts」という本を薦めていただきました。まだ読み始めなのですが私のニーズにぴったりです。

Java: The Good Parts

Java: The Good Parts

この本の著者はチェック例外について言及していますが、私は、Java言語のチェック例外機構そのものに問題はないと感じています*1
話が発散しているかもしれませんが、例外を考える上で重要なのは例外に対する設計思想だと思います。システム設計の中で例外(的な事象*2)をどう取り扱うのかしっかり方針を立て、その方針に基づき大勢のプログラマの意思統一をどう図るのか、というのが大切なんだと思います。

結論

理由は後で記す*3として、まず結論を列挙しておきます。

  • 検査例外は必要(使う)。そして設計によって例外クラス全体の継承構造を管理する*4
  • 検査例外の最上位クラスがExceptionクラスにならないよう、これに替わる例外クラス(ここではControlledExceptionと仮定します)を、開発に携わる人全員に共通したクラスとして定義する。
  • ソフトウェアの要求機能・性能として、エラー*5の回復を義務づけられているケースでは、ControlledExceptionを使う。それ以外は、RuntimeException及びその派生クラスを使う。
  • エラーが発生したパッケージ内でエラーの処置ができない場合、パッケージの公開インタフェースとしてControlledExceptionを使い、当該パッケージ内部では、必ずしもControlledExceptionを使わなくとも良い。

結論に至る根拠

検査例外は不要だという人たちの根拠

トレンドとして、どちらかと言うと”検査例外は不要”という話が多いように見受けられます。そこで、その説を支持している人たちの根拠を整理してみました。

  • 検査例外を使うと、Versioningの問題が顕著になる : 部品クラスにおいて、従来無かった検査例外を新たにスローするようコードを変更した場合、呼び出し側のクラスにおいてもコードの変更が必要になる。
  • 検査例外を使うと、Scalablityの問題あるいは例外連鎖が顕著になる : 検査例外を使用すると、例外をメソッドはそのメソッドの抽象化レベルにあわせて投げる必要があり、メソッドの実装方法の詳細に合わせた例外を使えない。仮に使うことを許してしまうと、Versioningの問題が顕在化し、メソッドの内部仕様を公開 し、抽象化のレベルに応じて例外クラスの数が増える、ことになる。
  • 検査例外を使うと、RuntimeExceptionの飲み込みが顕著になる : 例外を受け取る側では、詳細な例外が何であれ、例外的な事象に対する処置はひとつであることが多です。このことから例外的な事象に対して処置する側は、Exceptionでキャッチ(Exception派生例外を一網打尽にできる)したくなる(これは心情として避けられないという仮定)。こうなると、ExceptionクラスはRuntimeExceptionクラスの親でもあるので、バグ(RuntimeException例外)もキャッチしてしまう。
  • 検査例外を使うと、例外対処のコードを局所化ができない : 検査例外を使用すると、例外をスローしたメソッドから、例外を処理できるメソッドに至る途中のメソッド全てにおいて、例外に対して何もできないにもかかわらず、例外をキャッチして(何もせず)リスローする無駄なコードを書かなければなければならない。
検査例外は必要だという人たちの根拠
  • 検査例外は、例外仕様の文書化を強制する : コンパイル時に例外の仕様を検出することができるため、文書化されていない例外を見逃すことがない。クラスの使用法として、どのメソッドでどういう例外がスローされるのか判らなければ、どうエラー処理して良いのか判らない、あるいは必要ないエラー処理を書くことになる。
両者の意見を参考にして

検査例外を不要だという人たちの根拠は、設計によって軽減されるか無くすことができるように感じます。設計によって検査例外を管理すれば、挙げられている問題はかなり軽減すると考えられます。

優秀な開発者ばかりが集まった開発では設計によって検査例外を管理できそうですが、大規模な開発になればなるほど、優秀とは言えない人が増えていくわけですから、検査例外の管理ができなくなる虞があります。おそらく、検査例外は不要と主張されている方々は『いくら設計で管理していても、コーディング時には、それを無視して誤った使い方をする者が出てくる*6』と考えていらっしゃるのではないでしょうか*7

これらの説を集めている際、同様の指摘をされているウェブページがありました。参考までに挙げておきます。

developerWorks Japan Javaの理論と実践: 例外をめぐる議論

私の意見としては適切に例外を使うのは確かに難しく、また間違った使い方をされる例は多いものの、彼に同意する人たちは間違った理由から同意していると思います。ちょうど政治家が、誰でもチョコレートがもらえるような補助を主張すると10歳の子供から絶大な支持を受けるのと似た話のように思います。

開発者としては「面倒なこと(検査例外を使用する開発)は避けたい」と思ってしまうのですが、それは怠慢のような気がしています。管理者として「しっかり設計・コーディングして検査例外を管理しよう」と言いたくなりましたし、それを目指したいものです。

最後に

先述した本の3章では例外が触れられていますが、著者が、

Javaの例外ほど、みんなから悪口を言われて誤解されている機能はないだろう

と言われています。その通り、今でもJavaの検査例外に関する議論は止むことがないようです。

今回私は、Javaを使った開発において例外をどのように取り扱うべきか再考してみたのですが、その中で感じたことがあります。それは、検査例外だけでなく、エラー処理をどうするかというのは、開発において重要なポイントだということです。これは、止むことが無くて当然です。どのような設計方針とすべきか、いつも答えが違うし、いつも議論しているような気がします。

ただ今回、それで良いんだと感じました。

*1:メリットを活かせるなら使った方が良い。存在が悪ではない、という考えは間違っていないと思います。

*2:バグに起因する障害を含む

*3:長くなるため。

*4:Exceptionクラスを使わせない

*5:呼び出し元には例外のスローによって知らされた事象

*6:つまりコーディング以降管理不能に陥る

*7:個人的にはよく判ります

Contract4j5を使ってみた

DbC(Design by Contract)をJavaで実現できるContract4j5を使ってみました。

セットアップ

セットアップ方法が記されているページ(Getting Started with Contract4J)を参考にセットアップしました。

AspectJ Projectを新規作成する

[ファイル]-[新規]-[AspectJ Project]でプロジェクトを新規作成します。

contract4j5.jarをライブラリに追加する

プロジェクトのビルドパスに[外部jar追加]ボタンを押して、contract4j5.jarを追加します。

AspectJビルドの設定にcontract4j5.jar他ライブラリを追加する

プロジェクトのAspectJビルドパス-[Inpath]に[外部jar追加]ボタンを押して、contract4j5.jarを追加します。また、Contract4j5_080.zipに含まれていたlibディレクトリ下のjarファイルを全てビルドパスに追加します。

ソースを作成します。

パッケージは何でも良いと思うのですがsampleとします。ソースは以下のようにコーディングしました。Contract4J5を使わず普通のJavaアプリケーションとして実行すると”30"と表示されるプログラムです。

public class Sample {
	public static void main(String[] args) {
		Person person = new Person("Taro YAMADA", 20);
		int future = 10;
		System.out.println(person.getFutureAge(future));
	}
}
import org.contract4j5.contract.*;	/* Contract4J5を使う */

@Contract	/* DbC対象のクラス */
public class Person {
	private String name;
	@Invar("age >= 0")	/* 年齢は0未満になることがない */
	private int age;
	
	Person() {
		name = new String("-");
		age = -1;
	}
	
	Person(String aName, int aAge) {
		name = aName;
		age = aAge;
	}

	@Pre("aFuture >= 0")
	@Post("$return >= 0")
	int getFutureAge(int aFuture) {
		return age + aFuture;
	}
	
	@Post("$return != null")
	String getName() {
		return name;
	}
}
実行してみる

普通のJavaアプリケーションとして実行すると、想定通り"30"とだけ表示されます。

次にわざとPersonクラスを間違った使い方をしてみます。main()のPersonオブジェクトを生成する際、第2引数を20ではなく-1にして実行してみます。

Person person = new Person("Taro YAMADA", -1);

すると、以下のようなエラーが表示されました。ばっちり事前条件の違反(クラスの使い方が間違っている)を検出してました。

[FATAL] DefaultContractEnforcer: *** Contract Failure (Person.java:16): Invar test "age >= 0" for "sample.Person" failed. [failure cause = null]
Exception in thread "main" org.contract4j5.errors.ContractError: *** Contract Failure (Person.java:16): Invar test "age >= 0" for "sample.Person" failed. [failure cause = null]
at org.contract4j5.enforcer.ContractEnforcerHelper.makeContractError(ContractEnforcerHelper.java:183)
at org.contract4j5.enforcer.defaultimpl.DefaultContractEnforcer.finishFailureHandling(DefaultContractEnforcer.java:29)
at org.contract4j5.enforcer.ContractEnforcerHelper.handleFailure(ContractEnforcerHelper.java:110)
at org.contract4j5.enforcer.ContractEnforcerHelper.invokeTest(ContractEnforcerHelper.java:92)
at org.contract4j5.aspects.InvariantFieldCtorConditions$InvariantFieldCtorConditionsPerCtor.ajc$afterReturning$org_contract4j5_aspects_InvariantFieldCtorConditions$InvariantFieldCtorConditionsPerCtor$2$d2878b90(InvariantFieldCtorConditions.aj:143)
at sample.Person.(Person.java:19)
at sample.Sample.main(Sample.java:9)

所感

JavaDbCの実現方法を探していました。今回の実験でContract4J5を使うと事前条件/不変条件/事後条件がコーディングできそうなことが判り、とてもウキウキしています。

ところで、「なぜ事前条件がチェックできることがうれしいのか?」と感じられる方がいるかもしれません。その人のために”DbCを盛り込めるContract4J5のようなライブラリを使えるのは幸せなことなんだ”ということを自分なりに訴えてみます。

自分一人でプログラムを作る、小さいプログラムを作る、というケースではDbCのありがたみを感じられるものではないかもしれません。しかし、私のようにプログラミングを職業としている場合は事情が異なります。

職業でプログラミングをする場合、大勢でひとつのプログラムをコーディングします。その際、汎用的なクラス(以降部品クラスと呼びます)は共有ライブラリとして予め作成しておきます。部品クラスを作る人は、部品クラスの使用法を設計しjavadocを含むドキュメントに記します。部品クラスを使う人は、設計書やjavadocに書かれている使用法を見ながらコーディングします。

ドキュメントがきちんと書かれていれば、ドキュメント通りに部品クラスが作られていれば、ドキュメント通りに共通部品を使っていれば、Contract4J5は不要かもしれません。しかし、実際の現場ではそうならないケースが多です。よっぽどの天才集団で無い限り、開発の中では必ず先述した条件のいずれかが欠けた状態になります。

DbCソースコードに盛り込める場合、実際にシステムを動かした後にバグを見つける手間を大幅に短縮することができます。部品クラスを利用するクラスをコーディングしている時、あるいは部品クラスの単体テスト時に判るようになります。つまり、ドキュメントに書いているとおりに共通部品が作られているか、ドキュメント通りに共通部品を使っているかということを早期に(否が応でも)確認できます。

仕事でプログラムを作成する場合は、なるべく早く/なるべく安く/目指す品質のプログラムを作る必要があり、物作りの速度というのがこれらに直結しています。このような環境下では、Contract4J5のように早期にバグ*1を見つる仕組みを用いることにより、コーディングしている人が矢早く仕事を切り上げられ、会社としても人件費が抑えられ、仕事としてうまくいく可能性を上げられるのです。

さて、JavaDbCを実現するのはContract4J5だけではないようです。Googleからもcofojaというライブラリがリリースされるようです。私は未だcofojaを使えていませんが、こちらもなかなか良さそうです。いずれ再チャレンジしてみようと思っています。

*1:ドキュメントが間違っている。ドキュメント通りに部品クラスを使っていない。ドキュメント通りに共通クラスが作られていない、というケースです。

AspectJを使ってみる

AspectJを使ってみました。Javaを使った仕事にはAOPが欠かせないだろうという予想があったためです。

  • JDK6u24、NetBeans7.0β2
  • aspectj-1.6.10.jar

Toru TAKAHASHIさんのAspectJのウェブページの中にAspectJをどう使っていくかというページがあるのですが、そこに書かれていることは、仕事でJavaを使う場合のケースとして的を射ていると思いました。私はコンパイル時に(静的に)ウィービングすることだけをイメージしていたのですが、実行時にウィービングすることもできるんですね。これは活用の幅が広がりそうです。

AOPに関する用語は、AOPの用語にておさらいしておきました*1

NetBeans7.0β2/6.9.1用のAspectJをサポートするプラグインはないようだ

私が主に使っている開発ツールはNetBeansです。昨今の開発でIDEは欠かせないと考えているためで、自己啓発のために自宅でも使っています。
今回、AspectJNetBeans用プラグインをインストールしようとしましたが、NB7.0β2とは合わないためインストールできませんでした。NB6.9.1でもダメでした。

NetBeansAspectJをサポートしていたのはずっと昔のようで、今はプラグインが存在しないようです。

build.xmlを書き換えてコンパイルする

プラグインによる開発のサポートは諦めましたが、NetBeans主体でAspectJを使えないものか調査してみました。そこで判ったことは、build.xmlを書き換える手法をとっている方が多いということでした。
私もKdeveloper/Howto: AspectJ for Netbeansに記されている方法を真似てやってみたところjarファイルを作成することができました。これでなんとかウィービングまではNetBeansを使って開発できそうです。
build.xmlに追加したコードを載せておきます。

<!-- Add AspectJ tasks to Ant -->
    <!-- 出典:http://kdeveloper.com/aspectj-for-netbeans-howto/ -->

    <!-- aspectjtools.jarを格納するパスをaspectj.runtime.libに定義しました -->
    <taskdef classpath="${aspectj.runtime.lib}/aspectjtools.jar"
        resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties"/>

    <!-- Add an AspectJ build target -->
    <target name="aspectj">
        <echo level="info">--- aspectj (start) ---</echo>
        <iajc
            destdir="${build.classes.dir}"
            source="${javac.source}"
            target="${javac.target}"
            classpath="${javac.classpath}:${j2ee.platform.classpath}"
            deprecation="${javac.deprecation}"
            encoding="${source.encoding}"
            debug="${javac.debug}">
            <inpath>
                <pathelement location="${build.classes.dir}" />
            </inpath>
        </iajc>
    </target>

    <!-- Add the AspectJ target to your general project build -->
    <target name="-post-compile" depends="aspectj"></target>
</project>
runターゲットではウィービングされない

NetBeansを使って、ソースコードからウィービングされたクラスファイルを作成できるようになるまでに、私が大きく勘違いしていたことを報告しておきます。
勘違いしていたこと(判っていなかったこと)と言うのは、runターゲットが実行される前に、強制的にcompile(相当の)ターゲットが実行される(?)ということです。先にコンパイルしていれば、その時にできたファイルを使ってくれるものだと思っていました。
最初、Hello Worldをコーディングしたら即実行できると考えていたのですが、何度やっても実行結果がウィービングされたものになりません。
そこで初心に返り、以下に示すようなコマンドを手動で実行してみました。

java -cp %CLASSPATH%;build/classes メインクラス名
※CLASSPATHにはaspectjtools.jarを指定してあります。

結果、ウィービングされています。
どうやら、一度でもNetBeansで”主プロジェクトを実行”するとウィービングされていないクラスファイルになるようです。実行時に再コンパイルするのでしょうか、まだ仕組みはよく判っていません。現時点私は、Antの文法もろくに判っていないため今後の課題とします。

今回アノテーション方式を使いました

ちなみに、この時使ったアスペクトは以下のようになっています。Toru TAKAHASHIさんのAspectJにあるHello Worldをそのまま使わせていただきました。
アノテーション方式で記述してあります。その理由は単純で、NetBeansのエディタは、AspectJ言語で記述されたソースファイルをサポートしていないためです。

@Aspect
public class Trace {

    @Before("call (void HelloAspectJWorld.sayHello())")
    public void beforeAtSayHello() {
        System.out.println("[Before sayHello]");
    }

    @After("call (void HelloAspectJWorld.sayHello())")
    public void afterAtSayHello() {
        System.out.println("[After sayHello]");
    }
}

サンプルコード

所感

私の周りにはデバッグ用プリント文やデバッグのためのログを主体にデバッグする人が少なからずいます。この点の善し悪しはさておき、製品コードに(一時的にでも)デバッグ文を記載することは避けるべきである、という点は間違いないと思います。
AOP(特に実行時ウィービング)があればこの問題を解消できそうです。少なくとも、製品コードからトレース目的のデバッグ文を除去できます。製品コードに手を加えず、デバッグ文を組み込んだり抜いたりできるのはありがたい仕組みです。
また、今回は試してみませんでしたが、本来のAOP的活用として、フレームワークのバグやDB周りの例外をアプリケーション層とは全く別の場所(軸)で一括してキャッチできる、というのも魅力です。異常系処理は、AOPを使ってアプリケーション処理とは別の軸で一括処理することができれば、アプリケーション層を開発する人の手間がずいぶん減るのではないかと希望が膨らみます。

*1:とても判りやすかったです。