Saturday, November 12, 2011

Using Orchestration in JBoss ESB

NOTE: Creating a jBPM 3 project is not the only way of doing what I explain here. It is possible to write an orchestration process inside a JBoss ESB project.


NOTE: You may need to install the following software and configure a jBPM runtime before being able to follow this tutorial (check version 3 here: http://sourceforge.net/projects/jbpm/files/). This serves only for the development in the JBoss Developer Studio. Don't point to your JBoss ESB during installation of this jbpm, because JBoss ESB will break. Right now I can see the following in my preferences of JBoss Developer Studio:






To create a project for a jBPM process do as follows:




This will create a lot of files that can provide you some help, including a sketch of a process. We will take advantage of it! As you can see in the following figure, we have only three nodes. In the middle one we will invoke an ESB service. In the first and in the last we will invoke local handlers: com.sample.action.MessageActionHandler andc om.sample.action.MessageActionHandler2. In the second one we will display results that we got from the ESB service invoked before. Here is the picture of the process:





The code looks as follows:


package com.sample.action;

import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;

public class MessageActionHandler implements ActionHandler {

private static final long serialVersionUID = 1L;

/**
* The message member gets its value from the configuration in the
* processdefinition. The value is injected directly by the engine.
*/
String message;

/**
* A message process variable is assigned the value of the message
* member. The process variable is created if it doesn't exist yet.
*/
public void execute(ExecutionContext context) throws Exception {
System.out.println("MessageActionHandler: " + message);
}

}



package com.sample.action;

import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;

public class MessageActionHandler2 implements ActionHandler {

private static final long serialVersionUID = 1L;

/**
* The message member gets its value from the configuration in the
* processdefinition. The value is injected directly by the engine.
*/
String message;

/**
* A message process variable is assigned the value of the message
* member. The process variable is created if it doesn't exist yet.
*/
public void execute(ExecutionContext context) throws Exception {
System.out.println("Inside MessageActionHandler2");
System.out.println(context.getContextInstance().getVariable("theBody"));
}

}



and finally the process that corresponds to the figure above. One should carefully note the correspondence of names of the variables between jBPM and ESB and notice the access that we do to “theBody” in the MessageActionHandler2:


<?xml version="1.0" encoding="UTF-8"?>

<process-definition xmlns="urn:jbpm.org:jpdl-3.2"
name="simple">
<start-state name="start">
<transition name="to_state" to="first">
<action name="action" class="com.sample.action.MessageActionHandler">
<message>Going to the first state!</message>
</action>
</transition>
</start-state>

<node name="first">
<action name="esbAction"
class="org.jboss.soa.esb.services.jbpm.actionhandlers.EsbActionHandler">
<esbCategoryName>
My_Demo_Service
</esbCategoryName>
<esbServiceName>Hello</esbServiceName>
<bpmToEsbVars>
<mapping bpm="myname" esb="whoami" />
<mapping bpm="theBody" esb="BODY_CONTENT"/>
</bpmToEsbVars>
<esbToBpmVars>
<mapping esb="BODY_CONTENT" bpm="theBody" />
</esbToBpmVars>
</action>
<transition name="to_end" to="end">
</transition>
</node>

<end-state name="end">
<event type="node-enter">
<action name="action" class="com.sample.action.MessageActionHandler2">
</action>
</event>
</end-state>

</process-definition>


In the Deployment tab of the process we can define some details, including the classes we need (this is mandatary).


To deploy the process in the JBoss ESB server we pick the following option in the menus and save it to some directory as in the following two figures:



Then we deploy the process in the JBoss web management console. The URL is http://localhost:8080/jbpm-console and we log in with the username and password admin/admin. We upload the file p.par that we saved in the previous steps (selecting Deploy in the following figure):



We then follow to the ESB project:



It is very simple. It only has three entry points (queues): two serve to start the jBPM process (as usual a JMS and an ESB one) and are the entry point to the service My_Demo_Service/Start, whereas the last one is invoked by the process node called “first” (see the figure above) and is the entry point to the service My_Demo_Service/Hello. One should notice the property mep=”OneWay”. It means that the pipeline is unidirectional. No reply is expected. In the other service (My_Demo_Service/Hello), the pipeline responds to the invoker, in the case the process node “first”. How? This is automatic. The return message in the last action of the pipeline is the reply to the node “first”. You can then see how the contents of the message are converted back to the BPM side on the definition of the node “first” in the process. Let’s look at the jboss-esb.xml:

<?xml version="1.0"?>
<jbossesb parameterReloadSecs="5"
xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd">
<providers>
<jms-provider connection-factory="ConnectionFactory"
name="JMS">
<jms-bus busid="startGwChannel">
<jms-message-filter dest-name="queue/bpm_demo_start_Request_gw"
dest-type="QUEUE" />
</jms-bus>
<jms-bus busid="startEsbChannel">
<jms-message-filter dest-name="queue/bpm_demo_start_Request_esb"
dest-type="QUEUE" />
</jms-bus>
<jms-bus busid="HelloEsbChannel">
<jms-message-filter dest-name="queue/bpm_hello_Request_esb"
dest-type="QUEUE" />
</jms-bus>
</jms-provider>
</providers>
<services>
<service category="My_Demo_Service"
description="BPM Demo Orchestration: Use this service to start a process instance"
name="Start">
<listeners>
<jms-listener busidref="startGwChannel" is-gateway="true"
name="JMS-Gateway" />
<jms-listener busidref="startEsbChannel" name="ESB-Listener" />
</listeners>
<actions mep="OneWay">
<action class="action.InitializeListenerAction" name="init"
process="init" />
<action class="org.jboss.soa.esb.services.jbpm.actions.BpmProcessor"
name="lottery">
<property name="command" value="StartProcessInstanceCommand" />
<property name="process-definition-name" value="simple" />
<property name="esbToBpmVars">
<mapping bpm="myname" esb="userName" />
<mapping bpm="theBody" esb="BODY_CONTENT" />
</property>
</action>
</actions>
</service>
<service category="My_Demo_Service"
description="BPM Demo Orchestration: Use this service to invoke the hello"
name="Hello">
<listeners>
<jms-listener busidref="HelloEsbChannel" name="ESB-Listener" />
</listeners>
<actions>
<action class="action.MyListenerAction" name="helloaction" process="hello" />
</actions>
</service>
</services>
</jbossesb>


We need two listener actions. The initializer expects a message of the form:
single_name_no_spaces message perhaps with several spaces
Pay special attention to the names of the variables, which correspond to what we have in the “simple” process above.
package action;
/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/


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

public class InitializeListenerAction extends AbstractActionLifecycle
{

protected ConfigTree _config;

public InitializeListenerAction(ConfigTree config) {
_config = config;
}


public Message init(Message message) throws Exception{
System.out.println("---------------------------------- Initialize ----------------------------------");
String msgbody = (String) message.getBody().get();
String [] vec = msgbody.split(" ", 2);
String user = vec[0];
String text = vec[1];
message.getBody().add("userName", user);
message.getBody().add(text);
System.out.println("Initialize got the following data. User = " + user + ". Text = " + text);
System.out.println("-------------------------------- end initialize --------------------------------");
return message;
}


}



package action;
/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/


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

public class MyListenerAction extends AbstractActionLifecycle
{

protected ConfigTree _config;

public MyListenerAction(ConfigTree config) {
_config = config;
}

public Message hello(Message message) {
System.out.println("---------------------------------- hello ----------------------------------");
String who = (String) message.getBody().get("whoami");
String textinbody = (String) message.getBody().get();
message.getBody().add("Hello " + who + ". You said: " + textinbody);
System.out.println("-------------------------------- end hello --------------------------------");
return message;
}

}



Just to help you with the configurations I also add the jbm-queue-service.xml and the deployment.xml:
<?xml version="1.0" encoding="UTF-8"?>
<server>

<mbean code="org.jboss.jms.server.destination.QueueService"
name="jboss.esb.quickstart.destination:service=Queue,name=bpm_demo_start_Request_gw"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer
</depends>
</mbean>
<mbean code="org.jboss.jms.server.destination.QueueService"
name="jboss.esb.quickstart.destination:service=Queue,name=bpm_demo_start_Request_esb"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer
</depends>
</mbean>
<mbean code="org.jboss.jms.server.destination.QueueService"
name="jboss.esb.quickstart.destination:service=Queue,name=bpm_hello_Request_esb"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer
</depends>
</mbean>
<mbean code="org.jboss.jms.server.destination.QueueService"
name="jboss.esb.quickstart.destination:service=Queue,name=bpm_hello_Request_esb_reply"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer
</depends>
</mbean>
</server>



<?xml version="1.0"?>
<jbossesb-deployment>
<jmsQueue>bpm_demo_start_Request_gw</jmsQueue>
<jmsQueue>bpm_demo_start_Request_esb</jmsQueue>
<jmsQueue>bpm_hello_Request_esb</jmsQueue>
<jmsQueue>bpm_hello_Request_esb_reply</jmsQueue>
</jbossesb-deployment>



We need to deploy:



And we must not forget to add the class that puts everything moving:
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class SendJMSMessage {
  QueueConnection conn;
  QueueSession session;
  Queue que;
  
  
  public void setupConnection() throws JMSException, NamingException
  {
      Properties properties1 = new Properties();
properties1.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
properties1.put(Context.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");
properties1.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
InitialContext iniCtx = new InitialContext(properties1);

   Object tmp = iniCtx.lookup("ConnectionFactory");
   QueueConnectionFactory qcf = (QueueConnectionFactory) tmp;
   conn = qcf.createQueueConnection();
   que = (Queue) iniCtx.lookup("queue/bpm_demo_start_Request_gw");
   session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
   conn.start();
   System.out.println("Connection Started");
  }
  
  public void stop() throws JMSException
  {
      conn.stop();
      session.close();
      conn.close();
  }
  
  public void sendAMessage(String msg) throws JMSException {
  
      QueueSender send = session.createSender(que);        
      ObjectMessage tm = session.createObjectMessage(msg);
      
      send.send(tm);        
      send.close();
  }
     
  
  public static void main(String args[]) throws Exception
  {            
   SendJMSMessage sm = new SendJMSMessage();
   sm.setupConnection();
   sm.sendAMessage(args[0]);
   sm.stop();
  
  }
  
}


The respective configurations:



and the result:
11:09:39,114 INFO  [STDOUT] ---------------------------------- Initialize ----------------------------------
11:09:39,115 INFO  [STDOUT] Initialize got the following data. User = Paul. Text = I would like to say good morning to everybody!
11:09:39,115 INFO  [STDOUT] -------------------------------- end initialize --------------------------------
11:09:39,220 INFO  [STDOUT] MessageActionHandler: Going to the first state!
11:09:39,236 INFO  [InquiryHelper] uddi:juddi.apache.org:737e3ce5-cb96-4c3c-9146-00d9c7baa300 is modified Tue Nov 08 11:05:26 WET 2011 1320750326046
11:09:39,312 INFO  [STDOUT] ---------------------------------- hello ----------------------------------
11:09:39,312 INFO  [STDOUT] -------------------------------- end hello --------------------------------
11:09:39,318 INFO  [InquiryHelper] uddi:juddi.apache.org:924cfcef-5942-4f2b-af96-e3222743304a is modified Tue Nov 08 10:25:27 WET 2011 1320747927118
11:09:39,410 WARN  [ProxyWarnLog] Narrowing proxy to class org.jbpm.graph.node.EndState - this operation breaks ==
11:09:39,418 INFO  [STDOUT] Inside MessageActionHandler2
11:09:39,419 INFO  [STDOUT] Hello Paul. You said: I would like to say good morning to everybody!

So the sequence is: SendJMSMessage → ESB My_Demo_Service/Start → jBPM start (MessageActionHandler) → jBPM first node → ESB My_Demo_Service/Hello → return to jBPM first node → jBPM end node (MessageActionHandler2)

No comments:

Post a Comment