OC4J 10.1.3: Serving JCA 1.5 Applications

Frances Zhao
August 2004


1. Introduction

The J2EE Connector Architecture (JCA) defines standard contracts for integration of application servers with heterogeneous Enterprise Information Systems (EISs). As part of J2EE 1.4, JCA 1.5 adds new contracts to JCA 1.0 that specifically support asynchronous message delivery from a wide range of message providers. Now JCA applications can benefit from simplified bi-directional communications with EISs.

Being J2EE 1.4 compliant, Oracle Containers for J2EE (OC4J) 10.1.3 fully supports JCA 1.5 applications. Getting your JCA 1.5 applications up and running in OC4J is very simple, and this article uses a working application to demonstrate how you can do this. We first explain how to configure and deploy a JCA resource adapter that your application needs, and then explain how to configure your application to use the resource adapter to access an EIS. Our example application exercises the JCA 1.5 specific functionalities by using an MDB with a JMS provider to monitor inventory level and send out e-mail messages when the inventory level is low. All the functionalities described in this article are available in the OC4J 10.1.3 Developer Preview 2 release (previously named as OC4J 10.0.3 Developer Preview 2 on OTN), and our descriptions are based on this release.

Here is the source code for the sample in this article.


2. J2EE Connector Architecture 1.5

The J2EE Connector Architecture (JCA) facilitates the integration of application servers with heterogeneous Enterprise Information Systems (EISs). An EIS provides the information infrastructure for an enterprise. Examples of an EIS include: an ERP system like the Oracle E-Business Suite or SAP R/3, a mainframe transaction processing system, or a relational database system. JCA defines System-level Contracts that encapsulate important requirements for effective and scalable integration with EISs, such as connection management and pooling, transaction management to support transactions internal to an EIS and across multiple EISs, error logging and tracing, and a security framework enabling both container-managed and component-managed sign-on.

The EIS-side of these System-level Contracts is implemented in a Resource Adapter. A resource adapter is specific to an underlying EIS. It is a system-level software driver that is used by an application server or an application client to communicate and operate with an EIS. While a Resource Adapter is specific to the EIS it represents, it is not specific to a particular application server and can therefore be reused across any J2EE application server.

JCA was introduced (as JCA 1.0) in J2EE 1.3 to provide outbound communications from application servers to EISs. However, JCA 1.0 did not provide a mechanism for inbound communications from EISs to application servers, such as asynchronous message delivery or event notification.

Recognizing this need, JCA 1.5 adds a list of very important System-level Contracts like Transaction Inflow and Message Inflow to support bi-directional communications between J2EE applications and EISs. Specifically, JCA 1.5 defines:

* Lifecycle management contract: allows an application server to initialize a resource adapter instance during the deployment of the adapter or application server startup. It also allows an application server to send notifications to the resource adapter instance during server shutdown or undeployment of the adapter.

* Work management contract: allows a resource adapter to carry out its logic by using threads dispatched by an application server, rather than creating threads on its own. The handshake is done via a Work instance submission. The application server can thus more efficiently manage the threads, and have better control over their execution contexts (like security and transaction).

* Transaction inflow contract: allows an application server to process an imported transaction propagated from a resource adapter, and ensure that the ACID properties of the imported transaction are preserved.

* Message inflow contract: allows a resource adapter to send asynchronous messages to an application server�s message endpoints, regardless of the messaging style, semantics, or delivering infrastructure. This is a key contract in JCA 1.5, which makes it possible for a wide variety of message providers (e.g., Java Message Service (JMS), Java API for XML Messaging (JAXM)) to be plugged into an application server via a resource adapter.

* A new packaging model for different types of resource adapters (outbound only, inbound only, or both).


3. JCA 1.5 Application in action with OC4J

Oracle Containers for J2EE (OC4J) 10.1.3 is J2EE 1.4 compliant, and fully supports JCA 1.5. Getting your JCA 1.5 applications up and running in OC4J is very simple and straightforward.

In this section, we will walk you through a JCA 1.5 application and show you how to get it working in OC4J. We will focus on two aspects: first, we show you how to configure and deploy a JCA 1.5 resource adapter for both outbound and inbound communication; second, we show you how to configure the application to use the resource adapter to carry out its logic.

3.1 Application Design

Our application is a rather simplified inventory control program. It uses a JSP to let users update the inventory with information such as inventory id, order amount, order description, and stores such information on each new update into a JMS queue. In doing so, it uses a JCA 1.5 JMS resource adapter for outbound communication to the JMS provider. Meanwhile, the application includes an MDB that listens to the same JMS queue provided by the JMS provider, and gets triggered when a message is entered into that JMS queue. The MDB makes use of the same JCA 1.5 JMS resource adapter for inbound communication from the JMS provider. It checks on each new inventory update, and would send e-mail notification using JavaMail APIs to a contact personnel once the inventory level of a particular item is below a threshold.

Note that this application is for illustration only and uses hard-coded values in different places. For example, the contact personnel�s e-mail address is currently hard-coded in the MDB code, although it�s possible to use another JSP client to override that default setting. In a real-world application, the client would update the inventory database, and the MDB can use a resource adapter to associate itself with triggers from the database and carry out more sophisticated actions (e.g., sending out additional purchase orders via persistent JMS queues) when the inventory-level drops below certain threshold.

The JMS provider we use is OC4J JMS, and the JCA 1.5 resource adapter we use is OC4J 10.1.3�s generic JMS resource adapter.

Below is the architectural diagram of the JCA 1.5 application:





3.1.1 JMS

The Java Message Service (JMS) is an API for Java messaging clients. JMS provides two programming models: point-to-point and publish-and-subscribe. In the point-to-point model, one sender puts a message on a queue and it is delivered to only one receiver. The publish-and-subscribe model adds a broadcast mode in which any number of publishers can add messages to a topic, and any number of subscribers receive all messages posted to topics to which they subscribe. JMS queues and topics are bound in the JNDI environment and made available to J2EE applications.

Our application assumes the point-to-point model, and requires setting up a queue with a QueueConnectionFactory in OC4J JMS.

3.1.2 Web Client

The web interface lets the user enter three fields: inventory id, order amount, and order description, and then invokes a JSP to construct a JMS message to deliver to the queue we have set up in OC4J JMS. Both the queue and the QueueConnectionFactory (factory objects for accessing the queue) objects are accessed via OC4J�s generic JMS resource adapter. We will explain the detail in the configuration sections.

3.1.3 MDB

Message Driven Bean (MDB) is a special type of EJB. It is essentially a message consumer that listens to a message endpoint (e.g., a JMS queue or topic) and gets activated when a message arrives. An MDB does not have home or component interfaces like other types of EJBs; it only has a bean-implementation class. The MDB bean class has to implement the javax.ejb.MessageDrivenBean interface. Besides, it must also implement the message listener interface required by the messaging type that it supports; for example, since our application is JMS-based, the MDB listens to JMS messages and must implement the javax.jms.MessageListener interface.

Below is the key piece of MDB code that implements the onMessage(javax.jms.Message msg) method in the javax.jms.MessageListener interface.

  public void onMessage(javax.jms.Message msg)
                        
{
System.out.println("--simpleMdb::onMessage(msg) called");
try
{
String id = msg.getJMSMessageID();
String inventoryId = msg.getStringProperty("id");
String orderAmount = msg.getStringProperty("oamount");
String orderDesc = msg.getStringProperty("message");
String message = "Message received --" +
"\n\tDate: " + new java.util.Date() +
"\n\tId: " + id +
"\n\tInventoryId: " + inventoryId +
"\n\tOrderAmount: " + orderAmount +
"\n\tOrderDescription: " + orderDesc;
System.out.println(message);
if ( Integer.parseInt(inventoryId) == 1000 &&
INVENTORY - Integer.parseInt(orderAmount)
<= INVENTORY_LEVEL )
{
System.out.println("Low inventory level detected!!!");
System.out.println("Sending notification e-mail ...");
String from = "mdb@company.com";
String to = "inventory_control@company.com";
String subject = "Low Inventory Level Detected";
sendmail(from, to, subject, message);
System.out.println("E-mail sent successfully");
}
else
System.out.println("Inventory level ok.");
}
catch (Throwable ex)
{
ex.printStackTrace();
}
}

Below is the implementation of the sendmail(�) method in above code segment. It uses the JavaMail API to construct the e-mail message based on the JMS message, and then delivers it to the target address (or addresses).

  private void sendmail(String from, String to,
                        
String subject, String mesg)
throws Exception
{
Properties props = new Properties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", "localhost");
javax.mail.Session session =
javax.mail.Session.getDefaultInstance(props, null);
// Construct a MimeMessage
javax.mail.Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
// This could be a comma-separated list
msg.setRecipients(javax.mail.Message.RecipientType.TO,
InternetAddress.parse(to, false));
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setText(mesg);
// Send the message.
Transport.send(msg);
}

Now that we have gone through the application logic, let�s focus on the JCA-related aspects. Getting the JCA 1.5 application to run in OC4J involves two important steps: (1) properly setup a resource adapter in association with your EIS, like a JMS provider; (2) configure your application to use the resource adapter to access the EIS.

The first step assumes that you do not need to write up a new resource adapter. The task of writing a resource adapter is out of the scope of this article.

3.2 JCA 1.5 resource adapter and provider setup in OC4J

In our application, the EIS is a JMS provider, which is OC4J JMS. And our resource adapter is OC4J�s generic JMS resource adapter.

JMS provider setup

In order to setup the resource adapter, we first need to configure the JMS provider OC4J JMS. The <J2EE_HOME>/config/jms.xml file should be modified for such purpose. For each Destination object (queue or topic) used, you must specify its JNDI name (with the �location� attribute) and connection factory in this file.

For our application, we only need to set up a queue with a queue connection factory.

 <jms-server ... ...
                        
port="9127"
... ...
<queue name="Demo Queue" location="jms/demoQueue">
<description>A dummy queue</description>
</queue>
<queue-connection-factory
name="jms/QueueConnectionFactory"
location="jms/QueueConnectionFactory"/>
... ...
</jms-server>

Resource adapter configuration

Once we have configured the EIS provider, we can now configure the resource adapter to work with it. OC4J supports three resource adapter deployment descriptors: ra.xml, oc4j-ra.xml, and oc4j-connectors.xml. (Note that if you use the generic JMS resource adapter in an OC4J 10.1.3 version that is newer than the OC4J 10.0.3 Developer Preview 2, you can skip all of the following steps. When using OC4J 10.1.3 Developer Preview 2, you only need to uncomment the relevant section in the default oc4j-connectors.xml. Nevertheless, we describe these steps here as general illustration.)

ra.xml is the standard J2EE deployment descriptor for developing resource adapters. For our application, we need to define the JMS queue and the queue connection factory�s JNDI names. We also need to define a resource provider name that will be used as a prefix for JNDI lookup. Note that these are requirements coming from the way that the resource adapter is implemented (some other resource adapter might choose to do this entirely in a different way). And also that what ra.xml provides is information about the capabilities of the resource adapter. oc4j-ra.xml is really where you define the queue connection factory instance.

<connector ...>
                        
<resourceadapter>
<resourceadapter-class>
oracle.j2ee.ra.jms.generic.JMSResourceAdapter
</resourceadapter-class>
... ...
<config-property>
<config-property-name>resourceProviderName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>oc4jjms</config-property-value>
</config-property>
... ...
<!-- Queue admin object -->
<adminobject>
<adminobject-interface>javax.jms.Queue</adminobject-interface>
<adminobject-class>
oracle.j2ee.ra.jms.generic.AdminObjectQueueImpl
</adminobject-class>
<config-property>
<config-property-name>jndiName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jms/demoQueue</config-property-value>
</config-property>
<config-property>
<config-property-name>resourceProviderName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>oc4jjms</config-property-value>
</config-property>
</adminobject>
... ...
<outbound-resourceadapter>
<!-- non-XA Queue Connection Factory -->
<connection-definition>
<managedconnectionfactory-class>
oracle.j2ee.ra.jms.generic.ManagedQueueConnectionFactoryImpl
</managedconnectionfactory-class>
<connectionfactory-interface>
javax.jms.QueueConnectionFactory
</connectionfactory-interface>
<connectionfactory-impl-class>
oracle.j2ee.ra.jms.generic.QueueConnectionFactoryWrapper
</connectionfactory-impl-class>
<connection-interface>javax.jms.Connection</connection-interface>
<connection-impl-class>
oracle.j2ee.ra.jms.generic.ConnectionWrapper
</connection-impl-class>
<config-property>
<config-property-name>jndiLocation</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>
jms/QueueConnectionFactory
</config-property-value>
</config-property>
</connection-definition>
</outbound-resourceadapter>
</resourceadapter>
</connector>

The admin object configuration goes into oc4j-connectors.xml and connection factory configuration goes into oc4j-ra.xml.

oc4j-ra.xml contains OC4J-specific deployment configurations for a resource adapter. For example, it contains EIS connection information as specified in the deployment descriptor of the resource adapter ra.xml, the JNDI name to be used, connection pooling parameters, and resource principal mappings (security-config element). Whenever you deploy a resource adapter, OC4J generates this file if it does not already exist in the archive. For our application, we only need to define a connection factory, and reference the JNDI location of the queue connection factory as in ra.xml.

 <oc4j-connector-factories>
                        
<connector-factory location="genericjms/MyQCF" connector-name="GenericJMS">
<managedconnectionfactory-class>
oracle.j2ee.ra.jms.generic.ManagedQueueConnectionFactoryImpl
</managedconnectionfactory-class>
<config-property name="jndiLocation" value="jms/QueueConnectionFactory"/>
</connector-factory>
</oc4j-connector-factories>

oc4j-connectors.xml contains a list of resource adapters that have been deployed to OC4J. OC4J generates this file if it does not exist in the archive. For our application, we only need to define a queue, and reference the JNDI location of the queue as in ra.xml.

<oc4j-connectors ... ...>
<connector name="genericjms" path="genericjms.rar" location="genericjms">
<config-property name="lookupMethod" value="resourceProvider"/>
<config-property name="resourceProviderName" value="oc4jjms"/>
<adminobject-config location="genericjms/MyQueue1">
<adminobject-class>
oracle.j2ee.ra.jms.generic.AdminObjectQueueImpl
</adminobject-class>
<config-property name="jndiName" value="jms/demoQueue"/>
<config-property name="resourceProviderName" value="oc4jjms"/>
</adminobject-config>
</connector>
</oc4j-connectors>

Resource adapter deployment

OC4J supports the deployment of resource adapters as either stand-alone or packaged within an EAR file. A stand-alone resource adapter, materialized by a stand-alone Resource Adapter Archive (RAR) file, is available to all deployed applications in the application server instance. Alternatively, users can deploy a resource adapter that is packaged within an enterprise application archive (EAR) file. This resource adapter is available only to the J2EE application with which it is packaged. For our JCA 1.5 application, we will use a stand-alone resource adapter.

Standalone resource adapters can be deployed into OC4J using the admin.jar utility:

java -jar <J2EE_HOME>/admin.jar ormi://<host> <uid> <pwd>

-deployConnector -file genericjms.rar -name genericjms

<host> is the host name where your OC4J instance is running. <uid> and <pwd> are the username and password of an OC4J administrator. The .rar file after �-file� is the archive for the deployed resource adapter. And the �-name� argument specifies what name you want to give this resource adapter deployment. For our application, we are using the generic JMS provider in OC4J, so this step is optional (the generic JMS resource adapter will be deployed by OC4J 10.1.3 automatically if you specify it in oc4j-connectors.xml. In OC4J 10.1.3 versions newer than OC4J 10.0.3 Developer Preview 2, the resource adapter is available with the name �OracleASjms�.). But you can simply replace the RAR file name and the adapter name used here to deploy your own resource adapter.

Resource adapters packaged within EAR files are deployed into OC4J along with the applications. Simply add the RAR file to the application�s EAR file, add <connector> tag to the list of <modules> in application.xml and deploy the JCA application.

3.3 JCA 1.5 Application setup in OC4J

After we have configured and deployed the resource adapter, now we can configure our JCA application to access the EIS via the resource adapter. This will be done for both the MDB and the web client. But we need to first make sure that a resource provider is setup properly like in the resource adapter�s configuration.

Resource Provider setup

In the global <J2EE_HOME>/config/application.xml, we need to setup a resource provider name that matches the one in ra.xml (e.g., �oc4jjms�). For the generic JMS provider in OC4J 10.1.3, this is already done by default (for OC4J 10.1.3 Developer Preview 2, just uncomment the relevant section in the default application.xml). But for other resource adapters, this would be necessary.

... ...
                        
<resource-provider
class="com.evermind.server.jms.Oc4jResourceProvider"
name="oc4jjms">
<description>oc4j-jms loop back resource provider
</description>
</resource-provider>
... ...

MDB setup

The MDB configuration contains two parts: the standard deployment descriptor ejb-jar.xml and the OC4J-specific descriptor orion-ejb-jar.xml.

To enable the MDB in our JCA 1.5 application to receive messages from the JMS queue, we only need to specify in orion-ejb-jar.xml the bean�s name and the resource adapter�s deployment name.

<orion-ejb-jar>
                        
<enterprise-beans>
<message-driven-deployment name="simpleMdb"
resource-adapter="genericjms">
</message-driven-deployment>
</enterprise-beans>
... ...
</orion-ejb-jar>

For the standard MDB descriptor ejb-jar.xml, an MDB that is configured to listen on a message endpoint of a resource adapter requires some <activation-config> properties to be set, in the form of name-value pairs. These properties are defined according to the EIS provider used. For our application, this will be OC4J JMS; and the necessary properties include: DestinationType, DestinationName, MessageSelector, and ConnectionFactoryJndiName.

<ejb-jar>
                        
... ...
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>
DestinationType
</activation-config-property-name>
<activation-config-property-value>
javax.jms.Queue
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
DestinationName
</activation-config-property-name>
<activation-config-property-value>
java:comp/resource/oc4jjms/jms/demoQueue
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
MessageSelector
</activation-config-property-name>
<activation-config-property-value>
RECIPIENT='MDB'
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
ConnectionFactoryJndiName
</activation-config-property-name>
<activation-config-property-value>
java:comp/resource/oc4jjms/jms/QueueConnectionFactory
</activation-config-property-value>
</activation-config-property>
</activation-config>
... ...
</ejb-jar>

Note that the setup here is sufficient for our MDB to use the JMS resource adapter to access the OC4J JMS queue. The OC4J-specific descriptor orion-ejb-jar.xml would be very simple, and there is no need for resource/name mappings.

Client setup

Similarly to the MDB configuration, the web client also might require a two-part configuration to access the EIS via a resource adapter. For our application, we choose to use logical JNDI names in the client to access the resource adapter; and therefore, we need to add the name mappings in the OC4J-specific web descriptor orion-web.xml.


Here is the client JSP code that accesses the OC4J JMS via the resource adapter. Note how the variables QUEUE_NAME and QUEUE_CONNECTION_FACTORY are used in the JNDI lookups. Both are logical JNDI names, and use the outbound connectivity provided by the JMS resource adapter in this case.

<%@ page import="javax.jms.*, javax.naming.*, java.util.*" %>
                        
<%
String QUEUE_NAME="jms/theQueue";
String QUEUE_CONNECTION_FACTORY="jms/theQCF";
try
{
InitialContext ic = new InitialContext();
QueueConnectionFactory connectionFactory = (QueueConnectionFactory)
ic.lookup("java:comp/env/" + QUEUE_CONNECTION_FACTORY);
QueueConnection connection = connectionFactory.createQueueConnection();
connection.start();
QueueSession queueSession =
connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ic.lookup("java:comp/env/" + QUEUE_NAME);
System.out.println("Queue: " + queue);
QueueSender sender = queueSession.createSender(queue);
System.out.println("creating Message: " + queue);
Message message = queueSession.createMessage();
System.out.println("Message created");
message.setJMSType("theMessage");
message.setLongProperty("time", System.currentTimeMillis());
message.setStringProperty("id", request.getParameter("id"));
message.setStringProperty("oamount", request.getParameter("oamount"));
message.setStringProperty("message", request.getParameter("message"));
message.setStringProperty("RECIPIENT", "MDB");
System.out.println("Sending message ...");
sender.send(message);
System.out.println("Message sent");
... ...
%>

Now we define the logical JNDI names for the queue and connection factory in the standard web deployment descriptor web.xml:

 

<web-app>
                        
... ...
<resource-ref>
<description>The queue to which the mesg is sent</description>
<res-ref-name>jms/theQueue</res-ref-name>
<res-type>javax.jms.Queue</res-type>
</resource-ref>
<resource-ref>
<description>
The Factory used to produce connections to the queue
</description>
<res-ref-name>jms/theQCF</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
... ...
</web-app>

We then provide the name mappings in the OC4J-specific web descriptor orion-web.xml:

<orion-web-app ... ...>
                        
... ...
<resource-ref-mapping name="jms/theQueue" location="genericjms/MyQueue1"/>
<resource-ref-mapping name="jms/theQCF" location="genericjms/MyQCF"/>
... ...
</orion-web-app>


Note that the values of the �location� attribute here correspond to those defined in oc4j-ra.xml and oc4j-connectors.xml.


4. Conclusion

The J2EE Connector Architecture (JCA) defines standard contracts for integration of application servers with heterogeneous Enterprise Information Systems (EISs). JCA 1.5 is an important step beyond JCA 1.0 in supporting the complete range of EISs. With the more capable resource adapters supporting asynchronous message providers, you can now build a larger variety of JCA applications. In this article, we have walked through the important-yet-simple steps of running a JCA 1.5 application in OC4J, which uses an MDB to access a JMS provider to monitor the inventory level and send out e-mail notifications when the inventory level drops below certain threshold.

With full JCA 1.5 support, OC4J 10.1.3 greatly simplifies the task of integrating heterogeneous systems for your business applications.