原文はこちら。
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の非静的パブリック・メソッドを実行して、永続ストアにイベントのレコードを書き込みます。
ほかにも非静的パブリック・メソッドを定義することができますが、どれををコネクタが実行するのかはコネクタ次第です。
適切なコネクタが見つかり、MDBと紐付けられた場合、コネクタはBeanクラス定義を読み取り、分析することができます。
そのようなイベントをイベントキューに送信すると、MDBがそのイベントを永続化することがわかるでしょう。We assume that the syntax of the event is composed
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 interfacepublic NoMethodsListenerIntf {
}
2. Now define the bean class
MDBのクラスには、productBoughtとproductSoldという、二つの非静的パブリックメソッドがあります。そのため、両方ともメッセージリスナーメソッドとして公開されています。コネクタがsellQueueから製品販売イベントを取得すると、MDBのproductSoldを呼び出します。製品購入イベントでも同様です。コネクタが実行すべきターゲット・メソッドであることを示すよう、productSoldメソッドとproductBoughtメソッドに対して@EventMonitorで注釈を付けます。これらの2つのメソッドは、データベースまたは他の永続ストアにレコードを永続化します。ほかにも非静的パブリック・メソッドを定義することができますが、どれををコネクタが実行するのかはコネクタ次第です。
List 2 - Message-Driven BeanEventMonitor注釈を以下のように設定します。@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
}
}
List 3 - EventMonitor annotationこのMDBをWebLogic Serverにデプロイする際、EJBコンテナはこのMDBがEJB 3.2互換のMDBであることを検知します。resourceAdapterJndiNameの値を指定し忘れていると、WebLogic Serverが適切なコネクタリソースを見つけようとします。例えば、(現在のアプリケーションスコープ、もしくはサーバ全体のグローバルスコープでアクセス可能なコネクタのうち)同じno-methodメッセージリスナーインターフェースのサポートを宣言しているコネクタを見つけようとします。@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EventMonitor {
public String type();
}
適切なコネクタが見つかり、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 listenerpublic 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