Tuesday, February 13, 2007

Using Resource Adapters outside of EE containers

Java EE (J2EE) application servers usually are the ideal choice to host your back-end applications, especially if your applications require transactions, security or state management. Running in an application server, your application can easily connect to external systems provided by Resource Adapters. E.g. if your application would have to connect to a CRM (e.g. PeopleSoft), to an ERP (e.g. SAP) or to a system such as JMS, or even to a database (e.g. Oracle), it would use a Resource Adapter. Through this, the application would use advanced features such as connection pooling, connection failure detection and recovery, transactions, security propagation, etc.

As said, EE or J2EE application servers are ideal containers for hosting business side logic. But it's not the best choice in literally every situation. There are cases where you would prefer to write your application as a stand alone Java program. There's nothing strange about that: you should always use the best tools for the job at hand, and no single tool is best in all situations.

Now say that you need to write a stand-alone Java application, and in that application you would need to connect to an external system. Wouldn't it be nice to be able to use an off-the-shelf Resource Adapter for this connectivity, so that you would not have to hand-code features such as connection pooling, connection failure detection and recovery etc? As I will show, this is not that difficult.

Hacking a Resource Adapter

Resource Adapters are distributed in RAR files, i.e. a file with a .rar extension. A RAR is nothing more than a ZIP file. When you open up a RAR, you will see a bunch of jars and a descriptor file with the name ra.xml. In a nutshell, this is what you need to do to use a Resource Adapter:

  1. Add the jars to your application's classpath
  2. Instantiate the Resource Adapter classes (you can find out which ones by examining the ra.xml)
  3. Configure the Resource Adapter by calling a few setter methods (you can find which ones by examining the ra.xml)
  4. Activate the Resource Adapter (you can find out how by reading the Java Connector API specification, or just read on)

These four steps are basically what the application server does when it loads a Resource Adapter. There's a lot of logic involved in this, but in a stand-alone Java application we can take a lot of short-cuts so that the logic that we need to write is not too involved.

There's quite a big difference in how a Resource Adapter is used to provide outbound connectivity versus inbound connectivity. With outbound connectivity, I mean a situation where an application obtains a connection to an external system and reads or writes data to it. With inbound I mean a situation where the Resource Adapter listens for events from the external system and calls into your application when such an event occurs.

As an example we will use the JMSJCA resource adapter. This is an open-source adapter for JMS connectivity to various JMS servers.

Outbound connectivity

In a nutshell what we need to do is instantiate the Resource Adapter class, configure it, instanatiate a Managed Connection Factory, and from that obtain the application-facing connection factory.

When you open up the ra.xml file, you can find out class implements the ResourceAdapter interface:

    <resourceadapter>
        <resourceadapter-class>com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter</resourceadapter-class>

The Resource Adapter class has per the specification a no-args constructor and should implement the javax.resource.spi.ResourceAdapter interface. You could instantiate the ResourceAdapter simply by doing this:

    com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter ra = new com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter();

The drawback of this is that, although the chances of that being small, if the classname changes in future versions of the Resource Adapter, your application would no longer compile. A better approach would be to read the classname dynamically from the ra.xml. I'll leave that as "an excercise for the reader".

The Resource Adapter is a Java Bean with getters and setters. This is how you can configure the Resource Adapter. For example, we could set the connection URL as follows:

    ra.setConnectionURL("stcms://localhost:18007");

Next, we need to instantiate a  ManagedConnectionFactory.  Again from  the ra.xml, you can find the classname:

    <outbound-resourceadapter>
<connection-definition>
<managedconnectionfactory-class>com.stc.jmsjca.core.XMCFUnifiedXA</managedconnectionfactory-class>
 

Again, the ManagedConnectionFactory must have a no-arg constructor and is a Java bean, so you can simply instantiate and configure one as follows:

    com.stc.jmsjca.core.XMCFUnifiedXA mcf = new com.stc.jmsjca.core.XMCFUnifiedXA();
mcf.setUserName("Administrator");
mcf.setPassword("STC");

Next, you may need to associate the newly created ManagedConnectionFactory with the ResourceAdapter. Those that require this association (most likely all of them), implement the javax.resource.spi.ResourceAdapterAssociation interface. This leads to the following code:

    mcf.setResourceAdapter(ra);

Lastly, you create the application-facing connection factory. In case of JMS, this is a javax.jmx.ConnectionFactory. You can find evidence of this in the ra.xml:

   <outbound-resourceadapter>
<connection-definition>
<managedconnectionfactory-class>com.stc.jmsjca.core.XMCFUnifiedXA</managedconnectionfactory-class>
...
<connectionfactory-interface>javax.jms.ConnectionFactory</connectionfactory-interface>
<connectionfactory-impl-class>com.stc.jmsjca.core.JConnectionFactoryXA</connectionfactory-impl-class>
<connection-interface>javax.jms.Connection</connection-interface>
</connection-definition>

This leads to the following code:

javax.jms.ConnectionFactory f = (javax.jms.ConnectionFactory) mcf.createConnectionFactory();

Now, putting it all together, this is what you would need to create a JMS connection factory from the JMSJCA Resource Adapter:

    com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter ra = new com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter();
com.stc.jmsjca.core.XMCFUnifiedXA mcf = new com.stc.jmsjca.core.XMCFUnifiedXA();
ra.setConnectionURL("stcms://localhost:18007");
ra.setUserName("Administrator");
ra.setPassword("STC");
ra.setOptions("JMSJCA.NoXA=true");
mcf.setResourceAdapter(ra);
javax.jms.ConnectionFactory f = (javax.jms.ConnectionFactory) mcf.createConnectionFactory();

And that's all there's to it

As I mentioned, one of the advantages of using a Resource Adapter over using a client runtime directly, is that an Resource Adapter typically provides connection pooling and other nifty features. JMSJCA for instance provides a powerful and configurable connection manager with blocking behavior, time-out behavior, connection failure detection, etc. It even enlists the connection in the transaction if it detects that there is a transaction active when the connection is created.

Not all resource adapters will provide such a comprehensive connection manager: check the documentation of the resource adapter that you're planning to use. If it doesn't provide a connection manager to your liking, you can provide your own connection manager. If you need to write one, take a look at the one in JMSJCA: you may want to use it as a starting point.

Inbound connectivity

Inbound connectivity is where a resource adapter is used to receive messages from an external system; the resource adapter delivers these messages to a Message Driven Bean. In the case of JMS, this is javax.jms.MessageListener, with its void onMessage(javax.jms.Message) method. For other types of resource adapers, you will find other Message Driven Bean interfaces. Look in the ra.xml to find out which one.

Seting up inbound connectivity is a bit more involved than outbound connectivity. That is because with inbound, you need to explain to Resource Adapter how it should obtain a new instance of the Message Driven Bean, and how to obtain a thread that will call the onMessage() method or equivalent method.

We start with instantiating and configuring a ResourceAdapter object; the class can be found in ra.xml as I showed in the Outbound Connectivity section:

    com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter ra = new com.stc.jmsjca.unifiedjms.RAUnifiedResourceAdapter();
ra.setConnectionURL("stcms://localhost:18007");
ra.setUserName("Administrator");
ra.setPassword("STC")

Next, we need to call start() on the ResourceAdapter object. This method takes a javax.resource.spi.BootstrapContext object. You need to provide an implementation for this class; the most important method that you need to implement is the public javax.resource.spi.work.WorkManager getWorkManager() method. As you can see, this method should return a WorkManager object. This class has a number of methods that provide access to a threadpool. It would help at this point if you have a bit of knowledge of the internals of the Resource Adapter that you're trying to work with: some Adapters don't use a WorkManager, and the ones that do, typically only use one of the methods on the WorkManager object. For instance, here's a WorkManager that could be used with JMSJCA:

public class XWorkManager implements WorkManager {
private java.util.concurrent.Executor mPool;

public XWorkManager(int poolsize) {
mPool = new PooledExecutor(new LinkedQueue(), poolsize);
}

public void scheduleWork(Work work) throws WorkException {
try {
mPool.execute(work);
} catch (InterruptedException e) {
throw new WorkException(e);
}
}

// other methods just throw an exception
}

Fortunately we can make use of the java.util.concurrent tools introduced in JDK 5.0 for a threadpool. Next, we need to provide an implementation for javax.resource.spi.endpoint.MessageEndpointFactory. This is an interface with only two methods. One is there to indicate if the message delivery should be transacted, and the other one to is there to create a MessageEndpoint. This is a class that is a proxy around the Message Driven Bean. This proxy should implement the onMessage() or equivalent method which should simply delegate to the MessageListener or equivalent object in your application. The proxy should also implement three additional methods:

    void afterDelivery()
void beforeDelivery(Method method)
void release()

The beforeDelivery() and afterDelivery() methods are called just before the Resource Adapter calls the onMessage() or equivalent method. You could start and commit a transaction in these methods; if you're not using transactions, you can just leave these methods unimplemented. The release() method is called by the Resource Adapter when it's done using a Message Driven Bean. If you don't implement some sort of pooling mechanism for Message Driven Beans, and you probably won't, you can leave this method empty as well.

Now that you have implementations for the BootstrapContext, WorkManager, MessageEndpointFactory, and MessageEndpoint, you can finally tell the Resource Adapter to start delivering messages to your Message Driven Bean. You do that by calling the void endpointActivation(javax.resource.spi.endpoint.MessageEndpointFactory, javax.resource.spi.ActivationSpec) method on the ResourceAdapter. The ActivationSpec object is implemented by the Resource Adapter; you can find the classname in the ra.xml file. It is a Java bean that is used to configure the message delivery. For instance, in the case of JMS, you specify from which queue or topic to get messages.

As you can see, the inbound connectivity case is not as straight forward as the outbound connectivity case, but still very much doable.

Real examples

For a full sample source listing, take a look at the test suite in JMSJCA You can also look at the JMSBC as part of the Open JBI Components project. This Binding Component uses the JMSJCA Resource Adapter as described in this blog entry. By doing so, a lot of development effort was saved dealing with all the idiosyncracies of various JMS server implementations, connection management, etc.

9 comments:

sairam said...

thanks for the updates on jcaps.
Request for your help for the following problem.
http://forum.java.sun.com/thread.jspa?threadID=5139511

Frank Kieviet said...

Sorry, I cannot be of much use with the problem at http://forum.java.sun.com/thread.jspa?threadID=5139511
Frank

vijay said...

Frank,
I tried playing with the JMSJCA and tried to configure with SonicMQ but little success using JCAPS. Do you think I can make calls from the JMSJCA from a JCD in JavaCAPS and ofcourse running on GlassFish?

Frank Kieviet said...

BTW, for 5.1.3 you only need ESR 107235
Frank

philipb said...

hi, I've been trying to use this in a standalone program. how do I use it for inbound messaging? Thanks, Philip

Frank Kieviet said...

Hi Philip,


For inbound messaging you need to implement a few JCA container classes. I'm adding this functionality to JMSJCA, and intend to blog about it; it should be available soon.

Frank

Neal said...

Why do we need Resource Adapters for JMS when we have spring JMS?

Arie van der Veek said...

Is there any way to use the ra.xml file to configure the resource adaptor on standalone programs or does one need to use the setters?

We have tried changing the ra.xml, but it looks like the ra.xml file is not used in any way.

Frank Kieviet said...

Re Arie:

In standalone programs, you do need to use the setters: there's no container that will read the ra.xml for you and call the setters.

Frank