BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Java EE6:EJB3.1は、すばらしい進化だ

Java EE6:EJB3.1は、すばらしい進化だ

原文(投稿日:2010/02/15)へのリンク

Enterprise Java Bean 3.0 (EJB 3)の仕様は、エンタプライズでのJavaの長いマーチにおいて、非常に重要な中間地点となった。仕様が、コミュニティからのインプットで作り上げられたのは、非常に明白である。更に一貫性のあるサービスパラダイムを示しており、もっとPOJOフレンドリで、一般的に複雑でなくなっている。Java 5のアノテーションによってもたらされた間接のレベルが、このパラダイムをもっと強力にし、同時に開発者の労力を減らした。悪くて古い決定を捨てて、違う新しい解決を求める意欲によって、そのフレームワークは、以前、EJBを敬遠した人々に興味深いものになった。EJB Entity Beansがなくなり、JPA Entitiesに替った。EJB 2.1やそれ以前では、平均的なbeanに必要なJavaのクラスやinterfaceが手に余るほどあったが、2つぐらいで済むようになった。「規約は設定に勝る(Convention-over-configuration)」ベースのデフォルト値の実現で、起動して、走らせるのが、もっと簡単になった。EJB 3.0は、革命だった。

EJB 3.0が革命なら、EJB 3.1は、非常に使える、かつありがたい進化である。EJB 3.0にあればよかった、と思わせるたくさんのフィーチャがあるのが、EJB 3.1のフィーチャである。仕様のゆっくりしたペースは、許せることである-不完全なモデルをほとんど進化しない仕様に凍結するより、80%の達成率のほうがマシである。もちろん、その完成を歓迎しないわけがない。-これらの全ての新しいフィーチャでさえ-EJB 3.1仕様、すべての後方互換性そして更に加わったもの-で626ページであり、訳10年前のEJB 2.1より14ページ少ない。

この投稿で、これらのフィーチャのいくつかをレビュ-し、その利用法を考える。

EJB 3.1の新フィーチャ

EJB 3.1でのいくつかの大きな変更は、プラットフォームへの追加ではなく、プラットフォームを使う際に、必要な決まりきったことを減らしたことである。柔軟性を高めるために、ユーザインターフェースAPIの表面積を増したものもある。単に、柔軟性をもたらしたものもある。

シングルトン(Singleton)

シングルトン(singleton)は、1つ余分な保証をする、新しいタイプのセッションbeanである:beanは、動いているJVM1つにつき、1つしか生成されない。このフィーチャで、よりよいサービスが可能になるユースケースがたくさんある:例えば、キャシュ。アプリケーションサーバがまだ提供していないリソースへの共通のビューを保証する能力が、別の例である。永続的に保存される必要のない、しかし再生成するとなると高価な、貴重なデータ用の単純な保管場所が、別のオプションである。(わざとらしい)例を見てみよう。Userエンティティがすでに他の場所で(おそらくJPA 2.0を使って)作成されていると、仮定する。

@javax.ejb.Singleton 
public class ChatRoom { 

   private java.util.Map<User,Collection<String>> userComments; 

   @PostConstruct 
   public void setup (){ 
     userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>(); 
     /* ...*/
   }
   public void join(User usr){ /* ...*/ }
   public void disconnect(User usr){  /* ...*/ }
   public void say(String msg){ /* ...*/  }
}

全てのクライアントは、単に、ChatRoomインスタンスへの参照を取得して、同じミュータブルな状態を更新できる。クライアントは、取得の際に、同じインスタンスへのアクセスを保証されている。これは、セッションbeanなので、他のいかなるセッションbeanと同じ保障を提供している:このbeanは、完全にスレッドセーフである。

@javax.ejb.EJB 
private  ChatRoom  chatRoom ; 

 /* ... */

chatRoom.join(myUser) ;
chatRoom.say( "Hello, world!");
chatRoom.disconnect();

シングルトンは、コンカレントなアクセスができるように設計されている。今回の仕様で、開発者は、並列処理に関して精巧な制御ができるようになった。この動きは、デフォルトである。明示的に、コンテナ管理のコンカレンシィ(並行処理)を使うことが宣言できる、そのためには、@javax.ejb.ConcurrencyManagement(CONTAINER)とアノテートする。もしbeanをもっとコントロールしたければ、@javax.ejb.ConcurrencyManagement(BEAN)を使う。コンテナ管理のコンカレンシィは、メソッドレベルあるいはクラスレベルで、アクセスのタイプを明記することが必要である。デフォルトで、以下のように規定してもよい、すべてのビジネスメソッドは、クラスレベルで@javax.ejb.Lock(WRITE)を使って、シリアライズされ、それから、状態の変更による2次的な問題がないように、メソッドは、事実上、"read-only"になるように場合には、最適化する。read-onlyのメソッドは、@Lock(READ)でアノテートする。@Lock(WRITE) でアノテートされたメソッドへの全てのアクセスは、シリアライズされ、終了するまで、あるいは、タイムアウトが起きるまで、クライアントからのアクセスは、ブロックされる。java.util.concurrent.TimeUnit の値をとる@AccessTimeoutアノテーションを使って、タイムアウトの長さを制御することができる。このコンカレンシィ制御を利用するために、ChatRoomの最初の実装コードを変更することができる。

@javax.ejb.Singleton 
@javax.ejb.Lock(WRITE)
public class ChatRoom { 

   private java.util.Map<User,Collection<String>> userComments; 

   @PostConstruct 
   public void setup (){ 
     userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>(); 
     /* ...*/
   }
   public void join(User usr){ /* ...*/ }
   public void disconnect(User usr){  /* ...*/ }
   public void say(String msg){ /* ...*/  }

   @javax.ejb.Lock(READ)
   public int getCountOfActiveUsers(){ /* ... run through the map and count ... */ } 
}

当然、ChatRoomのようなものは、ユーザとポストの数が、アプリケーションサーバのメモリを使い尽くせば、最終的にメモリ枯渇で死んでしまう。デモの目的で、ポストされているChatRoomを検索して、LRUが古いチャットデータを破壊する(あるいは、おそらくJPA と EntityManagerを使って保持し、それから破壊する)定期的なガーベッジコレクタを想像しよう。

EJB Timer

EJB 2.1以来、EJBは、タイマのメカニズムを持っている。しかし、ミリ秒間隔でいつも動くものなので、設定がかなり面倒である。EJB 3.0では、その状況が少し改善されたが、タイマが、本質的に手続き的に宣言され、間隔ベースなことは、変わらないままだった。例えば、もし週の頭に何かをセットアップしたいと思うなら、大変なことだった。EJB 3.1は、Quartz や Fluxのような様々な他のスケジューラをやめて、宣言型で、柔軟性のあるタイマサービスを提供することによって、事態を改善した。CRONのようなスケジューリングを含んで、最も簡単なスケジューリングのニーズには、EJB 3.1は、よいソリューションである。我々のChatRoomに戻ることにする。古いデータを調べて、ガーベッジコレクトするbeanをスケジュールしたいと思う。

@javax.ejb.Singleton 
public class ChatRoom {

   private java.util.Map<User,Collection<String>> userComments;
        
   @javax.ejb.Schedule(minute="1", hour="*") 
   public void cleanOutOldDataFromChatLogs() { 
      /** ... not reprinting all the existing code ... */
   }
}

書いたメソッドは、データを検索し、高速なチェックを行う-必要なら-古い/関係のないチャットログを消去するので、管理可能な状況に保つことができる。ここでは、メソッドが1時間毎に必ず走るような、宣言型のモデルを使った。もちろん、EJB 3.0のTimerServiceを使うこともできる。

Interfaceなしのビュ-

EJB 3.0では、beanは、最低1つのinterface(ローカルあるいは、リモートのビジネスinterface)をサポートする必要があった。Interfaceが、beanのクライアントにとって、beanのビューとして使うことができるからである。interface経由の間接参照は、非常に強力な技法であるが、時々事を複雑にするだけのことがある。EJB 3.1では、interfaceを持たないbeanを書くことができる。その場合、クライアントへのビューは、クラスが見せる公開メソッドである。

@javax.ejb.Stateless 
public class Calculator { 

  public float add ( float a , float b) { 
    return a + b; 
  } 

  public float subtract (float a , float b){ 
    return a - b ; 
  }

} 

このbeanのクライアントは、普通に、単に注入によりbeanを取得し、起動する。:

@javax.ejb.EJB 
private Calculator calculator; 

...
float result = calculator.add( 10 , 10 ) ;
...

非同期サービス

スケーリング問題を扱う最も単純な方法は、(能力が伴なうまで)この問題を扱わないことである!このアプローチは、最も有名には、SEDA(段階的イベントドリブンアーキテクチャ)パターンの特徴があることであり、ボトルネックは、キューイングすることで避ける。このため、依頼した仕事は、キューに入れられ、クライントは、先に進むことができる。もしコンポーネントのダウンストリームに時間がかかり、システムに負荷がかかっている場合、このパターンが、遅いコンポーネントによって、システムがダウンしないことを保証する。

スケーリングに対するもう一つのアプローチは、一方向のメッセージ交換で、クライアントからの呼び出しをブロックしないことである。別のアプローチは、非同期で単純に動き、結果が戻るまで、クライント側での実行を進める。これら全てのアプローチは、EJB 3.1における、サービスの新しい非同期サポートにより実現した。beanクラスや個々のメソッドを@javax.ejb.Asynchronousでアノテートすると、呼び出しの結果が戻るまで、クライアントは、ブロックされるべきでないことをコンテナに言うことになる。これにより、クライアントは、直ちに、続行し、そして-理論的には-コンテナが、作業をバッファして、都合のいい時に実行できる、ようにできる。

もしbeanクラスかビジネスinterfaceが@Asynchronousでアノテートされていれば、beanのすべてのメソッドは、保留される。さもないと、@Asynchronousでアノテートされたメソッドだけが保留される。非同期メソッドは、voidか java.util.concurrent.Future<V>.のインスタンスを返す。クライアントは、後で任意の時に、結果の Future<V>のインスタンスを調べることができるが、呼出しの結果は、直ちに、クライアント側のスレッドで存続し、ブロックしない。このように、EJBが1時間2時間かかろうが、問題ではなく、クライアントは、影響されない。概念的には、これは、JMSキューにリクエストを送る、という単独のジョブでサービスを呼ぶのと同じである。

Future<V>のインスタンスは、ジョブのキャンセルや結果を待つのに使わうこともできる。クライアントにおける処理中のコンテキストは、非同期メソッドには、伝搬しない。その時、 REQUIREDは、実際上、非同期メソッドに対しては、REQUIRES_NEWである。

例をみよう:いくつものwebサービスと通信し、その結果を集めるサービスを作ろうと思う。結果は、欲しいが、クライアントのリクエスト(webページ、たぶん?)を止めたままにできない。そのようなサービスは、次のようになるだろう:

@javax.ejb.Stateless
public CarHotelAndAirLineBookingServiceBean implements CarHotelAndAirLineBookingService  {
  @javax.ejb.Asynchronous  
  public Future<BookingConfirmation> bookCarHotelAndAirLine( Car rental, Hotel hotel, AirLine airLine) { 
    /**  ...  */
   } 
 } 

クライアント側-JSFのアクション-そのようなサービスを次のように呼び出す:

@Named 
public BookingAction { 

        @javax.ejb.EJB  private  CarHotelAndAirLineBookingServiceBean bookingService; 

        private Future<BookingConfirmation> confirmation;

        public String makeRequest(){ 

                Hotel hotelSelection = ... ;
                Car rentalSelection = ... ;
                AirLine airLineSelection = ... ; 

                confirmation = bookingService.bookCarHotelAndAirLine(
                    rentalSelection, hotelSelection, airLineSelection ) ; 
                return "showConfirmationPending";
        }

        public Future<BookingConfirmation> getConfirmation(){ /* ... */ }

        /* ... */
}

簡単になったデプロイ

EJB 3.1は、また.WARファイル内部へのデプロイをサポートする、という劇的に簡単なデプロイのパラダイムを提供する、最初のバージョンの仕様となった。コンポーネント定義のアノテーションを伴なうクラスは、WEB-INF/classesディレクトリ内に、あるいは、WEB-INF/lib.ディレクトリ内の.jarとしてパッケージされた時に、enterprise beanコンポーネントになる。enterprise beanは、またWEB-INF/ejb-jar.xmlファイルを使っても、定義できる。.WARにパッケージされたbeanは、1つのネームスペースを共有し、そして.WARの環境の一部になる。WEB-INF/libディレクトリ内の.jarにパッケージ化するのは、こうして、意味的には、WEB-INF/classesディレクトリ内にクラスを置くのと等価である。

新しい仕様の別の新奇のフィーチャが、EJB Liteである。たくさんのアプリケーションにとって、EJBテクノロジは、必要以上のものである。EJB Liteは、session-beanコンポーネントの使用を中心とした、もっと効率化したサブセットを提供する。これは、EJBコンポーネントを組み込み可能なように使え、ユニットテストも単純にする方法を提供する。EJB Liteは、ステートレス、ステートフルそしてシングルトン セッションbeanをサポートする。Beanは、ローカルなinterfaceあるいは、interface無しのビューを持つことができる。これらは、インターセプターと協働でき、トランザクションやセキュリティのようにコンテナのサービスを使うことができる。

EJB 3.1は、開発者用のツールキットの中の強力なツールであり、明らかに、たいていのアプリケーションの80%のユースケースに合うものに、進化してきた。将来は、この仕様にとって良い方向である。この仕様は、Java SEの削除メカニズムを使って、あるフィーチャを将来の削除の標的にしている、最初のリリースでもある。将来、取り除かれる可能性のある仕様としては、以下のものがある。コンテナ管理による永続性とbean管理による永続性の古い型への3.0以前サポート、エンティティbeanのEJB 2.1仕様のクライアントビュー、EJB QL(EJB 2.1からのクエリ言語)そして、JAX-RPCベースのwebサービスサポート(両方のエンドポイントとクライアントビュー;これらは、J2EE 1.4で加えられた)。

明らかに、EJB 3.1は、実にすばらしい、後方互換なアップグレードであり、5年以上も前のJSR 220 (EJB 3.0)で始まった開発からのすばらしい次のステップを示した。

この記事に星をつける

おすすめ度
スタイル

BT