ステートフルネス(状態管理性)
SpringとEJB 3.0の間のより特徴的な違いの1つが、状態管理に対するアプローチです。状態は多くのアプリケーションにとって重要な要素です。なぜならユーザーとシステムとの相互作用には、通常、累積的な意味を持つ一連の手順が伴うからです。例えば、サンプルの航空券予約アプリケーションの場合、顧客はチケット予約を予約する際に、フライトの閲覧、座席の選択、支払い情報の提示といった各処理の間を行き来することになります。これらのステップを進めていくと、以前のステップで行った選択が会話のコンテキストにおいて暗黙のうちに示されます。
EJBは多くのアプリケーションで重要な役割を担う状態を認識したうえで、ステートフルな会話の管理に重点を置いたStateful Session Bean(SFSB)という1次レベルの構造を提供しています。SFSBを利用すると、あるBean以降の呼び出しはそのBeanのインスタンス(すなわち同じ状態)を共有することになります。また、こうした多くの点の実装は特定のベンダの実装に委ねられているとはいえ、SFSBはスケーラビリティおよび障害迂回の問題も念頭において設計されています。SpringにはSFSBにそのまま相当するものが一切用意されていませんが、SFSBと同じ結果を獲るためのアプローチがいくつか存在します。
ステートフルネス - 機能の比較
状態に対するSpringとEJB 3.0のアプローチを説明するために、チケット予約に必要な手順を考えてみましょう(図3を参照)。
次に示す単体テストは、予約エージェントがステートフルな方法で対話できることを検証します。
public void testPurchaseTicket_UseCase() throws Exception { // Begin purchase ticket conversation FlightCriteria flightCriteria = new FlightCriteria("DFW", "AUS", DateUtilities.parseDate("2006-01-15")); bookingAgent.bookRoundTripTicket(flightCriteria); // Select outbound flight List outboundFlights = bookingAgent.listOutboundFlights(); assertEquals(3, outboundFlights.size()); bookingAgent.selectOutboundFlight(selectFlight(outboundFlights, 1)); // Select return flight List<Flight> returnFlights = bookingAgent.listReturnFlights(); assertEquals(3, returnFlights.size()); bookingAgent.selectReturnFlight(selectFlight(returnFlights, 2)); // Select passenger Passenger passenger = new Passenger("Rod", "Coffin"); bookingAgent.selectPassenger(passenger); // Select seats bookingAgent.selectOutboundSeat(selectSeat(bookingAgent .retrieveCurrentTicket().getOutboundFlight(), "1B")); bookingAgent.selectReturnSeat(selectSeat(bookingAgent .retrieveCurrentTicket().getReturnFlight(), "1A")); // Purchase ticket Ticket ticket = bookingAgent.purchaseTicket(); assertTrue(ticket.getId() != 0); }
SpringとEJB 3.0はどちらもこの単体テストの条件を満たすことができます。Springによる実装を以下に示します。
public class BookingAgentSpring implements BookingAgent { private FlightCriteria outboundCriteria; private FlightCriteria returnCriteria; private Ticket ticket; public Ticket bookRoundTripTicket(FlightCriteria flightCriteria) { outboundCriteria = flightCriteria; returnCriteria = reverseCriteria(flightCriteria); ticket = new Ticket(); ticketDAO.save(ticket); return ticket; } ... }
Springで管理されたBeanに状態を含めるために、インスタンスを呼び出し側の間で共有することはできません。これを行うには、SpringにおいてステートフルBeanのスコープをプロトタイプ(prototype)として設定する必要があります。このことは、BeanがBeanファクトリから取り出されるたびに新しいインスタンスが作成されることを意味します。
<bean id="bookingAgentSpring" scope=”prototype” class="org.jug.flight.booking.spring.BookingAgentSpring"> ... </bean>
次に示すEJB 3.0の実装と比較してみてください。
@Stateful public class BookingAgentEJB implements BookingAgent { private FlightCriteria outboundCriteria; private FlightCriteria returnCriteria; private Ticket ticket; public Ticket bookRoundTripTicket(FlightCriteria flightCriteria) { outboundCriteria = flightCriteria; returnCriteria = reverseCriteria(flightCriteria); ticket = new Ticket(); ticketDAO.save(ticket); return ticket; } ... }
どちらの場合も、予約エージェントは状態を格納して、会話的な方法で呼び出し側とかかわることができます。このように、SpringでもEJB 3.0でもステートフルな動作を生成することは可能ですが、両者のアプローチとその実装は互いに大きく異なっています。
ステートフルネス - 機能以外の比較
上記の例は、SpringとEJB 3.0の双方においてステートフルなオブジェクトをどのように実装できるかを示したものです。どちらも機能的には等価ですが、状態の管理方法に大きな違いがあります。Springによる実装では呼び出し側が完全な予約エージェントを格納していますが(図4を参照)、EJB 3.0による実装では呼び出し側が予約エージェントのプロキシを格納しているだけです(図5を参照)。EJB 3.0バージョンの呼び出し側はプロキシを保持しているだけなので、SFSBとそれに関連する状態はサーバーの内部に存在し、そのサーバーによって管理されます。
SFSBを用いたEJBのアプローチには、いくつかの利点があります。1つは、SFSBをメモリと持続性ストレージとの間で入れ換え可能にすることによってサーバーのパフォーマンスを最適化できるようにSFSBが設計されている点です。これにより、アプリケーションサーバーは最適なパフォーマンスが得られるようにリソースを配分できます。2つ目は、呼び出し側とは別のプロセスでSFSBを実行できる点です。この方法は、アプリケーションの各階層を別々にスケーリングしたり、セキュリティ上の目的でスケーリングするために実施されることがあります。3つ目は、SFSBがトランザクションのロールバックイベントに呼応してBeanの状態をロールバックするために、オプションのSessionSynchronization
インターフェイスも実装できる点です。
SFSBがよく浴びる主な批判の1つは、SFSBの実行やスケーリングがステートレスアーキテクチャと同じようには行えないことです。Springフレームワークは、これを受けて最初からステートレスなフレームワークになっています。しかし、状態が数多くのアプリケーションで重要になっていることを考えると、状態の表現にはいくつかの選択肢が必要です。よくあるアプローチは、状態の保存先としてデータベース、HTTPセッション、または内部のメモリキャッシュを利用するというものです。上記の例では、Springによるステートフルな予約エージェントをこれらのいずれかに格納できるようになっていました。しかし、表4に示すように、これらの選択肢はそれぞれに欠点があります。
SFSBの代替案 | 利点 | 欠点 |
データベース | すべてのアプリケーションサーバーで共有。 | 普通はスケーラビリティが最も低いアプリケーションの階層。2次キャッシュを利用する場合はデータの複製が必要。 |
HTTPセッション | 使いやすい。 | データは複製が必要、トランザクション認識性なし。 |
メモリキャッシュ | すべてのアプリケーションサーバーで共有、潜在的にはデータベース利用よりも効率的。 | データは複製が必要、トランザクション認識性なし。 |
スケーラビリティについてのSFSBに対する批判は、(表4に示したように)SFSBの3つの代替案にもあてはまります。そのうえ、既にSFSBが提供している機能を改めて実装するために、相当な開発の労力をかけなければなりません。アプリケーションにおいて状態に対するアプローチを判断する際には、こうしたトレードオフを真剣に考慮する必要があります。
SpringとEJB 3.0のどちらにも、管理対象Beanのライフサイクルを開発者が操作できるようにフックが用意されています。SpringにはInitializingBeanおよびDisposableBeanの各インターフェイスがあり、これらはSpringによるBeanの初期化終了後および破壊前にBeanへの通知を行うフックを提供します。同じようにEJBコンポーネントでも、作成およびアクティブ化の後と破壊およびパッシブ化の前に必要に応じて通知を受け取ることができます。
最後に、SFSBとその代替案のいずれを使うにしても、どのようにしてBeanの会話状態をトランザクションのステータスと同期させるかという問題を解決しなければなりません。デフォルトでは、トランザクションのロールバック時に、トランザクション内にあるすべてのトランザクショナルリソース(データベースやメッセージングシステムなど)もロールバックされます。しかし、関係するBeanの状態はロールバックされません。こうしたBeanがステートフルであれば、持続性データストアとの同期が失われてしまいます。EJB 3.0の、コンテナに管理されたトランザクションを用いるSFSBでは、トランザクション的なイベントの通知を受け取るためにSessionSynchronization
インターフェイスを実装できます。Springにはこれに相当する機能はありませんが、アプリケーションが個別の方法でこうした同期をとることは可能でしょう。
ステートフルネス - まとめ
SpringとEJB 3.0は、どちらもステートフルなアプリケーションを実現できます。EJB 3.0によるアプリケーションの場合、SFSBの利用がそのための基本的な方法になります。Springはもともとステートレスなアーキテクチャですが、プロトタイプBeanを使ってステートフルネスを助長することができます。このアプローチを利用するアプリケーションでは、パフォーマンスおよびスケーラビリティに対する影響を考慮すると共に、SFSBが対処している問題をそれ以外のやり方で解決する手法がいくつか必要になるでしょう。
機能 | Spring | EJB 3.0 |
ステートフルな構造 | プロトタイプ(インスタンス)Bean | ステートフルセッションBean |
インスタンス管理 | シングルトン、プロトタイプ、要求、セッション、グローバルセッション | ルックアップごとのインスタンス |
ライフサイクル管理 | √(初期化/破壊) | √(作成/破壊、アクティブ化/パッシブ化) |
トランザクション認識性 | -- | √(SessionSynchronizationインターフェイス) |
標準 | 非該当 | √ |
おわりに
本稿では、アプリケーションフレームワークSpringとEJB 3.0仕様について、持続性、トランザクション管理、ステートフルネスのサポートに焦点を当てた比較を行いました。興味深いことに、どちらのテクノロジもこうした問題をうまく解決しており、両者の共通点がこの比較の妥当性を強調しています。
今回はSpringとEJB 3.0との多くの類似点の一部を取り上げて説明しました。その結果明らかになった両者の主な違いが、状態管理、設定の容易性、拡張性でした。
十分にステートフルなアプリケーションに取り組むなら、EJB 3.0のSFSBが優れたソリューションになる得るかどうかを検討する必要があるかもしれません。会話性の高いアプリケーションの場合は、SEAMを検討するのもよいでしょう。SEAMは会話的なやりとりのための強力なソリューションを提供し、SFSBおよびJSF上に構築されています。
Springは、アプリケーション開発の多くの面においてEJBよりも高い柔軟性を提供します。本稿で説明したように、この点は持続性およびトランザクションプロバイダに関しては特によく当てはまります。しかし、柔軟性向上のトレードオフとして、設定がより複雑になっています。EJB 3.0は柔軟性の点では劣りますが、その厳格なテクノロジの積み重ね、注釈ベースの設定、例外による設定の思想により、EJB 3.0アプリケーションの設定は非常にシンプルになっています。事前に定義されたアプローチよりも柔軟性を重視する場合には、間違いなくSpringを検討する必要があるでしょう。
標準化についても少しお話しておきましょう。SpringにはJTA、JDBC、JMSなど多くの標準規格が統合されていますが、Spring自体はJavaの標準規格ではありません。あなたの組織にとって標準化(ひいてはベンダのサポートやツールの存在など)が重要であれば、EJB 3.0を検討するとよいでしょう。
しかし、幸いなことにSpringとEJB 3.0はどちらか一方しか選べないわけではありません。これら2つのテクノロジを組み合わせ、それぞれの強みと弱みをうまく利用する非常に強力な方法があります。本シリーズの第2回(近日公開)では、メッセージング、リモート処理、スケジューリング、依存性管理、仲介(intermediation)などさらに別の特性を調査する予定です。その比較が終わったら、SpringおよびEJB 3.0に関する技術的な意思決定の助けになるような一般的な解説を行います。さらに、それぞれのテクノロジの特徴を活かすために両者を組み合わせる戦略をいくつか紹介する予定です。