Using Tibco EMS with SunONE AS 7

If for some reason you are unfortunate enough to have to use SunONE Advanced Server 7 (variously known as as7se, SunONE AS7, Sun Java System Advanced Server, etc.) and have to integrate it with Tibco's JMS server (Tibco EMS), here is what you need to do.

App server configuration

If for some reason you are unfortunate enough to have to use SunONE Advanced Server 7 (variously known as as7se, SunONE AS7, Sun Java System Advanced Server, etc.) and have to integrate it with Tibco's JMS server (Tibco EMS), here is what you need to do.

App server configuration

Put the Tibco client JARs (jndi.jar, jms.jar and tibjms.jar) in $SUNONE/share/lib. Add them to the server's classpath. In the server admin console, navigate to:

  • Server/JVM Settings/Path Settings/Class path suffix

Or directly in server.xml:

<server ...>
  ...
  <java-config ... classpath-suffix="C:\Sun\AppServer7\share\lib\jndi.jar;C:\Sun\AppServer7\share\lib\jms.jar;C:\Sun\AppServer7\share\lib\tibjms.jar" ...

Configure an "external JNDI" resource for the Queue Connection Factory:

  • Server/JNDI/External Resources

and set:

  • Resource Type: javax.naming.InitialContext
  • JNDI Lookup: QueueConnectionFactory
  • Factoryclass: com.tibco.tibjms.naming.TibjmsInitialContextFactory

and add a property:

  • java.naming.provider.url = tibjmsnaming://localhost:7222

Configure an "external JNDI" resource for the Queue:

  • Resource Type: javax.jms.Queue
  • JNDI Lookup: queue.sample
  • Factoryclass: com.tibco.tibjms.naming.TibjmsInitialContextFactory

and add a property:

java.naming.provider.url = tibjmsnaming://localhost:7222

If the initial JNDI lookup requires a username/password, this can be passed in by setting two additional properties:

  • java.naming.security.principal = username
  • java.naming.security.credentials = password

These can also be defined directly in server.xml:

<external-jndi-resource 
    enabled="true" 
    jndi-name="jms/tibco/QueueConnectionFactory" 
    res-type="javax.naming.InitialContext" 
    factory-class="com.tibco.tibjms.naming.TibjmsInitialContextFactory" 
    jndi-lookup-name="QueueConnectionFactory">

  <property 
    name="java.naming.provider.url" 
    value="tibjmsnaming://localhost:7222">

</external-jndi-resource>

<external-jndi-resource 
    enabled="true" 
    jndi-name="jms/tibco/queue.sample" 
    res-type="javax.jms.Queue" 
    factory-class="com.tibco.tibjms.naming.TibjmsInitialContextFactory" 
    jndi-lookup-name="queue.sample">

  <property 
    name="java.naming.provider.url" 
    value="tibjmsnaming://localhost:7222">

</external-jndi-resource>

Additional queue (and topic) resources can be added as required.

Bean Deployment

No changes are required to MDB code to support the Tibco provider. The deployment descriptor must be configured to use the external JNDI resources defined above.

In sun-ejb-jar.xml, the mdb-connection-factory should refer to the Queue connection factory defined above. Also, a resource reference should be defined refering to the factory. For example:

<pre>
<sun-ejb-jar>
   <enterprise-beans>
      <ejb>
         <ejb-name>TestBean</ejb-name>
         <jndi-name>jms/tibco/queue.sample</jndi-name>
         <resource-ref>
            <res-ref-name>jms/QueueConnectionFactory</res-ref-name>
            <jndi-name>jms/tibco/QueueConnectionFactory</jndi-name>
         </resource-ref>
         <mdb-connection-factory>
            <jndi-name>jms/tibco/QueueConnectionFactory</jndi-name>
            <default-resource-principal>
               <name>sunone</name>
               <password>guest</password>
            </default-resource-principal>
         </mdb-connection-factory>
      </ejb>
   </enterprise-beans>
</sun-ejb-jar> 
</pre>

In ejb-jar.xml, make sure the transaction type is "Bean" (see Transactions below). The resource reference should refer to the resource defined in sun-ejb-jar.xml above. Note that the "res-ref-name" in these two files is independant of the JNDI name of the actual resource (that is, they must only match each other).

<pre>
<ejb-jar>
   <enterprise-beans>
      <message-driven>
         <ejb-name>TestBean</ejb-name>
         <ejb-class>au.com.sensis.c3.cca.connectors.ejb.TestBean</ejb-class>
         <transaction-type>Bean</transaction-type>
         <message-driven-destination>
            <destination-type>javax.jms.Queue</destination-type>
         </message-driven-destination>
         <resource-ref>
            <res-ref-name>jms/QueueConnectionFactory</res-ref-name>
            <res-type>javax.jms.QueueConnectionFactory</res-type>
            <res-auth>Container</res-auth>
         </resource-ref>
      </message-driven>
   </enterprise-beans>
</ejb-jar>
</pre>

Transactions

From: swforum.sun.com

Sun ONE Application Server 7 does not support CMT MDBs with 3rd party JMS providers. (As you note, BMT MDBs work with MQSeries and Sun ONE App Server).

As stated, bean-managed transactions appear to work fine (see sample code below).

Sample Bean

A sample implementation of a minimal message-driven bean that was tested with SunONE and Tibco EMS:

<pre>
package au.com.sensis.c3.cca.connectors.ejb;

import javax.ejb.EJBException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;


/**
 * A simple MDB that listens for messages and mentions the fact if it
 * receives one.
 *
 * @author <a href="mailto:mrowe@mojain.com">Michael Rowe </a>
 */
public class TestBean
    implements MessageDrivenBean, MessageListener
{

    protected static org.apache.commons.logging.Log _log =
        org.apache.commons.logging.LogFactory.getLog(TestBean.class);

    protected MessageDrivenContext _context;

    public TestBean()
    {
    }

    public void onMessage(Message message)
    {

        UserTransaction ut = _context.getUserTransaction();

        if (message instanceof TextMessage)
        {
            String doc = null;
            try
            {
                ut.begin();
                TextMessage text = (TextMessage) message;
                doc = text.getText();
                ut.commit();
             }
             catch (Exception e)
             {
                try
                {
                    _log.warn("rolling back transaction");
                    ut.rollback();
                }
                catch (SystemException se)
                {
                    throw new EJBException("Rollback failed: " + se.getMessage());
                }
                _log.error("Can't get text", e);
             }
            _log.info("Got message: " + doc);
        }
        else
        {
            _log.warn("Message received, but it wasn't a TextMessage");
        }

    } // onMessage()

    public void ejbCreate()
    {
    }

    public void ejbRemove()
    {
    }

    public void setMessageDrivenContext(MessageDrivenContext context)
            throws EJBException
    {
        _context = context;
    }

}

SunONE mdb concurrency notes

Default pool settings allow mutiple beans to be created. They appear to be created (constructor/ejbCreate()) on demand, but then stay in the pool.

The onMessage() calls run on multiple threads, but there appears to be reuse (i.e. threads that have finished executing an onMessage() will be used again).

Setting the pool to have a max of one bean (bean-pool/max-pool-size) causes messages to be consumed serially by one instance of the bean, in one thread (as expected).

And interestingly (although not all that relevant), all the beans seem to get constructed by the same thread, and that thread doesn't do any message servicing. But there does seem to be a 1:1 relationship between bean instances and message servicing threads (although this is with only six beans in the pool, it could be different with lots more).

If there is only one bean in the pool, it is still constructed on a different thread to that on which is processes messages.

Comments

comments powered by Disqus