[WLS, Java] New EJB 3.2 feature - Modernized JCA-based Message-Driven Bean

原文はこちら。
https://blogs.oracle.com/WebLogicServer/entry/new_ejb_3_2_feature

WebLogic Server 12.2.1は、Java EE7仕様に完全に互換性をもつ実装です。このリリースのWebLogic ServerにおけるEJBコンテナでの大きな改善点の一つに、MDB(メッセージ駆動型Bean)がメソッド無しでリスナーインターフェースを実装できる点があります。このようなno-methodリスナーインタフェースを使用する場合、すべてのstaticではないBeanクラス(およびjava.lang.Object以外のBeanクラスのスーパークラス)のパブリックメソッドは、メッセージ・リスナー・メソッドとして公開されます。
それでは、ステップバイステップでサンプルを開発してみましょう。e-Commerceのwebサイトは、製品が売買されると、その売買イベントをJMS Queue、buyQueueとsellQueueそれぞれに送信するものとします。コネクタは、キューをリスニングし、MDBの非静的パブリック・メソッドを実行して、永続ストアにイベントのレコードを書き込みます。

1. Define a no-methods message listener interface

今回のサンプルでは、メッセージ・リスナー・インターフェースのNoMethodsListenerIntfにはメソッドがありません。
List 1 - No-methods message listener interface
public NoMethodsListenerIntf {
}

2. Now define the bean class

MDBのクラスには、productBoughtとproductSoldという、二つの非静的パブリックメソッドがあります。そのため、両方ともメッセージリスナーメソッドとして公開されています。コネクタがsellQueueから製品販売イベントを取得すると、MDBのproductSoldを呼び出します。製品購入イベントでも同様です。コネクタが実行すべきターゲット・メソッドであることを示すよう、productSoldメソッドとproductBoughtメソッドに対して@EventMonitorで注釈を付けます。これらの2つのメソッドは、データベースまたは他の永続ストアにレコードを永続化します。
ほかにも非静的パブリック・メソッドを定義することができますが、どれををコネクタが実行するのかはコネクタ次第です。
List 2 - Message-Driven Bean
@MessageDriven(activationConfig = {
  @ActivationConfigProperty(propertyName = "resourceAdapterJndiName", propertyValue = "eis/TradeEventConnector")
})

public class TradeEventProcessingMDB implements NoMethodsListenerIntf {
  @EventMonitor(type = "Retailer")
  public void productSold(long retailerUUID, long productId) {
    System.out.println("Retailer [" + retailerUUID + "], product [" + productId + "] has been sold!");
    // persist to database
  }

  @EventMonitor(type = "Customer")
  public void productBought(long customerId, long productId) {
    System.out.println("Customer [" + customerId + "] has bought product [" + productId + "]!");
    // persist to database
  }
}
EventMonitor注釈を以下のように設定します。
List 3 - EventMonitor annotation
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EventMonitor {
  public String type();
}
このMDBをWebLogic Serverにデプロイする際、EJBコンテナはこのMDBがEJB 3.2互換のMDBであることを検知します。resourceAdapterJndiNameの値を指定し忘れていると、WebLogic Serverが適切なコネクタリソースを見つけようとします。例えば、(現在のアプリケーションスコープ、もしくはサーバ全体のグローバルスコープでアクセス可能なコネクタのうち)同じno-methodメッセージリスナーインターフェースのサポートを宣言しているコネクタを見つけようとします。
適切なコネクタが見つかり、MDBと紐付けられた場合、コネクタはBeanクラス定義を読み取り、分析することができます。

3. Developing a connector that is used to associate with message-driven bean

コネクタアプリケーションでは、MessageEndpointFactoryのgetEndpointClass()メソッドを使ってBeanクラス定義を取得し、@EventMonitorで注釈が付いている場合にはすべてのメソッドを検査します。その後、javax.jms.MessageListenerをBeanクラスのターゲットメソッドを使って作成し、イベント・キューで待機します。
List 4 - trade event connector
@Connector(
    description = "This is a sample resource adapter",
    eisType = "Trade Event Connector",
    vendorName = "Oracle WLS",
    version = "1.0")

public class TradeEventConnector implements ResourceAdapter, Serializable {
  // jms related resources
  ......

  private static final String CALLBACK_METHOD_TYPE_RETAILER = "Retailer";
  private static final String CALLBACK_METHOD_TYPE_CUSTOMER = "Customer";

  @Override
  public void endpointActivation(MessageEndpointFactory mef, ActivationSpec activationSpec)
      throws ResourceException {
    try {
      Class<?> beanClass = mef.getEndpointClass(); // retrieve bean class definition
      ......
      jmsContextForSellingEvent = ...; // create jms context
      jmsContextForBuyingEvent = ...;
      jmsConsumerForSellingEvent = jmsContextForSellingEvent.createConsumer(sellingEventQueue);
      jmsConsumerForBuyingEvent = jmsContextForBuyingEvent.createConsumer(buyingEventQueue);

      jmsConsumerForSellingEvent.setMessageListener(createTradeEventListener(mef, beanClass, CALLBACK_METHOD_TYPE_RETAILER));
      jmsConsumerForBuyingEvent.setMessageListener(createTradeEventListener(mef, beanClass, CALLBACK_METHOD_TYPE_CUSTOMER));

      jmsContextForSellingEvent.start();
      jmsContextForBuyingEvent.start();
    } catch (Exception e) {
      throw new ResourceException(e);
    }
  }

  private MessageListener createTradeEventListener(MessageEndpointFactory mef, Class<?> beanClass, String callbackType) {
    for (Method m : beanClass.getMethods()) {
      if (m.isAnnotationPresent(EventMonitor.class)) {
        EventMonitor eventMonitorAnno = m.getAnnotation(EventMonitor.class);
        if (callbackType.equals(eventMonitorAnno.type())) {
          return new JmsMessageEventListener(mef, m);
        }
      }
    }
    return null;
  }

  @Override
  public void endpointDeactivation(MessageEndpointFactory mef, ActivationSpec spec) {
    // deactivate connector
  }
  ......
}
関連づけられたコネクタのアクティベーション・スペックを以下のように定義します。
List 5 - the activation spec
@Activation(
    messageListeners = {NoMethodsListenerIntf.class}
  )
public class TradeEventSpec implements ActivationSpec, Serializable {
  ......
}

4. Developing a message listener to listen on the event queue.

メッセージリスナのonMessage()が呼び出されると、MessageEndpointFactoryを使ってメッセージエンドポイントを作成し、このメッセージエンドポイントのターゲットメソッドを呼び出します。
List 6 - jms message listener
public class JmsMessageEventListener implements MessageListener {
  private MessageEndpointFactory endpointFactory;
  private Method targetMethod;

  public JmsMessageEventListener(MessageEndpointFactory mef, Method executeTargetMethod) {
    this.endpointFactory = mef;
    this.targetMethod = executeTargetMethod;
  }

  @Override
  public void onMessage(Message message) {
    MessageEndpoint endpoint = null;
    String msgText = null;
    try {
      if (message instanceof TextMessage) {
        msgText = ((TextMessage) message).getText();
      } else {
        msgText = message.toString();
      }
      long uid = Long.parseLong(msgText.substring(0, msgText.indexOf(",")));
      long pid = Long.parseLong(msgText.substring(msgText.indexOf(",") + 1));

      endpoint = endpointFactory.createEndpoint(null);
      endpoint.beforeDelivery(targetMethod);
      targetMethod.invoke(endpoint, new Object[]{uid, pid});
      endpoint.afterDelivery();
    } catch (Exception e) {
      // log exception
      System.err.println("Error when processing message: " + e.getMessage());
    } finally {
      if (endpoint != null) {
        endpoint.release();
      }
    }
  }
}

5. Verify the application

イベントの構文は、カンマで区切られた2個の数字、例えば328365,87265で構成されているものとします。前者の数字は、顧客や小売店のIDで、後者の数字は、製品IDです。
そのようなイベントをイベントキューに送信すると、MDBがそのイベントを永続化することがわかるでしょう。We assume that the syntax of the event is composed

0 件のコメント:

コメントを投稿