Search This Blog

Thursday, July 28, 2011

Split join in OSB




In this post, I am going to discuss split join.Split join is a very powerful feature in OSB. Through split join, we can make multiple service call outs in series or parallel and combine the result of these calls.For the purpose of illustration, I am going to build a sample application.

First, I am going to a create a java based web service which is going to act as the dummy service implementation for my business service.

Open JDeveloper.I am using JDeveloper 11.1.1.3.First create a new application and a new WEB project.To follow along name the application OSBSplitJoin and the project SplitJoinService.Right click on the project and choose the option project properties.In the left pane, select the Java EE Application node and change the application name and the context root to a name of your choice; to follow along enter split_join_service for both the application name and the context root.

Next,create a new package inside the project;to follow along name the package com.rahul.osb.

Next, create two classes inside the package:- Bill.java and Customer.java.Sources for the files are as follows:-

Bill.java:-


package com.rahul.osb;
public class Bill {
    
    private String billNo;
    private String customerId;
    private String amount;


    public void setBillNo(String billNo) {
        this.billNo = billNo;
    }

    public String getBillNo() {
        return billNo;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setAmount(String amount) {
        this.amount = amount;
    }

    public String getAmount() {
        return amount;
    }
}




Customer.java:-

package com.rahul.osb;

public class Customer {
   
    private String firstName;
    private String lastName;
    private String id;


    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}

Next create another class named UtilityService.java.This class implements the web service.Inside the class, I have created two dummy methods.Following is the source for the class:-

package com.rahul.osb;

import javax.jws.WebService;

@WebService(serviceName = "UtilityService")
public class UtilityService {
   
    public Customer getCustomerInfo(String id){
     
      Customer c=new Customer();
      c.setFirstName("Rahul");
      c.setLastName("Lahiri");
      c.setId("233037");
      System.out.println("***************************");
      System.out.println("customer info invoked");
      System.out.println("***************************");
      return c;
     
    }
   
    public Bill getBillingInfo(String id){
      Bill bill=new Bill();
      bill.setAmount("$5000");
      bill.setBillNo("12346");
      bill.setCustomerId("233037");
      System.out.println("***************************");
      System.out.println("billing info invoked");
      System.out.println("***************************");
      return bill;
    }
   
  
   
}







We are done with the service.Just create a WAR file and deploy the project to the weblogic server.Once deployed, make sure you can view the WSDL at

    http://<<server_host_name>>:<<server_port>>/<<context_root>>/UtilityService?wsdl

In my case, this is:-

   http://localhost:7001/split_join_service/UtilityService?wsdl 


Note here that the service name "UtilityService" was specified as an annotation in our service class UtilityService.java.

So if everything goes well, you will be able to view the WSDL file.This means our service is up and running.Next open the OEPE. I am using the Oracle Eclipse Plug-in.I prefer using the IDE because it has got design time support for XQuery which we are going to need in this case.If you are using the console, don't worry;the steps are going to be the same.

In OSB , we are going to make calls to the getCustomerInfo and getBillingInfo methods using split join, combine the results and return the result back to the caller.

Let us get started.First create a new OSB project;to follow along, name it SplitJoin_WS.Create two folders inside the project:- xsd and WSDL.

Inside the WSDL folder, create a new WSDL file.Name it UtilityService.wsdl. This WSDL file is the WSDL file for the java based service that we created earlier.The source of the WSDL is as follows:-



<?xml version='1.0' encoding='UTF-8'?>
 <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://osb.rahul.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://osb.rahul.com/" name="UtilityService">
 <types>
 <xsd:schema>
  <xsd:import namespace="http://osb.rahul.com/" schemaLocation="../xsd/UtilityService.xsd" />
  </xsd:schema>
  </types>
 <message name="getOrder">
  <part name="parameters" element="tns:getOrder" />
  </message>
 <message name="getOrderResponse">
  <part name="parameters" element="tns:getOrderResponse" />
  </message>
 <message name="getCustomerInfo">
  <part name="parameters" element="tns:getCustomerInfo" />
  </message>
 <message name="getCustomerInfoResponse">
  <part name="parameters" element="tns:getCustomerInfoResponse" />
  </message>
 <message name="getBillingInfo">
  <part name="parameters" element="tns:getBillingInfo" />
  </message>
 <message name="getBillingInfoResponse">
  <part name="parameters" element="tns:getBillingInfoResponse" />
  </message>
 <portType name="UtilityService">
 <operation name="getOrder">
  <input message="tns:getOrder" />
  <output message="tns:getOrderResponse" />
  </operation>
 <operation name="getCustomerInfo">
  <input message="tns:getCustomerInfo" />
  <output message="tns:getCustomerInfoResponse" />
  </operation>
 <operation name="getBillingInfo">
  <input message="tns:getBillingInfo" />
  <output message="tns:getBillingInfoResponse" />
  </operation>
  </portType>
 <binding name="UtilityServicePortBinding" type="tns:UtilityService">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
 <operation name="getOrder">
  <soap:operation soapAction="" />
 <input>
  <soap:body use="literal" />
  </input>
 <output>
  <soap:body use="literal" />
  </output>
  </operation>
 <operation name="getCustomerInfo">
  <soap:operation soapAction="" />
 <input>
  <soap:body use="literal" />
  </input>
 <output>
  <soap:body use="literal" />
  </output>
  </operation>
 <operation name="getBillingInfo">
  <soap:operation soapAction="" />
 <input>
  <soap:body use="literal" />
  </input>
 <output>
  <soap:body use="literal" />
  </output>
  </operation>
  </binding>
 <service name="UtilityService">
 <port name="UtilityServicePort" binding="tns:UtilityServicePortBinding">
  <soap:address location="http://localhost:7001/split_join_service/UtilityService" />
  </port>
  </service>
  </definitions>




Inside the xsd folder, create a new XML schema document named UtilityService.xsd.The source of the file is as follows:-

<?xml version="1.0" encoding="UTF-8"?> 
<xs:schema xmlns:tns="http://osb.rahul.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://osb.rahul.com/">

  <xs:element name="getBillingInfo" type="tns:getBillingInfo" />
  <xs:element name="getBillingInfoResponse" type="tns:getBillingInfoResponse" /> 
  <xs:element name="getCustomerInfo" type="tns:getCustomerInfo" /> 
  <xs:element name="getCustomerInfoResponse" type="tns:getCustomerInfoResponse" /> 
  <xs:element name="getOrder" type="tns:getOrder" /> 
  <xs:element name="getOrderResponse" type="tns:getOrderResponse" /> 
<xs:complexType name="getOrder">
<xs:sequence>
  <xs:element name="arg0" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="getOrderResponse">
<xs:sequence>
  <xs:element name="return" type="tns:order" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="order">
<xs:sequence>
  <xs:element name="billNo" type="xs:string" minOccurs="0" /> 
  <xs:element name="itemId" type="xs:string" minOccurs="0" /> 
  <xs:element name="orderNo" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="getCustomerInfo">
<xs:sequence>
  <xs:element name="arg0" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="getCustomerInfoResponse">
<xs:sequence>
  <xs:element name="return" type="tns:customer" minOccurs="0" />
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="customer">
<xs:sequence>
  <xs:element name="firstName" type="xs:string" minOccurs="0" />
  <xs:element name="id" type="xs:string" minOccurs="0" /> 
  <xs:element name="lastName" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="getBillingInfo">
<xs:sequence>
  <xs:element name="arg0" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
<xs:complexType name="getBillingInfoResponse">
 <xs:sequence>
  <xs:element name="return" type="tns:bill" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
 <xs:complexType name="bill">
 <xs:sequence>
  <xs:element name="amount" type="xs:string" minOccurs="0" /> 
  <xs:element name="billNo" type="xs:string" minOccurs="0" /> 
  <xs:element name="customerId" type="xs:string" minOccurs="0" /> 
  </xs:sequence>
  </xs:complexType>
  </xs:schema>


This schema is referred inside the UtilityService.wsdl.

Now we are going to create a business service based on the java service that we created earlier.To do that, right click on your project and choose new->other->Oracle Service Bus->Business Service.

Choose a name for your business service; to follow along name it SplitJoin_WS.biz..


Hit finish.In the window that pops up, hit Yes to dismiss it.If the business service is not already open, double click on it.

Select the general tab if it not already selected.Choose the WSDL Web Service option and click on browse.Select the UtilityService.wsdl file and the UtilityServicePort.Hit Ok once you are done.

Select the Transport tab and enter the  endpoint URL of the java service that you created earlier as the Endpoint URI and hit the add button.

The endpoint URL in my case is:-

        http://localhost:7001/ split_join_service/UtilityService  


This is the same URL that we used to view the WSDL but without the '?wsdl' part.Our business service configuration is complete.


Next create another WSDL file;to follow along choose the name SplitJoin.wsdl.This WSDL file is going to be used for our SplitJoin flow.Following is the source of the WSDL file:-


<wsdl:definitions name="SplitJoin" targetNamespace="http://splitjoin.rahul.com"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:data="http://osb.rahul.com/schemas"
        xmlns:tns="http://splitjoin.rahul.com"
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:client="http://osb.rahul.com/">
 
  <wsdl:types>
      <xsd:schema targetNamespace="http://osb.rahul.com/schemas">
          <xsd:import namespace="http://osb.rahul.com/" schemaLocation="../xsd/UtilityService.xsd"/>
         
          <xsd:element name="getAllInfoRequest" type="xsd:string"/>
          <xsd:element name="getAllInfoResponse">
              <xsd:complexType>
                  <xsd:sequence>
                      <xsd:element name="customer_info" type="client:customer" />
                      <xsd:element name="order_info" type="client:order" />
                      <xsd:element name="bill_info" type="client:bill" />
                  </xsd:sequence>
              </xsd:complexType>
          </xsd:element>
      </xsd:schema>
  </wsdl:types>
 
  <wsdl:message name="getAllInfoRequestMsg">
      <wsdl:part name="request" element="data:getAllInfoRequest"/>
  </wsdl:message>
 
  <wsdl:message name="getAllInfoResponseMsg">
      <wsdl:part name="response" element="data:getAllInfoResponse"/>
  </wsdl:message>

 <wsdl:portType name="SplitJoinPortType">
     <wsdl:operation name="getAllInfo">
         <wsdl:input message="tns:getAllInfoRequestMsg"/>
         <wsdl:output message="tns:getAllInfoResponseMsg"/>
     </wsdl:operation>
 </wsdl:portType>


 <wsdl:binding name="SplitJoinPortTypeBinding" type="tns:SplitJoinPortType">
     <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
     <wsdl:operation name="getAllInfo">
         <wsdl:input>
             <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
             <soap:body use="literal"/>
         </wsdl:output>
     </wsdl:operation>
 </wsdl:binding>
 
</wsdl:definitions>








Split joins are implemented as flows and wrapped by a business service.Right click on your project and select new->other->Oracle Service Bus->Split join and hit next.Choose a name; to follow along, name it SplitJoinFlow.Hit next,the next screen will prompt you to choose an operation.Expand the SplitJoinPortTypeBinding node under the SplitJoin.wsdl node and select the operation getAllInfo.





Hit Finish once you are done.You'll see three nodes SplitJoin, Receive and Reply in the flow as shown in the following image:-




Double click on the arrow sign as indicated in the image above.This will show the variables that have been automatically created. In this case, you will see a request and a response variable based on the message types have been automatically created.



Right click on the variables window and choose the option create variable as shown in the image below:-




In the window that pops up, enter the name customer and select the message types radio button.Then choose the getCustomerInfoResponse message under the UtilityService.wsdl node.Hit Ok once you are done.



So we have created a variable based on the getCustomerInfoResponse message of the UtilityService.wsdl file.The variables created at this level are accessible throughout the message flow.

Follow the same steps to create another variable named billing and base it on the getBillingInfoResponse message of the UtilityService.wsdl.




Next drag a parallel action from the Design Pallet just below the Receive activity.If the design pallet is not visible, select window->show view->other->Oracle Service Bus->Design Pallet.

You'll see two branches will be created by default.You can add more branches by clicking on the arow sign as indicated in the image below.



Each of the branches of a parallel action are executed in parallel.If you are familiar with Oracle SOA suite, this parallel action is similar to the flow activity in a BPEL process.

Next drag and drop an Invoke action under the Communication section from the Design pallet inside the first branch.Double click the Invoke action to bring up the properties window if it is not already open.




Hit the browse button and choose the getCustomerInfo under the SplitJoin_WS.biz. service.Hit OK once you are done.

 Now double click on the invoke activity again to bring up the properties window.Click on the Input Variable Tab and from the Message Variable drop down, select the option "create message variable".

In the window that pops up, notice that the type and appropriate namespace are already selected.Enter a name; to follow along, enter "getCustomerInfoRequest" and choose local variable option.

Repeat the above steps to create an output variable;to follow along, name it "getCustomerInfoResponse".

So we have configured our Invoke activity.As the name of the activity suggests, here we are invoking an operation i.e. getCustomerInfo on our business service.We have also created two variables:- an input variable that represents the input parameter to the operation and an output variable which represents the response received from the operation.

However our input variable is not populated.In this case, the input to the getCustomerInfo is a string.Drag and drop an assign activity just before the Invoke activity.

Double click on the assign activity to bring up the properties window.Click on the expression link and in the window that pops up, enter the following expression:-

<ns:getCustomerInfo xmlns:ns="http://osb.rahul.com/">
<ns:arg0>123465</ns:arg0>
</ns:getCustomerInfo>


Select the getCustomerInfoRequest.parameters option from the variable drop down.

If you take a look at our Java method getCustomerInfo() inside the utility service class, it just takes a string but I have not implemented any logic based on the input string.I just placed it for the sake of illustration.

So in the previous step, we have populated the input to our Invoke action.Next it is time to process the response obtained from the operation.We are going to store it in the variable customer that we created a few steps back.


Drag and drop an assign activity just below the Invoke action and double click on it to bring up the properties window.Next Click on the expression link.

In the window that pops up, enter the expression :-

$getCustomerInfoResponse.parameters

You don't have to type it in.On the right pane,expand the message:getCustomerInfoResponse node and drag and drop the getCustomerInfoResponse under the part:parameters node onto the expression editor.



Hit Ok once you are done.Select the customer.parameters option from the variable drop down.

So, in the previous step, we have stored the response to the customer variable.This completes the flow for the first branch of our parallel activity.

The second branch is going to be almost the same.This time only, we are going to invoke the getBillingInfo operation.So, create an invoke activity that invokes the operation getBillingInfo;create an input and an output variable named getBillingInfoRequest and getBillingInfoResponse respectively.

Next, drag and drop an assign activity just before the invoke and assign the expression:-

<ns:getBillingInfo xmlns:ns="http://osb.rahul.com/">
 <ns:arg0>123456</ns:arg0>
</ns:getBillingInfo>


to the variable getBillingInfoRequest.parameters.

Similarly create another assign activity just below the Invoke action and assign the expression :-

         $getBillingInfoResponse.parameters 

to the variable billing.parameters.This completes the flow for the second branch of our flow activity.

At this point, we have invoked two operations and we have got the responses returned by both the operations.Now it is time to combine the results.


First shrink the parallel flow by clicking on the minus sign at the top left corner of our parallel actin as indicated in the image below.



Next, drag and drop an assign activity just below the parallel branch.Now we are going to write an XQuery transformation to combine the results.

Right click on your project and select neww->XQuery Transformation.In the window that pops up, enter a name;to follow along name it TxCombineResults and hit next.

In the next window, we are going to specify the source types.In this case, our source types are going to be the results returned by the getCustomerInfo and getBillingInfo operations.


First expand the SplitJoin_WS node and select the return element under the getCustomerInfoResponse node under the xsd node.Hit Add.


In the similar way, expand the getBillingInfoResponse node and select the return element and then hit add. The screen should look like the following image:-






Hit next to proceed.In the target select the getAllInfoResponse under the WSDL node for the project SplitJoin_WS as shown in the folloing image and then hit Add.





Hit finish once you are down.If it not already open, double click on the XQuery file to open it.Connect the nodes from the source to the target side as shown in the following image.



Click save all.Next go to the Split Join flow and double click on the assign activity just below the parallel action to bring up the properties window.Click on the expression link.In the expression editor, click on the XQuery Resources tab as indicated in the image below:-




Notice that there are two bind variables as we chose two inputs on the source side of our XQuery file.Here we should pass the proper variables as parameter to the XQuery resource.Recall that our XQuery file takes two inputs that we defined on the source side :- one is a getCustomerInfoResponse element and the other is a getBillingInfoResponse element.So enter the expressions :-

$customer.parameters/return for return1 variable.
$billing.parameters/return for return 2 variable.

Rather than typing the expression in, you could drag and drop the proper variables in the same way that we previously did onto the expression editor.

Hit OK once you are done.back in the assign properties window, select the response.response option from the drop down for the variable.Click Save All.

Our split join is complete. Now we are going to wrap the flow by a a business service.To do that, right click on the SplitJoin.flow and select Oracle Service Bus -> generate business service.Choose a service name.To follow along, input SplitJoinFlow.biz.Hit Ok.

Our split join service is ready to be used.

Next we are going to write a proxy service to test the SplitJoinFlow.biz business service.This is pretty regular stuff.Just create a proxy service based on the SplitJoin.wsdl file and in the message flow add a route to action to the SplitJoinFlow.biz service invoking the operation getAllInfo.

Now you can export the project to a jar and deploy it to your OSB console and test it.


Hope this helps.

1 comment: