X

Proactive insights, news and tips from Oracle WebLogic Server Support. Learn Oracle from Oracle.

New EJB 3.2 feature - Modernized JCA-based Message-Driven Bean

WebLogic Server 12.2.1 is a fully compatible implementation
of Java EE 7 specification. One of the big improvements in EJB container
in this release of WebLogic Server is that, a message-driven bean is
able to implement a listener interface with no methods. When such a
no-methods listener interface is used, all non-static public methods of
the bean class (and of the bean class's super classes except
java.lang.Object) are exposed as message listener methods.

Let's
develop a sample step by step. The sample application assumes that an
e-commercial website sends the buy/sell events to JMS Queues - buyQueue
and sellQueue - respectively when a product is sold or bought. The
connector listens on the queues, and execute message-driven bean's
non-static public methods to persist the records in events to persistent
store.

1. Define a no-methods message listener interface

In our sample, the message listener interface NoMethodsListenerIntf has no methods in it.

List 1 - No-methods message listener interface

public NoMethodsListenerIntf {

}

2. Now define the bean class

In
message-driven bean class, there are two non-static public methods -
productBought and productSold, so they are both exposed as message
listener methods. When connector gets a product-sold event from
sellQueue, it will then invoke message-driven bean's productSold method,
and likewise for product-bought event. We annotate productSold method
and productBought method with @EventMonitor, indicating that they are
the target methods that connector should execute. These two methods will
persist the records into database or other persistent store.

You can define more non-static public methods, but which ones should be executed by connector are up to connector itself.

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

  }

}

The EventMonitor annotation is defined as below:

List 3 - EventMonitor annotation

@Target({ ElementType.METHOD })

@Retention(RetentionPolicy.RUNTIME)

public @interface EventMonitor {

  public String type();

}

When
this message-driven bean is deployed onto WebLogic Server, EJB
container detects that it's an EJB 3.2 compatible message-driven bean.
If you forgot to specify a value for resourceAdapterJndiName, WebLogic
Server will try to locate a suitable connector resource, for example, a
connector that is declaring support of the same no-methods message
listener interface (in the current application or server-wide connector
that is global-accessible).

If a suitable connector is found and
associated with message-driven bean, the connector can retrieve the bean
class definition and then analyze.

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

In
connector application, we retrieve the bean class definition via
getEndpointClass() method of MessageEndpointFactory, and then inspect
every method if it's annotated with @EventMonitor. After that, we create
a javax.jms.MessageListener with the target method of the bean class to
listen on the event queues.

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

  }

  ......

}

The associated activation spec for the connector is defined as below:

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.

When
message listener's onMessage() is invoked, we create a message endpoint
via MessageEndpointFactory, and invoke the target method on this
message endpoint.

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

We
assume that the syntax of the event is composed of two digits separated
with ",", for example, 328365,87265. The former digit is customer or
retailer id, and the latter digit is product id.

Now sending such events to the event queues, you'll find that they are persisted by message-driven bean.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services