Thursday, 15 September 2011

Invoking a remote webservice from BPEL using Apache ODE

Introduction:
In this post we will examine how an existing webservice can be invoked as part of a BPEL work flow.

Prerequisites:
Eclipse Helios
Tomcat 6.0.16
Apache ODE 1.3.4
BPEL Visual Designer 0.5.0  (Eclipse plugin).

Installation:
See following links for instructions on how to install these:
Tomcat: http://tomcat.apache.org/tomcat-6.0-doc/setup.html
ODE: Download ode war and deploy to tomcat webapps folder.
http://ode.apache.org/user-guide.html
Eclipse: Download the helios build of eclipse and extract the zip.
http://help.eclipse.org/helios/index.jsp
BPEL Visual Designer: Open the Eclipse, go to the menu Help→Install New SoftWare
Click on the button Add and define a new Eclipe update site with the location:
http://download.eclipse.org/technology/bpel/update-site


Invoking the webservice from BPEL

Let's assume that there is web service already implemented and call it EmployeeService. Now we want to implement a BPEL flow and as part of its execution invoke this already existing Employee Service.

1. Add partnerLink to the EmployeeService:

<plnk:partnerLinkType name="EmployeeService">
<plnk:role name="Em ployeeServiceProvider" portType="ns:EmployeeServicePortType"/></plnk:partnerLinkType>
Note: The portType should be same as the portType defined in the WSDL.

2. Create BPEL Project in eclipse:
File->New->Other->BPEL2.0-> BPEL Project
Name the project as HelloWorld

3. Right click on the bpelContent folder under the newly created HelloWorld Project and then New->Other->BPEL2.0-> New BPEL Process File.
Name it HelloWorld.bpel

4. Create the BPEL process flow like shown below:



The source for this will look like:

<!-- HelloWorld BPEL Process [Generated by the Eclipse BPEL Designer] -->
<!-- Date: Tue May 10 18:11:54 IST 2011 -->
<
bpel:process name="HelloWorld" targetNamespace="http://www.ibm.com/wd2/ode/HelloWorld"
suppressJoinFailure="yes" xmlns:tns="http://www.ibm.com/wd2/ode/HelloWorld"
xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
xmlns:emp="http://services.test.com">
<!-- Import the client WSDL -->
<bpel:import namespace="http://services.test.com"
location="EmployeeService.wsdl" importType="http://schemas.xmlsoap.org/wsdl/"></bpel:import>
<bpel:import location="HelloWorldArtifacts.wsdl"
namespace="http://www.ibm.com/wd2/ode/HelloWorld" importType="http://schemas.xmlsoap.org/wsdl/" />
<!-- ================================================================= -->
<!-- PARTNERLINKS -->
<!-- List of services participating in this BPEL process -->
<!-- ================================================================= -->
<bpel:partnerLinks>
<!-- The 'client' role represents the requester of this service. -->
<bpel:partnerLink name="client" partnerLinkType="tns:HelloWorld"
myRole="HelloWorldProvider" />
<bpel:partnerLink name="EmployeeService" partnerLinkType="emp:EmployeeService"
partnerRole="EmployeeServiceProvider" initializePartnerRole="yes"></bpel:partnerLink>
</bpel:partnerLinks>
<!-- ================================================================= -->
<!-- VARIABLES -->
<!-- List of messages and XML documents used within this BPEL process -->
<!-- ================================================================= -->
<bpel:variables>
<!-- Reference to the message passed as input during initiation -->
<bpel:variable name="input" messageType="tns:HelloWorldRequestMessage" />
<!-- Reference to the message that will be returned to the requester -->
<bpel:variable name="output" messageType="tns:HelloWorldResponseMessage" />
<bpel:variable name="id" messageType="emp:getEmployeeRequest" />
<bpel:variable name="employee" messageType="emp:getEmployeeResponse" />
</bpel:variables>
<!-- ================================================================= -->
<!-- ORCHESTRATION LOGIC -->
<!-- Set of activities coordinating the flow of messages across the -->
<!-- services integrated within this business process -->
<!-- ================================================================= -->
<bpel:sequence name="main">
in HelloWorld.wsdl -->
<!-- Receive input from requester. Note: This maps to operation defined
<bpel:receive name="receiveInput" partnerLink="client"
portType="tns:HelloWorld" operation="process" variable="input"
createInstance="yes" />
<!-- Generate reply to synchronous request -->
<bpel:assign validate="no" name="Assign">
<bpel:copy>
<bpel:from>
<bpel:literal>
<emp:getEmployeeRequest xmlns:emp="http://services.test.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<emp:id>emp:id</emp:id>
</emp:getEmployeeRequest>
</bpel:literal>
</bpel:from>
<bpel:to variable="id" part="parameters"></bpel:to>
</bpel:copy>
<bpel:copy>
<bpel:from>
<bpel:literal>
<emp:getEmployeeResponse xmlns:emp="http://services.test.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<emp:return>emp:return</emp:return>
</emp:getEmployeeResponse>
</bpel:literal>
</bpel:from>
<bpel:to variable="employee" part="parameters"></bpel:to>
</bpel:copy>
<bpel:copy>
<bpel:from part="payload" variable="input">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[tns:input]]></bpel:query>
</bpel:from>
<bpel:to part="parameters" variable="id">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0">
<![CDATA[emp:id]]>
</bpel:query>
</bpel:to>
</bpel:copy>
</bpel:assign>
<bpel:invoke name="Invoke" partnerLink="EmployeeService"
operation="getEmployee" inputVariable="id" outputVariable="employee"
createInstance="yes"></bpel:invoke>
<bpel:assign validate="no" name="Assign1">
<bpel:copy>
<bpel:from>
<bpel:literal>
<tns:HelloWorldResponse xmlns:tns="http://www.ibm.com/wd2/ode/HelloWorld"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tns:result>tns:result</tns:result>
</tns:HelloWorldResponse>
</bpel:literal>
</bpel:from>
<bpel:to variable="output" part="payload"></bpel:to>
</bpel:copy>
<bpel:copy>
<bpel:from part="parameters" variable="employee">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[emp:return]]></bpel:query>
</bpel:from>
<bpel:to part="payload" variable="output">
<bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[tns:result]]></bpel:query>
</bpel:to>
</bpel:copy>
</bpel:assign>
<bpel:reply name="replyOutput" partnerLink="client"
portType="tns:HelloWorld" operation="process" variable="output" />
</
</bpel:sequence>bpel:process>


Description:
The recieveInput takes the input which in this case is the employee id and this is assigned as an input to the webservice that needs to be invoked (i.e. EmployeeService). The output of the invocation of the service (getEmployee service method) is then assigned back to the bpel process flow which is the result.

5. Now create the ODE deployment descriptor. File->New->Other->BPEL2.0->Apache ODE Deployment Descriptor
Name it as deploy.xml

It should look like:

<?xml version="1.0" encoding="UTF-8"?>
<deploy xmlns="http://ode.fivesight.com/schemas/2006/06/27/dd"
xmlns:pns="http://www.ibm.com/wd2/ode/HelloWorld"
xmlns:emp="http://services.test.com"
xmlns:wns="http://www.ibm.com/wd2/ode/HelloWorld">
<process name="pns:HelloWorld">
<active>true</active>
<provide partnerLink="client">
<service name="wns:HelloWorldService" port="HelloWorldPort" />
</provide>
<invoke partnerLink="EmployeeService">
<service name="emp:EmployeeService" port="EmployeeServiceHttpSoap11Endpoint" />
</invoke>
</process>
</deploy>

Description:

The "process" specifies the BPEL process, its service name and port. The "invoke" defines the partner webservice that needs to be invoked as part the process execution.

6. Deploy the service. Just copy the HelloWorld eclipse folder to <TOMCAT_HOME>/webapps/ode/WEB-INF/processes/

7. You can check if the processes is deployed by opening the url:
http://localhost:8080/ode/processes/





Wednesday, 14 September 2011

Dynamic mapping with hibernate

In this post I will share my experience in implementing dynamic mapping with hibernate. The idea is to write generic code that can be made to query data from any table with any no.of columns, ofcourse the actual table & column information is provided as a configuration.

1. Use dynamic map entity mode
This can be done by adding the following in your hibernate.cfg.xml:

<property name="default_entity_mode">dynamic-map</property>

2. Now provide the mapping, this is the only part where you provide the actual table & column details, just changing this will suffice if new columns are added or if you intend to reuse this code to query some other table.

Following is an example:

<class entity-name="TestEntity" table="ENTITY">
    <id name="id"  type="long" column="ID">
          <generator class="sequence"/>
    </id>

    <property name="title" column="TITLE" type="string"/>
    <property name="text" column="TEXT" type="string"/>
</class>

3. Code to query the table:

  SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();

  Session s = sessionFactory.openSession();

   // Retrieve the data.
    String QUERY ="from TestEntity"; // Should use the entity name as defined in the hibernate mapping.
     Query query = s.createQuery(QUERY);
     HashMap<String, Object> temap = null;
     for(Iterator it=query.iterate();it.hasNext();){
       temap =(HashMap<String, Object>)it.next();  // for each row you get a map.
       // copy the map contents to your object or add to some list as needed.

     }


4. Similarly inserting can be done by creating a map with all the column/value pairs and then

Map teMap = new HashMap<String,Object>();

// fill the map with the column names & values.

s.save("TestEntity", teMap);


This can also be extended to generate the web client code based on the configuration, for example if you use extjs as the javascript framework to build the web ui, then the above can be exposed as restful service and then the table can be dynamically created based on the columns in the DB table.  I will explore that in my next post.