jBoss jBPM meets ESB

{ 2008-07-14 } von Markus Demolsky

The combination of a process engine and an Enterprise Service Bus (ESB) is one interessting aspect of modern service oriented architectures (SOA). Therefor I (and Bernd Rücker) wrote an article in the German Java Magazin about it. To have a practical showcase the integration is shown with a small example using JBoss jBPM,

Hint: This article only looks on the example with JBoss jBPM and the Mule. The example with JBoss ESB can be found here:

Hint: You should read the Java Magazin article first (if possible) to get a better understanding of the context and the example.

Overview

The easy showcase implements the following example: Some event is generated and saved as a file (This may be an order, some incident, an alert, whatever). This file is picked up by the ESB and a new jbpm process is started. The process contains a human task, where somebody has to review the data of the event and decides, if that event can be ignored (e.g. a false alert) or if it has to be handled. In the latter case, the event is sent to an existing case management system via Web Service (could be Lotus Notes or something like that). The case management systems sends a JMS message as soon as the case is closed. This message is again picked up by the ESB and the right process instance is triggered (called "signaled" in jBPM). This is illustrated in the following picture.

The combination of jBoss jbpm and Mule ESB

Case Management System

I developed a simple Mock-Casemanagementsystem, which only make a log message, in order to illustrat its invokation.

public class DummyCaseManagement implements
  ICaseManagement{
  private Logger log = Logger.getLogger(getClass());
    public void createCase(String caseContent,long referenceId) {
        log.info("Create case for " + caseContent + 
            ". Refrenced-Id: " + referenceId);
  }
}

In our example we use Mule to expose the service as Web Service by using the Axis Transport from Mule.

<model name="CaseManagement">
  <service name="CaseManagementService">
    <inbound>
      <axis:inbound-endpoint
        address="http://localhost:8080/
        casemanagement-casemanagement"/>
    </inbound>
    <component 
      class="com.indqa.spikes.environment.
            DummyCaseManagement"/>
      <outbound>
        <outbound-pass-through-router>
          <vm:outbound-endpoint path="caseCompletionService"/>
        </outbound-pass-through-router>
     </outbound>
  </service>
</model>

As in the overview illustrated, the Case Management System sends a Message to a queue. In this example we use the internal queue system from Mule. In this case after the Case Management system was invoked, a message to the caseCompletionService Queue will be send.

Mule Configuration

The example was developed with the 2.0 Version from Mule. For our german audience, I can refer to my article on Jax Center describing new features from Mule 2. In our example we use some transport/connectors from Mule:

  • File Transport, for the first event handling
  • BPM Transport to communicate with the jBPM (start, advance processes and receive events from the jBPM)
  • VM Transport in order to illustrate messaging
  • Axis Transport in order to invoke and expose web services

In the next code listings I only describe the basic configurations from the services used by the example. A detailed description of the transports can be found on MuleSource. Please note, that the Standard configuration style from Mule 2 is Spring, so Mule users can now take full advantage from Spring and all Modules around the Spring Framework. In this example I also use Spring Modules in order to configure the jBPM:

<!-- jBPM configuration -->
<bean id="jbpmConfiguration"
    class="org.springmodules.workflow.
    jbpm31.LocalJbpmConfigurationFactoryBean"> 
    <property name="sessionFactory" ref="hbnSessionFactory" />
    <property name="configuration" value="config/jbpm.cfg.xml" /> 
    <!--  Process will be automatically deployed when 
           context starts --> 
    <property name="processDefinitionsResources">
        <list>
            <value>EsbShowcase/processdefinition.xml</value> 
        </list>
    </property>
</bean>

Map the process with Mule

With the BPM connector Mule will enabled to communicate with processes in the workflow engine. For this purpose we configure a BPM system in the Mule context with the following code snipped. The BPM endpoint will be connected with a deployed process in the jBPM. Using this endpoint in the outbound-section Mule will send Events to the process. Using this endpoint in the inbound-section, Mule receives events from the process.

<!-- JBPM Process Engine -->
<spring:bean id="jbpm" class="org.mule.transport.bpm.jbpm.Jbpm"
  destroy-method="destroy">
   <spring:property name="jbpmConfiguration" ref="jbpmConfiguration"/>
</spring:bean>

<bpm:connector name="jbpmConnector" bpms-ref="jbpm"/>
<bpm:endpoint name="esbShowCaseProcess" 
  process="EsbShowcase"/>

Now let us take a closer look to the first file event. By using the file inbound-endpoint we've a polling service listening on the test-data/in folder. The service will use a filter in order to ensure, that we only process txt files. There is also a transformer, transform the content of the File in a string. All correct files ends with the result, that a new process in the jBPM workflow engine will be created, by using the outbound-endpoint, referenced to the BPM Endpoint mapped with a deployed process in the engine.

<file:connector name="myFileConnector" streaming="false"/>
<file:file-to-string-transformer name="FileToString"/>
<bpm:endpoint name="esbShowCaseProcess" process="EsbShowcase"/>
  <service name="requestService">
    <inbound>
      <file:inbound-endpoint path="./test-data/in" 
        transformer-refs="FileToString">
        <file:filename-wildcard-filter pattern="*.txt"/>
      </file:inbound-endpoint>
    </inbound>
    <outbound>
      <outbound-pass-through-router>
        <outbound-endpoint ref="esbShowCaseProcess"/>
      </outbound-pass-through-router>
    </outbound>
</service>

Our example also has a Web Service call using the Axis transport. The event will be send by the process (showing later) which will be catched up by Mule and forwarded illustrated in the following code snipped:

<service name="fromBPM">
  <inbound>
    <inbound-endpoint ref="esbShowCaseProcess"/>
  </inbound>
  <outbound>
    <endpoint-selector-router selectorExpression="header:endpoint">
      <outbound-endpoint address="vm://openServiceCall"/>
    </endpoint-selector-router>
  </outbound>
</service>

<service name="openServiceCall">
  <inbound>
    <inbound-endpoint address="vm://openServiceCall"/>
  </inbound>
  <component class="com.indqa.spikes.environment.PrepareWsCall"/>
    <outbound>
      <outbound-pass-through-router>
       <axis:outbound-endpoint address="http://localhost:8080/
        casemanagement-casemanagement/CaseManagementService
        ?method=createCase" 
        soapAction="[methodNamespace][method]">
        <axis:soap-method
        method="qname{createCase:http://environment.spikes.indqa.com}">
          <axis:soap-parameter parameter="in0" 
            type="string" mode="IN" /> 
          <axis:soap-parameter parameter="in1" type="long" 
            mode="IN" /> 
        </axis:soap-method>
     </axis:outbound-endpoint>
   </outbound-pass-through-router>
  </outbound>
</service>    

The component com.indqa.spikes.environment.PrepareWsCall is used to prepare the parameters for web service call. The web service call the before Dummy Case Management system, which does a simple logging, and sends a message to a message queue. In our ESB we have a listen service to this message queue and advance the process if such a message is received.

<service name="caseCompletion">
   <inbound>
     <vm:inbound-endpoint path="caseCompletionService"/>
   </inbound>
   <outbound>
     <outbound-pass-through-router>
     <outbound-endpoint ref="esbShowCaseProcess"/>
    </outbound-pass-through-router>
  </outbound>
</service>

jBPM Process

The process is really straightforward at the moment. To get around using some frontend to trigger the human tasks I didn't create any task inside the TaskNode you see. If there is not Task in a TaskNode ut will not behave like a wait state and the process will just run through it. By doing so, no human can decide about which leaving transition to take, so it will be always the default one, which is "investigate" in our case. This is quite handy, so we can just start process instances, they will run to the "work on case" state and wait there. Later we can signal them again, so they will run from "work on case" to "end".In jBPM you can attach ActionHandler to a process. An ActionHandler is a small peace of Java-Code which will be executed when the process execution passes by a specific point in the process (called Events). In our case, we want to have such an ActionHandler when the execution arrives in the "work on case" state. Then we want to call the ESB service "CreateCaseService". This is done via a nod-enter event on the "work on case" state.

<process-definition name="EsbShowcase">
    <start-state name="start">
        <transition to="review" />
    </start-state>
    <task-node name="review">
        <transition to="work on case" name="investigate" />
        <transition to="end" name="ignore" />
    </task-node>
    <state name="work on case">
      <event type="node-enter">
        <action class="org.mule.transport.bpm.
         jbpm.actions.SendMuleEvent">
         <payloadSource>incoming</payloadSource>
         <endpoint>vm://openServiceCall</endpoint>
         <synchronous>false</synchronous>
       </action>
     </event>
     <transition to="end" name="case closed" />
    </state>
  <end-state name="end" />
</process-definition>

The bold action class in the process definition shows how to send Events to Mule. Mule provides much more predefined Action Handlers which can be used in the process (see BPM Transport  documentation on Mulesource). The payloadSource is the name of a process varialbe which should be used as message payload. The endpoint defines the service (an existing inbound-endpoint of a service) in the Mule configuration.

Installation

The whole example can used without any web server, application server or database. Installation seps:

  1. You need a Maven 2 installation
  2. Download the ZIP containing the source code and unpack the ZIP
  3. Go to the source folder and execute mvn eclipse:eclipse to create a Eclipse project
  4. Import the project in your Eclipse IDE
  5. Start the LaunchESBServer in your IDE
  6. Move the test.txt located in test-data in test-data/in
  7. The showcase is running :)

Conclusion

The integration of jBPM and the ESB is technically very easy. In practice this makes sense in a lot of cases. By combining these technologies you don't have to put business process logic in your ESB routing rules but also can keep the business process clean from integration stuff. For some more thoughts on this, please refer to the Java Magazin article.


Zurück