[Java] JMS 2.0 Early Draft - Simplified API Sample Code

原文はこちら。
https://blogs.oracle.com/arungupta/entry/jms_2_0_early_draft

Java Message Service (JSR 343) はJava EE 7の一部として改訂される作業が進んでいます。仕様の早期ドラフトは数週間前から利用可能になっており、オンラインのJavaDocも利用できます。その他の仕様(Java EE 7に含まれるJPA 2.1JAX-RS 2.0EJB 3.2JavaServer Faces 2CDI 1.1など)の早期ドラフトもリリースされています。仕様の一部を以下のリンクで詳細に説明してくれています。
このエントリでは、JMS 2.0早期ドラフトのアップデートをお伝えしたいと考えています。

JMS 1.1は2003年12月にリリースされました。その当時からこれまでに、Javaの世界ではたくさんの変更がありました。annotation、generics、auto-closeable、dependency injection、その他たくさんの変化がありました。Java EEプラットフォーム自体が、それ以来広範囲に進化してきましたが、特にJava EE 6は"ゲームチェンジャー"と呼ぶべき存在です。いまなお開発されている複数のJMS実装があり、開発・デプロイの実績もたくさんあります。
そのすべてを維持しつつ、JMS2.0の主な目標は以下のとおりです。
  • 開発を容易にする変更
  • JMSと他のJava EE仕様間の関係の明確化
  • 新しい必須APIを定義し、任意のJMSプロバイダがJava EEアプリケーションサーバと統合可能にする
  • Java EE 7をサポートする拡張
  • コミュニティより要求があったその他の機能強化
このエントリでは早期ドラフトの11.4からコードサンプルを取り出し、JMS 2.0になるとどれほど簡単に開発ができるようになるかを取り上げます。

以下は既存のJMS APIを使ってメッセージを送信する場合のコード例です。
@Resource(lookup = "jms/connectionFactory")
ConnectionFactory connectionFactory;

@Resource(lookup="jms/inboundQueue")
Queue inboundQueue;

public void sendMessageOld (String payload) {
  Connection connection = null;
  try {
    connection = connectionFactory.createConnection();
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageProducer messageProducer = session.createProducer(inboundQueue);
    TextMessage textMessage = session.createTextMessage(payload);
    messageProducer.send(textMessage);
  } catch (JMSException e) {
    // do something
  } finally {
    try {
      if (connection != null)
        connection.close();
    } catch (JMSException e2) {
      // do something else
    }
  }
}
問題なく動作しますが、いくつか問題があります。
  1. メッセージ送信のために複数の中間オブジェクト(ConnectionSessionMessageProducerなど)の作成が必要。こうしたオブジェクトには全て目的があるが、JMSアプリケーションが複雑になってしまう。また、お作法のような決まり切ったコードがたくさん必要になる。
  2. createSessionの引数がわかりづらい。第1引数はセッションがトランザクショナルか否かを指定する。第2引数は、メッセージ受信のセッションの場合、メッセージのACKを返す方法について指定する。これらの2個の引数は独立していないので、第1引数をtrueとする場合、第2引数は無意味になり、第1引数のみが必要。
    その他、このメソッドがEJBに存在する場合トランザクションはコンテナが管理する。Bean-Managed Transactionを使っている場合、トランザクションはJMS APIではなく、UserTransactionで開始し、コミットする。実のところ、EJBの仕様によると、このコードがトランザクション内にある場合、createSessionへの引数は完全に無視されてしまうが、いくつかの引数を指定してコントラクトを満足させる必要がある。
  3. Connectionは明示的にfinallyブロック内でクローズし、サーバ上のリソースを適切にリリースする必要がある。コードにはMessageProducerSessionのクローズがないので、finallyブロックがネストした例外処理で見苦しくなっている。
では、新しいAPIだとどれほどプログラミングモデルを簡単になるか見てみましょう。
@Resource(lookup = "jms/connectionFactory")
ConnectionFactory connectionFactory;

@Resource(lookup="jms/inboundQueue")
Queue inboundQueue;

public void sendMessageNew (String payload) {
  try (JMSContext context = connectionFactory.createContext();){
    context.send(inboundQueue,payload);
  }
}
シンプルでしょう?

大きな変更点は以下の通りです。
  1. 全ての定型コードがなくなっている。その代わりに、ConnectionFactoryを作成し、そこからコンテキストを作成し、コンテキストのsendメソッドを呼び出している。
  2. 送り先(inboundQueue)をMessageProducerではなく、sendメソッドで指定している。
  3. Connectionは自動的にクローズされるので、tryブロックは自動的にクローズする。
  4. 新しいメソッドはランタイム例外を送出するのでコードが非常にすっきりする。
Dependency Injectionを使う場合、コードをもっとシンプルにすることができます。
@Inject
@JMSConnectionFactory("jms/connectionFactory")
private JMSContext context;

@Resource(mappedName = "jms/inboundQueue")
private Queue inboundQueue;

public void sendMessageNew(String payload) {
  context.send(inboundQueue, payload);
}
ここで特記しておくと、Some clean ups to note here are ...
  • アプリケーションによるJMSContextの初期化は不要
  • 同一CDIスコープであれば、injectされた同一のJMSContextを他の場所でも利用可能。
クールでしょ?
次はシンプルになったAPIでメッセージを同期受信する例です。
@Inject
@JMSConnectionFactory("jms/connectionFactory")
private JMSContext context;

@Resource(lookup="jms/inboundQueue")
Queue inboundQueue;

public String receiveMessageNew() {
  JMSConsumer consumer = context.createConsumer(inboundQueue);
  return consumer.receivePayload(String.class);
}
JMS 2.0早期ドラフトの11.4には、標準API(もしくは既存のAPI)とシンプル(もしくは新しい)APIを使ったサンプルがたくさんあります。

JMS 2.0専門家グループが向いている方向を好ましく思っていますか?フィードバックをしたり、貢献したいと思いませんか?

JMS 2.0に関する最新の進捗は以下のリソースから追跡できます。
JMS 2.0を改良し、シンプルかつ使いやすくするために、ユーザメーリングリストに参加頂いて、皆様のお知恵を分けて下さい!

0 件のコメント:

コメントを投稿