JCA 1.5, Part 3: Message inflow

Message-driven beans take on a new life

In this final installment of his three-part series on the latest version of the J2EE™ Connector Architecture (JCA), David Currie introduces the new message-inflow contract. This enhancement enables a resource adapter to invoke an application asynchronously through a message-driven bean. This article is essential reading if you want use this functionality in an existing resource adapter or are considering writing a new JCA 1.5 resource adapter. It should also be of interest if you write applications that use resource adapters and want to know more about what goes on behind the scenes.

David Currie (david_currie@uk.ibm.com), Staff Software Engineer, IBM UK Ltd

Photo of David CurrieDavid Currie recently joined the IBM Software Services for WebSphere organization based in Hursley, UK. Prior to that, he worked on the development of WebSphere Application Server, initially in the area of transactions and, most recently, designing and implementing JCA resource adapters for the messaging support. You can reach David at david_currie@uk.ibm.com.



07 June 2005

Also available in Russian Japanese

Part 1 of this three-part series on new features in JCA 1.5, the latest version of the J2EE Connector Architecture, introduced optimizations that can make outbound resource adapters go faster and additions that let resource adapters take on a new life of their own. Part 2 looked at JCA's new work-management contract, which lets a resource adapter create timers for delayed or periodic execution of work and lets it perform processing using application-server threads. In this article, I'll show how the new message-inflow contract removes the complexities of the old Java Message Service (JMS) Application Server Facilities and lets message-driven beans take on a whole new life. I'll also show how administered objects let JavaBeans defined by the resource adapter be configured by an administrator and bound into the application's namespace.

EJB 2.0 message-driven beans

In the days of J2EE 1.3 and EJB 2.0, message-driven beans (MDBs) had a fairly limited role. They let your application asynchronously receive messages delivered to a JMS destination. Your MDB needed to implement the javax.jms.MessageListener, shown in Listing 1:

Listing 1. The javax.jms.MessageListener interface
public interface MessageListener {

    void onMessage(Message message);

}

MDBs also needed to implement the MessageDrivenBean interface. As a result, a typical class would look something like ExampleMdb in Listing 2:

Listing 2. Example EJB 2.0 MDB
public class ExampleMdb implements MessageDrivenBean, MessageListener {

    public void ejbCreate() throws CreateException { ... }
    public void ejbRemove() { ... }
    public void setMessageDrivenContext(MessageDrivenContext ctx) { ... }
    public void onMessage(Message message) {

        if (msg instanceof TextMessage) {
            String text = ((TextMessage) message).getText();
            InitialContext context = new InitialContext();
            ExampleHome home = (ExampleHome)context.lookup("java:comp/env/ejb/Example");
            Example bean = home.create();
            bean.operation(text);
        }

    }

}

(In all the examples in this article series, I've excluded exception-processing logic to aid clarity.)

If you followed the EJB 2.0 specification's advice, as in Listing 2, the onMessage method was responsible for unpacking the message's contents and would then invoke some other EJB to perform the actual business logic.

Now don't get me wrong -- EJB 2.0 MDBs certainly had their good points. For starters, because an MDB wasn't invoked by a client, you didn't need to write a home, remote, or local interface. Also -- one of the major benefits -- multiple instances could operate in parallel. In the absence of MDBs, your application could have polled a destination to receive messages synchronously but -- without the ability to spin off work on to another thread -- it would have had to process the first message before moving on to the next.

Like other EJBs, MDBs could use bean- or container-managed transactions. The latter had a slight twist for MDBs though. First, you were permitted to use only two of the transaction attributes: Required and NotSupported. This was entirely reasonable. Without a client to invoke the MDB, there's no transaction to inherit, so all you needed was the ability to say whether or not the bean should run in an transaction. Second, and more important, if you specified a transaction attribute of Required, then the message delivered to the MDB was received as part of that transaction. If something went wrong and the transaction rolled back, the message was placed back on the destination for reprocessing.

As you can see in Listing 3, the fact that an MDB was only used for JMS was also reflected in the deployment descriptor:

Listing 3. EJB 2.0 deployment descriptor
<ejb-jar>
  <enterprise-beans>
    <message-driven>
      <ejb-name>Example MDB</ejb-name>
      <ejb-class>example.ExampleMDB</ejb-class>
      <transaction-type>Bean</transaction-type>
      <acknowledge-mode>Auto-acknowledge</acknowledge-mode>
      <message-driven-destination>
        <destination-type>javax.jms.Topic</destination-type>
        <subscription-durability>Durable</subscription-durability>
      </message-driven-destination>
      <message-selector>
        JMSType = 'car' AND color = 'red'
      </message-selector>
    </message-driven>
  </enterprise-beans>
</ejb-jar>

You can see in Listing 3 that some elements in the deployment descriptor correspond to the JMS concepts of acknowledge mode, subscription durability, and message selector. Another element gives the type of the message destination, which had to be either javax.jms.Queue or javax.jms.Topic.


EJB 2.1 message-driven beans

So how have things changed in J2EE 1.4 with EJB 2.1? Well, there is one big difference: An MDB can now implement any interface. That's right -- any interface. For example, the JCA Common Client Interface (CCI) defines the interface shown in Listing 4, which should be implemented by an MDB wishing to be driven asynchronously by a CCI connector:

Listing 4. The javax.resource.cci.MessageListener interface
public interface MessageListener {

    Record onMessage(Record inputData)
            throws ResourceException;

}

The CCI interface demonstrates some of the benefits of this new flexibility. First, an MDB doesn't need to be passed a JMS message. In the interface in Listing 4, a Record object is passed in. And the arrival of a JMS message at a destination needn't be what triggers the invocation of the method. The JCA specification doesn't place any restriction on the circumstances under which an MDB will be driven. It could be the result of the resource adapter receiving some external stimulus from a back-end system. Or it could just be some internal event, possibly even timer driven. Second, as Listing 4 shows, the invoked method can also have a return parameter. In this case a second Record object is passed back but, as the first method on the ExampleListener interface in Listing 5 shows, the types need not be the same:

Listing 5. An example listener interface
public interface ExampleListener {

    ExampleOutput process(ExampleInput input);

    String getData();

    void request(int id, ExampleRequest request);

}

You'd typically use the return parameter from the method to provide a response to the originator of the event that caused the method to be invoked, whether it is the resource adapter or some other system. However, as the second method on the ExampleListener interface illustrates, you could actually call the MDB without any parameters and use it simply to retrieve some value. The third method demonstrates that the number of parameters the method can take is unrestricted. This means that the resource adapter could, for example, parse the initiating event into multiple values to be passed to the MDB. As you can also see, the method parameters can be primitive types or objects. Finally, it would be valid to use the ExampleListener interface even though it has multiple methods. It would then be up to the resource adapter to decide which method to invoke, depending on the event.

With all the JMS-related restrictions gone from the MDB interface, where does this leave the deployment descriptor? Listing 6 shows an example of an EJB 2.1 deployment descriptor for an MDB implementing the ExampleListener interface:

Listing 6. EJB 2.1 deployment descriptor
<ejb-jar>
  <enterprise-beans>
    <message-driven>
      <ejb-name>Example MDB</ejb-name>
      <ejb-class>example.ExampleMdb</ejb-class>
      <messaging-type>example.ExampleListener</messaging-type>
      <transaction-type>Bean</transaction-type>
      <activation-config>
        <activation-config-property>
          <activation-config-property-name>
            HostName
          </activation-config-property-name>
          <activation-config-property-value>
            example.com
          </activation-config-property-value>
        </activation-config-property>
        <activation-config-property>
          <activation-config-property-name>
            DefaultId
          </activation-config-property-name>
          <activation-config-property-value>
            2001
          </activation-config-property-value>
        </activation-config-property>
      </activation-config>
    </message-driven>
  </enterprise-beans>
</ejb-jar>

You can see in Listing 6 that the messaging-type element contains the class name of the interface that the MDB implements. If this element is missing, then the old value of javax.jms.MessageListener is assumed. The JMS-specific elements have been replaced by a set of generic name-value pairs known as the activation-configuration properties. In Listing 6 you can see that values of example.com and 2001 are specified for the HostName and DefaultId properties, respectively.

Being able to write an MDB that implements an arbitrary interface sounds too good to be true -- and indeed it is. The catch is, perhaps not surprisingly, that you need to find a resource adapter prepared to call that interface. A resource adapter indicates the interfaces (and there can be more than one) that it is prepared to support using the messagelistener-type element in its deployment descriptor, as shown in Listing 7:

Listing 7. Deployment descriptor for inbound resource adapter
<connector>

 <display-name>Example Inbound Resource Adapter</display-name>
 <vendor-name>Example COM</vendor-name>
 <eis-type>Example EIS</eis-type>
 <resourceadapter-version>1.0</resourceadapter-version>

 <resourceadapter>

  <resourceadapter-class>
   example.ExampleRaImpl
  </resourceadapter-class>

  <inbound-resourceadapter>
   <messageadapter>
    <messagelistener>
     <messagelistener-type>example.ExampleListener</messagelistener-type>
     <activationspec>
      <activationspec-class>example.ExampleActivationSpecImpl</activationspec-class>
      <required-config-property>
       <config-property-name>HostName</config-property-name>
      </required-config-property>
      <required-config-property>
       <config-property-name>PortNumber</config-property-name>
      </required-config-property>
     </activationspec>
    </messagelistener>
   </messageadapter>
  </inbound-resourceadapter>

 </resourceadapter>
</connector>

It's worth looking at this example deployment descriptor in some detail. It begins with some pleasantries: the display name, the name of the vendor, the type of enterprise information system (EIS) to which the resource adapter connects, and the version of the adapter. These are followed by a single resourceadapter element, the first subelement of which contains the name of the adapter's implementation of the ResourceAdapter interface that you saw back in Part 1 of this series. This is followed by the inbound-resourceadapter element which, in turn, can contain multiple messageadapter instances. Each messageadapter is associated with a single messagelistener-type. So you can write a single inbound resource adapter that supports multiple interfaces by having several messageadapter elements. You must, however, ensure that each messageadapter specifies a different interface.

Each messageadapter also contains the name of a class implementing the ActivationSpec interface. In the next section I'll show you how this class is used to contain configuration information for each deployed MDB.


Deploying an MDB

As you saw in the previous section, each MDB interface that a resource adapter supports gives the name of a class implementing the ActivationSpec interface, shown in Listing 8:

Listing 8. The javax.resource.spi.endpoint.ActivationSpec interface
public interface ActivationSpec extends ResourceAdapterAssociation {

    void validate() throws InvalidPropertyException;

}

This implementation class follows the JavaBean standard and can define a number of properties through appropriately named getter and setter methods. This class is used when you deploy an MDB into an application server as follows:

  1. The application server determines the interface that your MDB implements by looking at the contents of the messaging-type element in the MDB's deployment descriptor.
  2. The application server then locates the deployed resource adapters that support that interface, and you select the one against which you wish to deploy the MDB.
  3. The application server creates an instance of the ActivationSpec class corresponding to the selected resource adapter and interface and then uses introspection to determine the properties of that class along with the default values.
  4. The default property values are overridden with any properties of the same name configured on the parent ResourceAdapter class.
  5. You can then choose to override any of these property values.
  6. The properties are then overridden again by any values specified in the activation-config properties of the MDB's deployment descriptor.
  7. The application server checks that all of the bean properties specified in the resource adapter's deployment descriptor as being required (in the example case, HostName and PortNumber) have had values provided.

As you can see, the properties finally used with the deployed MDB can come from a number of locations, and it's important to remember the order of precedence. In particular, note that the MDB's deployment descriptor does not need to contain all of the properties that the resource adapter requires.

Once all of the properties have been set, the application server can chose to call the validate method on the ActivationSpec, or it can defer calling this method until the application is started. The resource adapter might use this method to check that numeric properties are within the acceptable range or to ensure that a string property representing a value from an enumeration is valid. The resource adapter should also check that the final set of properties is consistent; for example, the values of certain properties might be restricted or required depending on the values of other properties. An InvalidPropertyException containing an array of the invalid properties should be thrown if one or more checks fail.

Successful validation doesn't necessarily imply that the deployed MDB will start successfully. The resource adapter is permitted to perform only static checks, that is, those that can be performed without connecting to the back-end system. Only when the application starts can the resource adapter verify whether properties such as host names and port numbers are correct.


MDB lifecycle management

Once you've successfully deployed an application containing an MDB into an application server, the next step is to start the application. The application server notifies the resource adapter about the lifecycle events of a particular MDB (referred to as an endpoint by the specification) using the two methods on the ResourceAdapter interface shown in Listing 9:

Listing 9. The endpoint lifecycle methods on the ResourceAdapter interface
public interface ResourceAdapter {

    void endpointActivation(MessageEndpointFactory endpointFactory,
            ActivationSpec spec) throws ResourceException;

    void endpointDeactivation(MessageEndpointFactory endpointFactory,
            ActivationSpec spec);

    ...

}

When you start the application, the endpointActivation method is invoked once for each deployed MDB. The first parameter (which I'll cover in more detail in the next section) is a factory class for creating endpoint instances. The second parameter on the method is the ActivationSpec that you configured in the previous section. If it has not done so already, this is the point at which a resource adapter might typically make some form of remote connection to a back-end system using the information from the ActivationSpec. If, in the process of doing this, the resource adapter determines that the configured information is incorrect, then it should throw a NotSupportedException.

Note that the resource adapter should not block in this method. Once it has determined the configuration is valid it should return immediately. If further processing is required (for example to poll the connection periodically for new events) then the resource adapter should use the WorkManager interface that I introduced in Part 2 in order to schedule work on another thread.

A corresponding endpointDeactivation method is called when the application containing the MDB is stopped or during an orderly shutdown of the application server. The parameters passed to this method are exactly the same objects passed on endpointActivation. Indeed, because the JCA specification mandates that a new instance of MessageEndpointFactory is created for each endpoint activation, the object passed on deactivation can be used as a key to correlate between the two. For example, resources created on activation could be placed in a map keyed off MessageEndpointFactory and then retrieved on deactivation in order to be cleaned up.


Invoking an MDB

You now have everything in place for the moment when the resource adapter actually invokes an MDB. In the days of J2EE 1.3 this step would have used the rather convoluted Application Server Facilities that form part of the JMS specification. The interactions were complex and, in places, badly specified, leading to different vendors interpreting the requirements in different ways. Fortunately for you, not only has the JCA specification enabled the MDB to implement any interface, but it has also simplified things greatly in the process.

Listing 10 shows the MessageEndpointFactory interface implemented by the object passed to the endpointActivation method:

Listing 10. The javax.resource.spi.endpoint.MessageEndpointFactory interface
public interface MessageEndpointFactory {

    boolean isDeliveryTransacted(Method method) throws NoSuchMethodException

    MessageEndpoint createEndpoint(XAResource xaResource) throws UnavailableException;

}

The isDeliveryTransacted method lets the resource adapter determine whether the associated MDB is expecting the given method to be invoked in a transaction. This returns true if the MDB is using container-managed transactions and the method in question has a transaction attribute of Required. If the transaction attribute takes the other allowed value of NotSupported, or the MDB is using bean-managed transactions, then it returns false.

I'll cover transactions in the next section so, for the moment, assume that isDeliveryTransacted returned false (or, alternatively, that the resource adapter does not support transactions). This means that you can pass null as the XAResource parameter when you call the createEndpoint method. This method returns an object implementing the MessageEndpoint interface, shown in Listing 11:

Listing 11. The javax.resource.spi.endpoint.MessageEndpoint interface
public interface MessageEndpoint {

    void release();

    void beforeDelivery(Method method)
            throws NoSuchMethodException, ResourceException

    void afterDelivery() throws ResourceException;

}

The object returned also implements the interface that the MDB declared in its deployment descriptor. In the simplest case, the resource adapter is therefore able to cast the MessageEndpoint to the necessary interface and then invoke the desired method.

The object returned from createEndpoint is obviously not an instance of the class the application developer implemented, because that class does not implement the MessageEndpoint interface. Instead, it is a proxy created by the application server. The application server might have used the dynamic proxy support introduced by Java 1.4 to create the object on the fly, or it might have generated the necessary class when the application was deployed. The container uses the proxy to wrap the actual endpoint in order to provide services such as transactions and security. Compare the proxy to a reference to a stateless session bean's local interface. In the session-bean case you can only invoke methods declared on the local interface. Similarly, for the proxy you can only invoke methods on the interface declared in the MDB's deployment descriptor.

The resource adapter can choose to hold on to an endpoint for use on subsequent invocations. Alternatively, the resource adapter can call release when it has finished and create another endpoint for the next invocation. Typically, the application server keeps a pool of endpoints, so the second option is likely to provide for greater reuse between multiple threads.

I said that this represented the simple case. The JCA specification refers to this as Option A message delivery. As an example of when the alternative Option B message delivery might be required, imagine that the resource adapter is part of a messaging system that transports serialized Java objects on behalf of applications. The MDB method that you wish to invoke takes the deserialized object as a parameter. Unfortunately, the class corresponding to the serialized object is part of the J2EE application and is therefore on the application's classpath, not the resource adapter's. Option B solves this problem neatly with the beforeDelivery and afterDelivery methods. Calling beforeDelivery performs early some of the container operations that would normally happen between invoking the proxy and calling the method on the actual endpoint. These operations include associating the application classloader with the thread. Having called beforeDelivery, the resource adapter can therefore now deserialize the object using the thread classloader and pass it to the required method on the MDB interface.

The container knows that it has already performed some of the required operations on this endpoint proxy and will skip those when the actual MDB method is invoked. The method called must, however, match the Method object passed on beforeDelivery else a RuntimeException will be thrown. After calling the method, the resource adapter must call afterDelivery to perform the corresponding container operations that should happen after a method has been invoked. This allows, for example, the resource adapter to serialize an object returned from the method, again using the application's classloader.

Figure 1 shows a side-by-side comparison of the two message-delivery options.

Figure 1. Message-delivery options
Delivery options

For Option A you can see that the preinvoke and postinvoke logic is performed as part of the single method invocation, whereas for Option B they are split out and driven by beforeDelivery and afterDelivery, respectively.


MDBs and transactions

If the resource adapter supports XA (global) transactions, and isDeliveryTransacted indicates that the MDB is expecting the container to start one, then the resource adapter should pass an implementation of the XAResource interface in when calling createEndpoint. XA transactions are a complex subject, and specifics other than those that relate to JCA are beyond this article's scope. For more details refer to the Java Transaction API specification (see Resources).

The resource adapter has two options for how to proceed. It can choose to associate the transactional work to be performed directly with its XAResource object. The resource adapter can then use Option A message delivery. When the proxy is invoked, the container enlists the XAResource in the transaction by calling start. At this point the resource adapter can associate the given Xid with the work to be performed. The proxy proceeds to invoke the endpoint instance. After the endpoint method returns, the container then completes the transaction. When the XAResource receives a call to prepare, it should ensure that it can complete the required work successfully before returning XA_OK. Finally, the XAResource receives a commit or rollback, at which point it should harden or undo the work that has been performed.

Alternatively, the resource adapter can use Option B message delivery, in which case the transaction is started as part of beforeDelivery. This lets the resource adapter retrieve the Xid from the XAResource prior to invoking the MDB method. This might be necessary if, for example, the resource adapter needs to pass the Xid to the back-end system before it can construct a parameter that is passed to the method. As you might expect, in this case the transaction is completed when afterDelivery is called.

If the application server should fail between calling prepare and commit or rollback, then transaction recovery needs to take place when the server restarts. In order to perform recovery, the application server needs to obtain an XAResource for each resource manager. The application server achieves this by writing the ActivationSpec associated with an endpoint into persistent storage before calling prepare. On recovery, the server reads back a list of ActivationSpec objects for each resource adapter that correspond to transactions it believes to be incomplete. This array of objects is then passed to the getXAResources method on the ResourceAdapter interface, shown in Listing 12:

Listing 12. The transaction recovery method on the ResourceAdapter interface
public interface ResourceAdapter {

    XAResource[] getXAResources(ActivationSpec[] specs)
            throws ResourceException;

    ...

}

The application server calls recover on each of the XAResource objects to determine the transactions that the resource manager has prepared. It then invokes commit or rollback for each transaction as appropriate.

In Part 2 of this series you saw how the ExecutionContext can be used in conjunction with the WorkManager in order to import a transaction into the application server. If an endpoint method with a transaction attribute of Required is invoked on a thread with an imported transaction, then the method will inherit that transaction. In this case, any XAResource passed on createEndpoint is not enlisted in the transaction.


Administered objects

In this final section I'll take you through a subject that, although covered in the message-inflow chapter of the JCA specification, is equally applicable to both inbound and outbound resource adapters. One of the aims of version 1.5 of the specification is to allow a JMS provider to be implemented as a JCA resource adapter. If you know JMS, you're aware that it contains two types of administrable object: connection factories and destinations. Through managed connection factories and their properties defined in the resource-adapter deployment descriptor, JCA has always provided a mechanism that enables an administrator to define connection factories so that a deployed application can look them up through the Java Naming and Directory Interface (JNDI). But JMS destinations are not factories, just objects with various properties describing the queue or topic that they represent. So how do you create a JMS destination?

The JCA 1.5 specification defines the concept of an administered object to fill this gap. A resource adapter's deployment descriptor can contain one or more adminobject elements, such as the one shown in Listing 13:

Listing 13. Deployment-descriptor snippet showing administered object
  <adminobject>
   <adminobject-interface>example.ExampleAdminObject</adminobject-interface>
   <adminobject-class>example.ExampleAdminObjectImpl</adminobject-class>
   <config-property>
    <config-property-name>Color</config-property-name>
    <config-property-type>java.lang.String</config-property-type>
     <config-property-value>Red</config-property-value>         
   </config-property>
   <config-property>
    <config-property-name>Depth</config-property-name>
    <config-property-type>java.lang.Integer</config-property-type>
   </config-property>
  </adminobject>

In this case, the application is expecting to look up from JNDI (through a resource-environment reference) an object that implements the example.ExampleAdminObject interface. The deployment descriptor provides the name of a JavaBean class that will provide the implementation of this object. It also provides a number of named properties, each of which has a type and an optional default value in much the same way as a managed connection factory. When an administrator wishes to define one of these objects, the application server's tooling should allow the administrator to select the resource adapter and administered object interface, and then prompt the administrator for a value for each property. The application server then binds a reference containing this information into JNDI. When an application performs a lookup, an instance of the administered object is created and the properties set before returning the object to the application.

You should now be able to see how a JCA resource adapter would provide the JMS destination objects by using administered objects. Don't forget, though, that you can use administered objects for any JavaBean (other than a connection factory) that you want an administrator to be able to configure and make available through JNDI.


Conclusion

In this article, I've explained how message-driven beans have changed between J2EE 1.3 and 1.4 and, in particular, how they can now implement any interface. You've seen how the JCA message-inflow contract can be used to configure a resource adapter so that it can retrieve instances of, and invoke methods on, an MDB. I've also covered in detail the options for invoking the bean method and the implications of calling transactional methods. Finally, I've taken a look at how administered objects let a resource adapter define JavaBeans to be configured and made accessible through JNDI.

This series of articles has covered some of the new features of JCA 1.5 from optimizations and lifecycle management, through work management and transactions, and ending up with the message-inflow contract. As always, the specification provides the definitive source of information and undoubtedly will continue to evolve in future versions. I hope that you have found this series useful, whether you are intending to upgrade an existing resource adapter, are writing a new one from scratch, or simply use resource adapters as part of your applications.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=84562
ArticleTitle=JCA 1.5, Part 3: Message inflow
publish-date=06072005