Saturday, August 07, 2010

JBossESB - Setting up Long Running Synchronous Orchestrations

A client wanted to invoke a BPM Orchestration synchronously via a Web Service using JBossESB and jBPM within the JBoss SOA-Platform.  Sure, it's no problem usually. There are a couple of excellent articles explaining how jBPM and JBossESB work together on InfoQ and mastertheboss.com.
The difficulty in this client's case was that some of the services executed within the orchestration process take an exceptionally long amount of time to complete and signal back to the process instance.  The error that constantly cropped up was
INFO  [ServiceInvoker] Unresponsive EPR: JMSEpr ... ....
while executing this long running instance. Although the process orchestration would continue the webservice client would receive a timeout. Worse yet, the JBossESB would not have a reply for the RequestReply and would attempt to redeliver.
After creating a Sleeper Action that would just block the thread and wait X number of seconds, in our case 10 minutes, I figured out that you can extend the timeout of the web services by adding the following property within your jbossesb-properties.xml file located within your deploy/jbossesb.sar directory.  Here's the configuration snippet:
<properties name="transports" depends="core">
<property name="org.jboss.soa.esb.mail.smtp.host" value="localhost"/>
 <property name="org.jboss.soa.esb.mail.smtp.user" value="jbossesb"/>
 <property name="org.jboss.soa.esb.mail.smtp.password" value=""/>
 <property name="org.jboss.soa.esb.mail.smtp.port" value="25"/>
 <property name="org.jboss.soa.esb.mail.smtp.auth" value="true"/>
 <property name="org.jboss.soa.esb.ftp.localdir" value="/tmp"/>
 <property name="org.jboss.soa.esb.ftp.remotedir" value="/tmp"/>
 <property name="org.jboss.soa.esb.jms.connectionPool" value="20"/>
 <property name="org.jboss.soa.esb.jms.sessionSleep" value="30"/>
<property name="org.jboss.soa.esb.ws.timeout" value="600000"/>
</properties>
The org.jboss.soa.esb.ws.timeout is set in milliseconds. If you have a long running synchronous process you'll definitely want to extend this timeout or modify your architecture to use asynchronous processing.  That's about it.
What happens is that a synchronous request will actually invoke the class org.jboss.internal.soa.esb.webservice.RequestResponseBaseWebService.  Look at the code you'll see there's a timeout of 30 seconds
public class RequestResponseBaseWebService extends BaseWebService
{
    private static final long DEFAULT_TIMEOUT = 30000L ;
    private static final long TIMEOUT ;
    ...
    ...
    static
    {
        final PropertyManager propertyManager = ModulePropertyManager.
          getPropertyManager(
           ModulePropertyManager.TRANSPORTS_MODULE) ;
        final String timeoutVal = propertyManager.getProperty(
           Environment.WS_TIMEOUT);
        long timeout = DEFAULT_TIMEOUT ;
        if (timeoutVal != null)
        {
            try
            {
                timeout = Long.parseLong(timeoutVal) ;
            }
            catch (final NumberFormatException nfe)
            {
                LOGGER.warn("Failed to parse specified timeout: " + timeoutVal, nfe) ;
            }
        }
        TIMEOUT = timeout ;
    }
The timeout is set via the Arjuna properties manager which happens to be looking at jbosseb-properties.xml.
SleeperAction - If you do need to set up your own artificial delays for testing purposes here is a copy of my source code for a Sleeper Action.
package org.jboss.soa.esb.samples.quickstart.actions;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.jboss.soa.esb.actions.AbstractActionLifecycle;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.message.Message;

public class SleeperAction extends AbstractActionLifecycle {

    Log log = LogFactory.getLog( SleeperAction.class );

    private static final String PARAM_SLEEP_MILLIS = "sleepInMillis";

    private long sleepInMillis = 1000L;
    protected ConfigTree _configTree;

    public SleeperAction(ConfigTree tree)
    {
        _configTree = tree;
        sleepInMillis = _configTree.getLongAttribute( PARAM_SLEEP_MILLIS, 1000 );
    }

    public Message process(Message message)
    {
        log.debug( "******** Received message.  Will sleep for "
           + sleepInMillis + " milliseconds *****" );

        try {
            Thread.sleep( sleepInMillis );
        } catch ( InterruptedException e ) {
            log.warn( "Sleep process interrupted: " , e );
        }

        log.debug( "******** Completed sleeping "
          + sleepInMillis + " milliseconds *****" );

        message.getBody().add("reply", "I waited "
          + sleepInMillis + " milliseconds for you!" );
        message.getBody().add( "***** I waited " + sleepInMillis
           + " milliseconds for you *****" );

        log.debug( message );
        return message;
    }
}

No comments:

Post a Comment