Develop inbound connectors with JCA 1.5

Integrate enterprise information systems with J2EE servers

The bidirectional connectivity between a J2EE application and an enterprise information system (EIS) is an essential requirement for enterprise integration. For example, an application component such as a session bean should be able to talk to an EIS to publish or update information. Similarly, the EIS should be able to invoke a method call on the session bean asking for an application process to begin.

Unfortunately, the initial flavors of the J2EE specification failed to address these enterprise integration scenarios; a standard was not available for developing communication protocols between a J2EE application and an EIS. To address this limitation, vendors provided connectivity models specific to their own enterprise information systems and a particular application server. These vendor-specific connectivity models proved futile when the application was ported to a different application server or if the application component talked to a different EIS.

The introduction of the J2EE Connector Architecture (JCA) provided a standard in extending J2EE application servers to integrate with any EIS and opened the doors to a new era in enterprise integration. While the initial offering (version 1.0) of outbound communications was successful, inbound connectivity was not introduced until the 1.5 version.

JCA 1.5 provides an enhanced communication model, where the EIS can initiate and control inbound communications, including security context and transaction propagation. These capabilities are fundamental building blocks for true enterprise application integration, although the standards are complex and at times obtuse with respect to implementation considerations.

In an inbound communication model, all communication is sourced from an EIS via a resource adapter to an application component hosted in a J2EE container. A resource adapter is a software library that enables the communication between an application server and an EIS. It supports bidirectional connectivity. In a nutshell, it receives messages from an EIS and passes it to J2EE application components, usually message-driven beans (MDB).

In this article, we show how inbound connectivity may be elegantly resolved by developing and implementing a resource adapter using JCA. In doing so, the adapter runs in a secure, reliable, and manageable execution environment provided by an application server.

Note: We describe the external behavior required of our EIS; the internal details are beyond this article's focus.

Let's begin by introducing our EIS, which produces trade messages.

Enterprise information system

Consider a market trading system that generates trading messages for two markets: foreign exchange (FX) and money market (MM). From the EIS perspective, two steps are involved:

  • Produce FX and MM trade messages for appropriate locations
  • Expose the location and connection information to the parties interested in receiving them

One of the primary requirements of an EIS is to publish the locations and connection factory information to a JNDI (Java Naming and Directory Interface) store.

Let's discuss the classes that represent the trade messages.

Trade messages

We represent trade messages produced by the EIS in a TradeMessage class, as shown in Listing 1. The Location class represents the market. A TradeConnectionFactory object helps achieve a connection to the EIS. A TradeConnection is the connection object that encapsulates the EIS API.

Listing 1. The TradeMessage, Location, Factory, and Connection classes

 

class TradeMessage extends java.io.Serializable { public String market; public String commodity; public Double offer; }

class Location extends java.io.Serializable { public String market; }

class TradeConnectionFactory extends javax.resource.cci.ConnectionFactory { public TradeConnection createConnection(Location location); }

class TradeConnection extends javax.resource.cci.Connection { public TradeMessage receive(){ .. } }

As we have already mentioned, a resource adapter can provide bidirectional connectivity to multiple enterprise information systems. While ManagedConnectionFactory is used for outbound connectivity, the EIS uses the ActivationSpec object to initiate inbound communication.

ActivationSpec

The ActivationSpec class represents the configuration required by the resource adapter to establish the connection between the EIS and an MDB. This class is described in the MDB's deployment descriptor, instantiated by the application server, and passed to the resource adapter. The ActivationSpec classes must conform to the JavaBeans specification. In our example, we need to connect to a remote EIS and receive zero or more TradeMessages associated with a given market.

The TradeActivationSpec in Listing 2 identifies the JNDI names for TradeConnectionFactory and Location. The TradeConnectionFactory is configured with the property values used to create TradeConnection instances with a specific EIS instance. Once the TradeConnection instance is created, the Location is then used to establish a recipient.

Listing 2. The TradeActivationSpec class

 

class TradeActivationSpec extends javax.resource.spi.ActivationSpec { private String _factory; private String _location; private ResourceAdapter _adapter;

public String getFactory() { return _factory } … }

Now that we have seen the primary elements in developing a resource adapter, let's discuss the resource adapter itself.

Resource adapters

Resource adapters are software components deployed in an application server's address space, providing integration between the application server and the EIS. The adapter is EIS-specific and can generate inbound, outbound, or bidirectional connectivity between the EIS and application server. Additionally, a single resource adapter may provide services to one or more EIS instances; however, we ignore this capability for our purposes here.

The inbound model requires the resource adapter to access the EIS's registered classes (ConnectionFactory and Location), typically within JNDI. In an inbound model, MDBs are designed to receive external EIS messages. Prior to Enterprise JavaBeans 2.1, MDBs were required to act as Java Message Service listeners to receive JMS messages. We'll discuss inbound communications further in following sections.

Resource adapters use the deployed MDB's ActivationSpec to create and manage a relationship between the EIS and the MDB via the resource adapter. All resource adapters must implement javax.resource.spi.ResourceAdapter, which has five methods, as illustrated in Listing 3. The application server accesses these methods to manage the resource adapter's lifecycle and provide notifications when a message endpoint is deployed or undeployed.

Listing 3. The TradeResourceAdapter class

 

class TradeResourceAdapter implements javax.resource.spi.ResourceAdapter { public void start(BootstrapContext ctx){..} public void stop(){..}

public void endpointActivation (MessageEndpoingFactory mf ,ActivationSpec a){..}

public void endpointDeactivation (MessageEndpoingFactory mf ,ActivationSpec a){..} public void getXAResources(ActivationSpec[] activationSpecs){..} }

When a resource adapter deploys, the configuration values are extracted from the deployment descriptor and passed to the resource adapter instance during initialization. Also after deployment, the application server invokes the start() method. The server provides a BootstrapContext object to the resource adapter, enabling access to the server's facilities, such as the WorkManager and Timer.

To receive a specific EIS message, the recipient (MDB) should show interest in consuming that message by implementing a message listener.

Message listener

For our TradeResourceAdapter, we define an interface, TradeMessageListener, as shown in Listing 4, which an MDB must implement to receive messages from the EIS. Remember, prior to EJB 2.1, MDBs couldn't receive any messages apart from JMS messages. TradeMessageListener is a simple interface that defines a single method designed to receive a TradeMessage and do "something" with the message:

Listing 4. The TradeMessageListener class

 public interface TradeMessageListener {
   public void   onMessage(TradeMessage message);
} 

JCA 1.5 introduced the work management contract that allows the resource adapter to submit works to the application server, which manages these works using its own thread pool.

Work management

The TradeResourceAdapter creates a TradeWorker instance for each MDB deployment. A more sophisticated solution would offer scalability by pooling many MessageEndpoint instances, providing a load-balancing capability for receiving and processing TradeMessages.

The resource adapter submits a TradeWorker to the application server for execution. The application server invokes the run() method, which starts a connection for receiving the messages incessantly. Once you receive a message, call the recipient's onMessage() method to process the message:

 

public void TradeWorker extends javax.resource.spi.Work { private ActivationSpec _spec; private MessageEndpoint _mep; private TradeConnection _conn;

public TradeWorker(ActivationSpec spec,EndpointListener mep,TradeConnection con) { // Hold these references for this Work instance _spec = spec; _mep = mep; _conn = con; }

public void run() { while (ok) { // Connection receives the trade messages TradeMessage msg = conn.receive(); // Invoke the message endpoint(MDB)'s onMessage // for processing a trade message if (msg != null) { ((TradeMessageListener)mep).onMessage(msg); } } } }

The MessageEndpoints are basically the components that show interest in receiving the messages from an EIS. They implement the message listener interface, which is discussed in the following section.

Endpoint activations

When a MessageEndpoint starts or stops, that is, when an MDB is deployed or undeployed, the application server calls the endpointActivation() and endpointDeactivation() methods respectively. The application server checks the MDB's messaging type with the list of resource adapters that support the ActivationSpec.

During the endpointActivation call, a TradeWorker is submitted for execution via the WorkManager's scheduleWork() method. As you have already seen, TradeWorker establishes a connection to the EIS in its run() method and starts receiving the messages:

 

public void endpointActivation (MessageEndpointFactory endpointFactory ,ActivationSpec spec ) throws ResourceException { try { // Create a message endpoint from the endpoint factory. // The disadvantage of doing so is that you end up with // just one instance of MDB and hence not taking the advantage // of object pooling. See the text for more information.

MessageEndpoint mep = endpointFactory.createEndpoint(null);

if (mep instanceof TradeMessageListener) { // Use spec to fetch Connection Factory from JNDI. TradeConnectionFactory f = … //Lookup JNDI. // Use spec to fetch Location Location location = … //Lookup JNDI. // Use Connection Factory to create Connection. TradeConnection connection = f.createConnection(location); TradeWorker worker = new TradeWorker (TradeActivationSpec) spec ,(TradeMessageListener) mep ,connection );

// Scheduling the work. _workManager.scheduleWork(worker); } } catch (UnavailableException e) {..} }

As mentioned in the code comments, there's a disadvantage in creating the message endpoint during the enpointActivation() method. We'll end up having just one instance of an MDB! To take advantage of MDB's object pooling, create message endpoints for each message in the TradeWorker's run() method as shown below:

 

public void TradeWorker extends javax.resource.spi.Work { private ActivationSpec _spec; private MessageEndpointFactory _mef; private TradeConnection _conn;

public TradeWorker(ActivationSpec spec, MessageEndpointFactory mef,TradeConnection con) { // Hold these references for this Work instance _spec = spec; _mef = mef; _conn = con; } .... public void run() { while (ok) { // Connection receives the trade messages TradeMessage msg = conn.receive();

// Create endpoints for each of the messages if (msg != null) { MessageEndpoint mep = _mef.createEndpoint(null); if (mep instanceof TradeMessageListener){ ((TradeMessageListener)mep).onMessage(msg); } } } } }

Certainly, we can improve the MDB instance pooling in the above code, but I leave that task for you.

The resource adapter's configuration details are described in a deployment descriptor named ra.xml. Read on for details.

Resource adapter's deployment descriptor

Resource adapters are configured using a deployment descriptor. Pay attention to the messagelistener-type and activationspec tags:

1 2 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more