第4回 疑似EJB
3.0環境をSpringとXDocletで作る
Page.1 | Page.2 |
|
Springプロキシ機能を利用して EJBサービス・オブジェクトを取得する |
Springフレームワークを利用すると、たとえサービスオブジェクトがEJBとして実装されたものであっても、それがあたかもPOJOであるかのようにクライアント側に見せることができます。しかも、そのためのコーディング作業は一切必要なく、Springフレームワークが提供するLocalStatelessSessionProxyFactoryBeanクラスを利用して、以下のような定義ファイルを用意するだけです。
リスト8 セッション・ビーン取得用Spring定義<bean name="CustomerService" |
この定義のビーンが取得された場合、LocalStatelessSessionProxyFactoryBeanは以下の手続きを実行します。
- JNDI名“java:comp/env/ejb/CustomerService”を用いて、JNDIルックアップ処理を実行し、EJBホームインターフェイスCustomerServiceLocalHomeを取得する(ルックアップ・サービス)
- EJBホームインターフェイスCustomerServiceLocalHomeのcreate()メソッドを呼出し、EJBローカルインターフェイスCustomerServiceLocalを取得する
- EJBローカルインターフェイスCustomerServiceLocalをラップし、指定されたCustomerServiceインターフェイスを実装したプロキシーをランタイムに生成し、そのプロキシーを返す(ビジネス・デレゲート)
これにより、クライアントからは、サービスオブジェクトが単なるPOJOなのか、EJBでラップされたものなのかを意識する必要がないわけです。リッチクライアントのようにEJBをリモートアクセスしなければならない場合は、LocalStatelessSessionProxyFactoryBeanの代わりにSimpleRemoteStatelessSessionProxyFactoryBeanを用いてSpring設定ファイルを定義すれば、リモート・クライアントにおいてもEJBはPOJOとしてアクセスすることができます。
クライアントからEJBを経由して、POJOなサービスオブジェクトにアクセスしたときのランタイム構成は図2のようになります。
図2 Springを利用したEJB 3.0疑似環境の構成 (クリックすると拡大します) |
POJOのサービスオブジェクトにたどり着くまで、非常に多くのクラスが関連していますが、実際にアプリケーションとして開発しなければならないのは、サービス・インターフェイスCustomerServiceと、それを実装したPOJOなサービス・オブジェクトCustomerServiceImplだけであることに注意して下さい。そして、Java EE 5環境では、このPOJOのサービス・オブジェクト部分だけが必要で、それらがそのままJava EE 5環境でも再利用できるのです。
|
EJB 3.0に移行可能なインターセプターを実装する |
次はインターセプターを実装して、サービスオブジェクトに特定の処理を織り込むことを考えます。インターセプターを任意のオブジェクトに織り込む仕組みは、Java EE 5を待たなくてもSpringフレームワークを利用することでその恩恵を得ることができます。しかし、残念なことにSpringとEJB 3.0のインターセプターは、そのAPIが異なっています。
Springフレームワークにおいてインターセプターを実装する場合は、AOP Allianceに基づいたMethodInterceptorインターフェイスを実装し、invoke()メソッドを定義する必要があります。
リスト9 Springにおけるインターセプターの実装例import org.aopalliance.intercept.MethodInterceptor; |
一方、EJB 3.0におけるインターセプターは、POJOとして実装でき、前後処理のロジックを実装するメソッド名も自由な名前が付けられることになっています。
リスト10 EJB 3.0におけるインターセプターの実装例public class MyInterceptor { |
どちらの場合も前処理と後処理の間にターゲットのメソッドを実行するためにproceed()メソッドを呼び出すところは非常によく似ていますが、proceed()メソッドが定義されたコンテキスト・オブジェクトのインターフェイスも異なります。できれば、インターセプターの実装もJava EE 5以降の環境で再利用できるようにしたいものです。そこで、図3に示すようなSpringとEJB 3.0の両方で利用可能なハイブリッド形式でインターセプターを設計するようにします。
図3 Spring、EJB 3.0共用インターセプターの構成 (クリックすると拡大します) |
例えば、ビジネス・メソッドの呼出し引数と戻り値をログ出力するTracerクラスを実現する場合、実際のログ出力のコードはEJB 3.0でも再利用できるTracerクラスに実装し、Spring仕様のTracerInterceptorクラスは、MethodInvocationオブジェクトをInvocationContextオブジェクトに変換し、Tracerをデレゲート呼出しするように実装します。
リスト11 TracerInterceptorの実装例public class TracerInterceptor implements MethodInterceptor { |
インターセプターをサービス・オブジェクトのビジネス・メソッドに織り込むには、以下のようなSpring用のビーン定義を追加します。
リスト12 インターセプターのSpring定義<!-- トレース出力インターセプターのポイントカット定義 --> |
目的のインターセプターをどのクラスのどのメソッドに織り込むかの定義には、ReqexpMethodPointcutAdvisorを利用して、ポイントカット定義を行います。patternプロパティには、<クラス名>.<メソッド名>の形式で評価する正規表現を記述します。このpatternにマッチしたメソッドにのみ対象のインターセプターが織り込まれます。上記の例では、クラス名が“Service”で終るクラスのすべてのメソッドがポイントカットの対象になることを宣言しています。また、PointcutAdvisorを定義する場合には、Spring管理化のビーン取得ときにポイントカットの評価が行われるようにするため、DefaultAdvisorAutoProxyCreatorも宣言しておきます。
上記の定義により、CustomerServiceのメソッドが呼ばれるたびに、ログに以下のようなトレース・ログが現れるようになります。
リスト13 Tracerインターセプターによるログ出力の例[#|2005-08-06T15:03:47.856+0900|INFO|sun-appserver-pe8.1_02|com.example.interceptor.trace.Tracer|_ThreadID=13;|ユーザ[user1]によりメソッド[findCustomer]を実行します:SERVICE=com.example.business.CustomerServiceImpl, PARAMETERS=[{name=, company=}]|#] |
|
Tomcatでもトランザクションや アクセス制御の動作確認をするためには |
これまで述べてきた方法により、サービス・オブジェクトは、EJBを含めたEARアーカイブを作成すれば、フルスペックのアプリケーション・サーバで動作させることができ、またPOJOのままのサービス・オブジェクトを含むWARアーカイブを作成すれば、TomcatなどのWebコンテナ上でも動作せることができます。この特徴を生かすと、開発サイクルを効率的に進めるうえで非常に役に立ちます。例えば、非常に短い周期でアプリケーションを修正しながら動作確認を繰り返す場合には、デプロイのオーバーヘッドが小さいWAR形式を使用し、ある程度実装したユースケースがまとまった段階で、EJBを含めたEAR形式を使用するといった構成の切替が簡単にできるからです。
しかしながら、先の例では、トランザクション管理やアクセス制御の機能にEJBコンテナの機能を前提としていました。
/** |
上記のXDoclet用のメタタグはサービスオブジェクトをEJB化したときにだけ機能し、Tomcat上では同一の振る舞いを再現することはできません。もし、開発環境にJDK 1.5が利用できるなら、サービス・インターフェイスには、Springのインターセプターが認識できるアノテーション形式のメタデータを使用し、サービス・インターフェイスからEJBを生成する際に、アノテーションからXDocletのタグに変換できるように、カスタムDocletを拡張することも考えられます。Springはバージョン1.2から、アノテーション@Transactionalを用いてUserTransactionを制御できるようになっています。一方、アクセス制御の仕組みは現在のところSpringには含まれておらず、SpringのサブプロジェクトAcegiを別途入手する必要があります。実際には、Acegiを設定するのは非常に面倒なので、アクセス制御に関してはEJB 3.0のアノテーション@RolesAllowedを認識するSecurityInterceptorを自作してしまった方が早いでしょう。JDK 1.5のアノテーションを前提とした場合のトランザクションおよびアクセス制御のメタデータ表現は例えば以下のようになります。
リスト14 JDK 1.5のアノテーションを利用する場合 @Transactional(propagation=Propagation.REQUIRED) |
もし、JDK 1.4環境しか利用できない場合でも、JakartaプロジェクトのCommons Attributesを利用することで、JDK 1.5のアノテーションと同等のメタデータをメソッドやクラスに宣言することができます。Commons Attributesを利用した場合のトランザクションおよびアクセス制御定義は以下のようになります。
リスト15 JDK 1.4でCommons Attributesを利用する場合 /** |
ここでは、前者のアノテーションを利用する方針を採用し、EJBとPOJOの両方でトランザクションとアクセス制御の振舞に互換性を持たせることにします。
まず、EJB側の対応として、先ほどのCreateSessionBeanDocletを改良し、サービス・インターフェイスに宣言されたアノテーションを読み込んで、XDocletのタグに変換する機能を加えます。この目的のためにAnnotationUtilクラスを作成し、MethodGeneratorクラスから利用するようにします。
リスト16 アノテーション→XDocletタグ変換の例public class AnnotationUtil { |
上記のように、JDK 1.5からMethodDocやClassDocからAnnotationDescというアノテーションのメタオブジェクトが取得できるようになっているため、これを利用して、XDocletタグへの変換をすればよいことが分かります。
一方、Tomcat上でトランザクションを扱う場合は、Springフレームワークが提供するTransactionInterceptorがサービス・オブジェクトに織り込まれるように、Spring定義ファイルを用意します。
リスト17 Tomcat用Spring TransactionInterceptorの定義例<!-- JTAトランザクション・マネージャ --> |
また、先ほど述べたように、Springにはアクセス制御用のインターセプターがありませので、Tomcat上でアクセス制御が行えるように、新たにSecurityInterceptorクラスを作成します。SecurityInterceptorクラスの実装は、@RolesAllowedに宣言されたロール名について、isUserInRole()メソッドをチェックし、結果がtrueならターゲットのメソッドを実行し、falseならSecurityExceptionをスローするという単純なものです。また、このインターセプターは、Java EE 5環境で再利用する必要はないため、先ほどのようなハイブリッド形式にする必要はありません。
リスト18 Tomcat用SecurityInterceptorの実装例public class SecurityInterceptor implements MethodInterceptor { |
TransactionInterceptorの場合と同様に、Springの設定ファイルを定義してすべてのビジネス・メソッドにSecurityInterceptorが織り込まれるように設定します。
リスト19 Tomcat用SecurityInterceptorの定義例<!-- アクセス制御のインターセプター --> |
以上で、EJBを使う場合と使わない場合それぞれについてランタイム環境の準備が整いましたので、この2つの環境を切替える方法を考えます。EJBを含むEARファイルとPOJOのままのWARファイルを切替えるためには、それぞれで必要なSpring定義ファイルを分割し、ファイル単位で使用と未使用を切替えるとよいでしょう。
リスト17とリスト19のインターセプターの定義はTomcatの場合にしか使いませんので、web-only-context.xmlという名前のファイルでまとめておきます。一方、リスト8のEJB取得用の定義はEJBを使用する場合にしか使いませんので、ejb-local-context.xmlという名前のファイルにまとめておきます。Springは分割された複数の設定ファイルを読み込んでマージしてくれますので、antコマンドでWARファイルを作成する場合に、それぞれに必要な定義ファイルを取捨選択すればよいことになります。
リスト20 AntビルドファイルにおけるWARファイル作成の定義例<target name="war" depends="compile" description="WARファイルを生成します"> |
以上、Springフレームワークを利用して、EJB 3.0と同じようにビジネス・ロジックをPOJOで実現する手法について説明しました。POJOで実現されたコンポーネントは長期的にはJava EE 5への移行を容易にするだけでなく、TomcatなどのWebコンテナを用いて簡単に動作確認することもできます。実際の開発プロジェクトでは、ここで紹介した一部の手法だけを採用すれば十分かもしれません。あるいは、もっと開発効率を追求できる可能性があれば、この手法をさらに拡張してみることも検討してみてください。
次回は、本連載の最終回となるHibernateを利用したインテグレーション層の開発手法についての解説です。
2/2 |
INDEX |
||
第4回 疑似EJB 3.0環境をSpringとXDocletで準備する | ||
Page1 サービス・オブジェクトをPOJOでデザインする POJOのサービスオブジェクトをEJBでラップする カスタムDocletでEJBを自動生成する |
||
Page2 Springプロキシー機能を利用してEJBサービスオブジェクトを取得する EJB 3.0に移行可能なインターセプターを実装する Tomcatでもトランザクションやアクセス制御の動作確認をするためには |
Java Solution全記事一覧 |
- 実運用の障害対応時間比較に見る、ログ管理基盤の効果 (2017/5/9)
ログ基盤の構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。今回は、実案件を事例とし、ログ管理基盤の有用性を、障害対応時間比較も交えて紹介 - Chatwork、LINE、Netflixが進めるリアクティブシステムとは何か (2017/4/27)
「リアクティブ」に関連する幾つかの用語について解説し、リアクティブシステムを実現するためのライブラリを紹介します - Fluentd+Elasticsearch+Kibanaで作るログ基盤の概要と構築方法 (2017/4/6)
ログ基盤を実現するFluentd+Elasticsearch+Kibanaについて、構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。初回は、ログ基盤の構築、利用方法について - プログラミングとビルド、Androidアプリ開発、Javaの基礎知識 (2017/4/3)
初心者が、Java言語を使ったAndroidのスマホアプリ開発を通じてプログラミングとは何かを学ぶ連載。初回は、プログラミングとビルド、Androidアプリ開発、Javaに関する基礎知識を解説する。
|
|