Oracle Message Broker Adminstration Guide
Release 2.0.1.0

Part Number A65435-01

Library

Product

Contents

Index

Go to previous page Go to next page

14
Asynchronous Component Invocation

This chapter covers the Oracle Message Broker Asynchronous Component Invocation (ACI) feature. ACI links JMS, as provided by the Oracle Message Broker, to applications running in the Oracle Database Server. ACI can automatically invoke Enterprise JavaBeans (EJB), when messages arrive at a JMS destination. The EJB can subsequently consume and process JMS messages from a queue or topic.


Note:

EJBs can only access remote mode Oracle Message Brokers. Oracle Message Broker Local mode is not supported. 


Asynchronous Component Invocation has the following important features:

This chapter covers the following:

ACI Architecture

The Oracle Message Broker ACI feature consists of the following components:

Both the ACI Listener and the Dispatcher reside in the same JVM as the Oracle Message Broker.

Figure 14-1 shows a high-level view of ACI components.

Figure 14-1 ACI Components


ACI Listener

When a user-defined condition occurs, the ACI Listener generates an event that sends a notification to an EJB. Each condition, called a trigger, acts on one and only one JMS destination (queue or topic). It is not possible to set triggers that involve multiple JMS destinations.

The ACI Listener watches one or more JMS destinations and checks if a condition, specified by a rule stored in the LDAP Directory, is met. When a condition is met, the ACI Listener generates a notification event and gives it to the ACI Dispatcher.

When several ACI triggers are set on the same JMS queue, all the triggers that meet the condition associated with their rules receive a notification. In this situation, it is possible for several EJBs to receive a notification for the same message. In this case, only one of the EJBs consumes the message. It is part of the developer's task to ensure that multiple ACI triggers set on the same JMS queue do not compete for the same messages. One way around this problem is to use disjoint message selectors for the rules associated with a trigger on a queue.

When associating ACI triggers to topics, each trigger acts as a distinct consumer, and all notified EJBs can consume copies of the same messages.

ACI Dispatcher

The ACI Dispatcher sends notifications to registered EJBs according to the specified configuration. The EJB can then consume one or more messages using the Java framework or using JMS.

The ACI Dispatcher invokes EJBs at the request of the ACI Listener. EJB invocations are asynchronous, that is, many requests can be issued and managed simultaneously without performance degradation in the Oracle Message Broker.

Using ACI, JMS topics require a durable subscription. Since JMS only allows one active use of a durable subscription, there must only be only one notification outstanding at a time for topics. This means that the ACI Dispatcher cannot issue a notification until the previous notification is completed. This may become a problem with some trigger configurations if EJBs take time to process notifications, and there are many messages published on a topic.

If the ACI Dispatcher fails invoking an EJB multiple consecutive times for the same trigger (this may happen for instance if the Oracle database is not started, or if the EJB does not provide robust error handling and a queue contains an invalid message that systematically make the EJB fail), that trigger will be automatically canceled and a message will be inserted in the log file. To re-enable the trigger, one has to explicitly disable and re-enable the trigger using Oracle Message Broker administrative tools.

ACI Adapters

ACI Adapters (EJB Adapter) - provide an application-side framework for receiving asynchronous notifications. Each adapter supports a type of component. Currently, the ACI feature only provides an adapter for Oracle EJBs.

ACI Helper Classes

ACI Helper Classes (Java Framework) - the helper classes can optionally be used by EJBs to simplify management of JMS messages. Such classes automate the process of consuming messages by hiding most of the JMS complexity (e.g., creating a connection, session, and consumer). These APIs make EJB programming easier, as compared to JMS programming, upon asynchronous invocation.

EJBs that do not use the ACI helper classes and directly use the JMS API have the following restrictions:

ACI Triggers

The conditions upon which asynchronous component invocations occur are specified by setting ACI triggers on a JMS destination using an LDAP Directory ACI trigger entry. Trigger entries are configured using the Oracle Message Broker administrative tools.

Trigger entries have the following parameters:

ACI triggers are set by specifying the above parameters on a destination, and by enabling the trigger using the status parameter. Since some trigger parameters only apply to queues or topics, there are two ACI trigger entry types: QueueTrigger and TopicTrigger (for more information on administration for these entries "Creating and Configuring Asynchronous Component Invocation Triggers").

Setting the ACI Threshold Parameter

The threshold parameter specifies the number of messages that must be stored in a destination before an ACI is sent. The ACI subsystem cannot guarantee that an invocation receives a "threshold" number of messages. The quality of service is best effort and the number of invocations issued may not match what is expected. Problems that could result in a different number of messages delivered include:

For ACI to work as expected, each component should consume only the number of messages specified with the "threshold" parameter.

Setting the ACI Concurrency Parameter

The concurrency parameter specifies the number of concurrent ACIs that can execute for a given trigger (for queues only). ACI attempts to guarantee that there are never more invocations in progress for a trigger at one time than the concurrency parameter. This property will always be true, unless a communication failure happens while a component is processing the asynchronous notification, and the dispatcher interprets the communication failure as a component failure.

Note that ACI invocations must not be issued concurrently for topics (therefore the "concurrency" parameter must be equal to 1). This limitation is implied by the JMS specification, which specifies that only one session at a time can have a topic subscriber for a particular durable subscription. Since triggers set on topics require durable subscriptions, the triggering mechanisms must ensure that there is only one active invocation at a time.

EJB Adapter

The ACI EJB adapter provides support classes for writing Enterprise JavaBeans that process incoming JMS messages. The ACI EJB adapter provides two sets of classes that support the interface between EJBs and the Oracle Message Broker:

Message-driven beans provide a higher-level abstraction and are easier to use. Notification-driven beans are more flexible; for instance, they make it possible to consume several messages as part of the same transaction, or browse a queue to find a specific message.

Notification-driven Beans

The EJB adapter defines the following classes and interfaces for notification-driven beans:

/*
 * ACI exception class.
 */
public class ACIException extends Exception {
  	// ...
}

/**
 * The description of the JMS destination on which the message(s) reside(s).
 */
public abstract class DestinationDescriptor
{
  	public String cf;			           // Connection factory to use
	  public String dest;			         // JMS destination to access
	  public String selector;			     // Optional message selector
	  public String subscription; 		// Durable subscription (for topics only)
}

public class QueueDescriptor extends DestinationDescriptor
{
}

public class TopicDescriptor extends DestinationDescriptor
{
  	public String subscription; 		// Durable subscription
	  public String client_id;   			 // Client identifier
}

/**
 * This interface must be inherited by an EJB "Remote" interface that will get
 * messages from a JMS destination.
 */
public interface ACIRemote
{
  	public boolean msgPending(DestinationDescriptor dest, int threshold)
		    throws ACIException;
}

The "DestinationDescriptor" class defines the information necessary for the EJB to get the message(s) that triggered the asynchronous invocations. The "cf" and "dest" members are names that can be used to lookup a connection factory and a destination. The "selector" member is an optional message selector used by the ACI Listener. "DestinationDescriptor" is an abstract class. Concrete destination descriptors are instances of the "QueueDescriptor" or "TopicDescriptor", which inherit from "DestinationDescriptor". "QueueDescriptor" does not add member variables to "DestinationDescriptor". "TopicDescriptor" defines an additional subscription name ("subscription") and the client identifier ("client_id"), necessary for consuming messages through a durable subscriber. Note that the topics used with ACI must always be accessed through durable subscribers. The durable subscriber associated to a trigger must be created using Oracle Message Broker's administrative tools prior to starting the broker.

Notification-driven beans implement the "msgPending" operation, which will be invoked asynchronously by Oracle Message Broker. As a convenience, the ACI EJB adapter provides an interface called "ACIRemote" that defines the "msgPending" method: notification-driven beans simply need to implement this interface. Using this interface is however not mandatory, and EJBs can directly define "mgsPending" among their own public methods.

The "msgPending" method must return true if it completed successfully, and false otherwise (in that case, the method may be re-invoked depending on the configuration of the ACI trigger). "msgPending" takes a destination descriptor (instance of "QueueDescriptor" or "TopicDescriptor") and an integer number which indicates the number of messages that the component is expected to consume, based on the configuration of the ACI trigger.

Figure 14-2 illustrates the asynchronous invocation of a notification-driven bean of type "MyEJB". Oracle Message Broker first creates an EJB by invoking the "create" operation of the EJB "Home" object (1). As a result of this request, the EJB receives an invocation to its "ejbCreate" method (2). Then, Oracle Message Broker sends an asynchronous invocation to the "msgPending" operation of the EJB "Remote" object (3), which leads to the actual invocation of the "msgPending" method of the bean (4).

Figure 14-2 Asynchronous Invocation of an EJB


Changing a standard EJB into a notification-driven bean is easy: it just requires adding an inheritance link to the "Remote" bean interface (optional), implementing the "msgPending" operation, and re-deploying the bean.

Sample Notfication-Driven Bean

The directory $OMB_HOME/samples/aci/jms (or on Windows NT systems %OMB_HOME%\samples\aci\jms), contains a sample notification-driven bean.

Message-driven Beans

A message-driven bean is a JMS "MessageListener". A message-driven bean is invoked implicitly as a result of the arrival of a JMS message. Message-driven beans must not be created or invoked explicitly by clients. They are anonymous and have no client-visible identity.

A message-driven bean instance is an instance of a message-driven bean class. To a client, a message-driven bean is a message consumer that implements some business logic running on the server.

A client accesses a message-driven bean through JMS by sending messages to the JMS destination (queue or topic) for which the message-driven bean is the "MessageListener". The "onMessage" method is called when a message has arrived for the bean to service. The "onMessage" method contains the business logic that handles the processing of the message. It has a single argument, the incoming message. Message-driven bean instances have no conversational state. This means that all bean instances are equivalent when they are not involved in servicing a client message.

All message-driven beans must implement the "MessageDrivenBean" interface. The "setMessageDrivenContext" method is called to associate a message-driven bean instance with its context maintained by the container. Typically a message-driven bean instance retains its message-driven context as part of its state.

The ACI message-driven bean framework provides the message-driven bean instance with a "MessageDrivenContext". The "MessageDrivenContext" interface has the following methods:

The "ejbRemove" notification signals that the instance is in the process of being removed by the container. In the "ejbRemove" method, the instance releases the resources that it is holding.

A message-driven bean is created in three steps. First, the message-driven bean framework creates a new message-driven bean instance. Second, it calls the "setMessageDrivenContext" method to pass the context object to the instance. Third, it calls the instance's "ejbCreate" method. Each message-driven bean class must have one "ejbCreate" method, with no arguments.

Calls to each message-driven bean instance are serialized. Therefore, a message-driven bean does not have to be coded as reentrant. However, many instances of a message-driven bean class can be executing concurrently, thus allowing for the concurrent processing of a stream of messages. No guarantees are made as to the exact order in which messages are delivered to the instances of the message-driven bean class. Message-driven beans should therefore be prepared to handle messages that are out of sequence: for example, the message to cancel a reservation may be delivered before the message to make a reservation.

Message-driven beans are actually implemented using underlying session beans. Like standard session beans, they have a home and a remote interface. These interfaces should however not be used explicitly by clients.

An ACI message-driven bean must extend the "MessageDrivenBeanAdapter" class. All ACI message-driven classes are declared in the "oracle.oas.aci" package.

Messages are consumed on a destination using either a transacted or a non-transacted session. To use a transacted session, the property "oracle.oas.mercury.aci.transacted" must be set to "true" in the bean's environment. When using a transacted session, each message is consumed in the context of a new transaction, and the transaction is committed when the "onMessage" method returns. It is not possible to perform other operations in the context of that transaction. However, the user transaction returned by the bean's context can be used to create an independent transaction in the context of the database.

The directory $OMB_HOME/samples/aci/messagedriven (%OMB_HOME%\samples\aci\messagedriven on Windows NT), contains a sample message-driven bean.

Java Helper Classes

Oracle Message Broker's ACI feature provides helper classes that can optionally be used by EJBs to simplify management of JMS messages. When using the EJB adapter and notification-driven beans, the developer can use the "ACIHelper" class to automate the process of consuming JMS messages.

The "ACIHelper" class is instantiated with a destination descriptor, a bean's session context, and a boolean flag indicating whether sessions have to be transacted or not. It provides simple functions for consuming messages, without requiring the EJB to know whether messages come from a queue or a topic, and to deal explicitly with connections, sessions, or producers. Note that it does not provide blocking methods for consuming messages, since such methods should not be used by well-behaved beans.

The "ACIHelper" class defines the following methods:

class ACIHelper {
  /** Creates a new helper. */
  public ACIHelper(DestinationDescriptor dd,
                   SessionContext sc,
                   boolean transacted)
    throws ACIException
  { ... }

  /** Close the resources opened by the helper. */
  public void close()
    throws ACIException
  { ... }

  /** Consumes a message from the destination without blocking. */
  public Message consume()
    throws ACIException
  { ... }

  /** Commits the session if it transacted. */
  public void commit()
    throws ACIException
  { ... }

	  // Helper methods for EBJs that use directly the JMS API

  /** Create a queue connection factory from a string. */
  static public QueueConnectionFactory createQueueConnectionFactory(String id)
    throws JMSException
  { ... }

  /** Create a topic connection factory from a string. */
  static public TopicConnectionFactory createTopicConnectionFactory(String id)
    throws JMSException
  { ... }

  /** Create a queue from a string. */
  static public Queue createQueue(String id)
    throws JMSException
  { ... }

  /** Create a topic from a string. */
  static public Topic createTopic(String id)
    throws JMSException
  { ... }
}

Use the ACIHelper methods createTopic, createQueue, createTopicConnectionFactory, and createQueueConnectionFactory, to create destinations and connection factories based on the identifier embedded in the destination descriptor given to the bean. EJBs should use these methods to create destinations and connection factories instead of using LDAP. See the code found in the sample notification driven bean for more details.

A sample bean that uses the ACI helper classes is shown in the directory $OMB_HOME/samples/aci/helper (%OMB_HOME%\samples\aci\helper on Windows NT).

ACI Tutorial

This section illustrates the different steps of developing and deploying an EJB that uses ACI. A message-driven bean, of class MyMDBean, receives JMS messages and prints their content in a log file.

All the code of this tutorial is found in the directory $OMB_HOME/samples/aci/messagedriven (or on Windows NT systems, $OMB_HOME%\samples\aci\messagedriven).

The steps of this tutorial follow:

  1. Configure Oracle Database

  2. Define a Remote Interface

  3. Define a Home Interface

  4. Implement the EJB

  5. Compile and Generate Jar File

  6. Deploy the EJB

  7. Add a Trigger Entry to Oracle Message Broker

Configure Oracle Database

Before using ACI, load Oracle Message Broker's client classes in the Oracle Database Server, and grant permissions to the schema in which the EJB executes using the following two commands (in this tutorial, we use the sample schema SCOTT/TIGER):

# grant permissions to SCOTT
sqlplus sys/sys_password @$OMB_HOME/admin/plsql/setupaci.sql SCOTT
# Loading OMB client classes
loadjava -r -g SYS -u SCOTT/TIGER ${OMB_HOME}/classes/ombclt.jar

or on Windows NT systems:

sqlplus sys/sys_password @%OMB_HOME%\admin\plsql\setupaci.sql SCOTT
loadjava -r -g SYS -u SCOTT/TIGER %OMB_HOME%\classes\ombclt.jar

Where:

sys_password is the administrative user sys's password.


Note:

Run these commands only once for each schema, prior to deploying EJBs in the schema. 


Define a Remote Interface

ACI beans must define a remote interface, which extends from ACIRemote. In addition, like any EJB remote interface, this interface must extend EJBObject. The remote interface of MyMDBean is defined in file test/MyMDBeanRemote.java as follows:

package test;

public interface MyMDBeanRemote extends javax.ejb.EJBObject,
                                        oracle.oas.mercury.aci.ACIRemote
{
}

Define a Home Interface

ACI beans must provide a home interface with a create() method that returns an instance of the bean and takes no arguments. The home interface is defined in file test/MyMDBeanHome.java as follows:

package test;

public interface MyMDBeanHome extends javax.ejb.EJBHome {
  public MyMDBeanRemote create() 
    throws java.rmi.RemoteException,
           javax.ejb.CreateException;
}

Implement the EJB

A message-driven bean class implements the MessageDrivenBean interface (which extends MessageListener) and must implement its onMessage() method. In addition, it must extend the MessageDrivenBeanAdapter class. The message-driven bean implementation is defined in file test/MyMDBean.java as follows:

package test;

import java.rmi.*;
import javax.ejb.*;
import javax.jms.*;
import oracle.oas.mercury.aci.*;

public class MyMDBean extends MessageDrivenBeanAdapter
                      implements MessageDrivenBean
{
  public void setMessageDrivenContext(MessageDrivenContext ctx) { }

  public void ejbCreate() throws RemoteException, CreateException { }

  public void ejbActivate() { }

  public void ejbPassivate() { }

  public void ejbRemove() { }

  public void onMessage(Message msg)
  {
    try {
      if(msg instanceof TextMessage)
        System.out.println("Message is: " + ((TextMessage)msg).getText());
      else
        System.out.println("Message is not of type TextMessage");
    } catch(JMSException e) {
        System.out.println(e.getMessage());
    }
  }
}


Note:

The standard output of EJBs goes to the a file named "$ORACLE_HOME/admin/$ORACLE_SID/bdump/${ORACLE_SID}*.trc" 


Compile and Generate Jar File

Files must be compiled and added to a jar file before deployment:

javac test/*.java
jar cf0 server.jar test/*.class

The resulting server.jar file contains all EJB implementation classes.

Deploy the EJB

The information necessary for creating the deployment descriptor consists of the name of the bean, its classes, and its environment. The contents of the "test.ejb" file used to generate the deployment descriptors is as follows:

SessionBean test.MyMDBean
{
  BeanHomeName = "test/myEJB";
  RemoteInterfaceClassName = test.MyMDBeanRemote;
  HomeInterfaceClassName = test.MyMDBeanHome;

  AllowedIdentities = { PUBLIC };
  RunAsMode = CLIENT_IDENTITY;
  StateManagementType = STATEFUL_SESSION;

  EnvironmentProperties {
    oracle.oas.mercury.aci.transacted = "true";
    oracle.oas.mercury.aci.debug = "true";
  }
}

The "server.jar" file can then directly be deployed using Oracle Database Server deployment tools, as follows:

deployejb -republish -temp temp -u SCOTT -p TIGER -descriptor test.ejb \
  -s sess_iiop://localhost:2481:${ORACLE_SID} server.jar

Add a Trigger Entry to Oracle Message Broker

The last step consists configures the trigger entries in the LDAP Directory. Define two trigger entries. The first trigger is registered on a volatile queue (volqueue) and is fired every 5 messages with up to 3 concurrent invocations. The second trigger is registered on a volatile topic and is fired every 10 messages.

The sample configuration file SetupACI shows how to setup these triggers. This file is located in the directory $OMB_HOME/samples/admin (or on Windows NT systems, %OMB_HOME%\samples\admin). Lines marked with "USERINFO" may need to be modified to match your system configuration. Run SetupACI, using the AdminUtil command.


Go to previous page Go to next page
Oracle
Copyright © 1996-2000, Oracle Corporation.

All Rights Reserved.

Library

Product

Contents

Index