Using BPEL 2.0 To Wrap An Existing Web Service
Using BPEL 2.0 To Wrap An Existing Web Service
Summary
The web service we have already created is generic; any client could potentially make use of it. However, this is not quite true for mobile clients. Mobile devices, by their nature, are less powerful than desktop machines and so have a more restricted set of APIs available in Java. One particular restriction we run into for our project is that the SOAP implementation for mobile devices (JSR 172) only contains a subset of the SOAP specification in use by the web service we have already created. In short, the mobile device will not be able to call our web service directly. Our solution is to build a new, lightweight web service which wraps our existing web service and exposes only WSDL that can be consumed by the mobile device. The steps we are going to take are: 1. Create WSDL to expose to mobile client. 2. Import WSDL for the service implementation. 3. Orchestrate. 4. Test.
Implementation steps
In the project panel, select New Project SOA BPEL Module and call it "TravelAgendaSvc" Creating JSR 172 compliant WSDL Under the Process Files node, choose New WSDL Document... and call it "TravelAgenda". In the Abstract Configuration screen that follows, change the message part name of the input to "parameters", and the name of the output to "results". Leave the element/type at the defaults (xsd:string) and we will change these to appropriate types later. At the bottom of the dialog, uncheck "Generate partnerlinktype automatically".
On the "Concrete Configuration" screen, choose "finish". Now we build the structures needed for the WSDL. In the WSDL editor, right click on "Types" and Add Inline Schema.
The inline schema appears in tree structure in the editor window. In the properties panel, change Attribute Form Default and Element Form Default to "Qualified". Experimental evidence indicates that without this change, the client (which we will generate later) will often fail to call our web service with namespace errors.
Add a new Complex Type called "PersonComplexType" (keep all other defaults). Under the sequence, select Add Element... and call it "PersonId". For the type, use Existing Type Built-in Types int Add another Complex Type called "TripComplexType" (again, keep all other defaults). Add the following elements, of type "string": DepartureDate DepartureCity DestinationCity Reason As there may be multiple trips for a given person, we need to be able to return multiple instances of the previous structure. Add a final Complex Type called "ResultComplexType", which is a sequence named "Trip" of type "TripComplexType". In the properties panel, Change MaxOccurs for the Trip to "unbounded", and MinOccurs to 0. Under the Elements node, we will create an element to represent to request and responses. First, create an element called "AgendaRequest" with a Complex Type of PersonComplexType. Next, create an element called "AgendaResponse" with a Complex Type of ResultComplexType. Now, under Messages/TravelAgendaOperation, we can change the types of parameters from string to the correct types. Change parameters to be the element "AgendaRequest, and the results to be element "AgendaResponse". Under Bindings/TravelAgendaBinding/soap:binding, change the style in the properties panel from "rpc" to "document". Inside TravelAgendaOperation, there is a soap:operation element. Although this is
blank by default, it is mandatory for the JSR172 wizard we will use in the next stage of development. This could be any text but we will use "getTravelAgenda". In the same properties sheet, set the style to "document". By now, you should have created WSDL which looks like this:
<?xml version="1.0" encoding="UTF-8"?> <definitions name="TravelAgenda" targetNamespace="http://j2ee.netbeans.org/wsdl/TravelAgenda" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://j2ee.netbeans.org/wsdl/TravelAgenda" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types> <xsd:schema targetNamespace="http://j2ee.netbeans.org/wsdl/TravelAgenda" xmlns:tns="http://j2ee.netbeans.org/wsdl/TravelAgenda" attributeFormDefault="qualified" elementFormDefault="qualified"> <xsd:complexType name="PersonComplexType"> <xsd:sequence> <xsd:element name="PersonId" type="xsd:int"></xsd:element> </xsd:sequence> </xsd:complexType> <xsd:complexType name="TripComplexType"> <xsd:sequence> <xsd:element name="DepartureDate" type="xsd:string"></xsd:element> <xsd:element name="DepatureCity" type="xsd:string"></xsd:element> <xsd:element name="DestinationCity" type="xsd:string"></xsd:element> <xsd:element name="Reason" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> <xsd:complexType name="ResultComplexType"> <xsd:sequence> <xsd:element name="Trip" type="tns:TripComplexType" maxOccurs="unbounded" minOccurs="0"></xsd:element> </xsd:sequence> </xsd:complexType> <xsd:element name="AgendaRequest" type="tns:PersonComplexType"></xsd:element> <xsd:element name="AgendaResponse" type="tns:ResultComplexType"></xsd:element> </xsd:schema> </types> <message name="TravelAgendaOperationRequest"> <part name="parameters" element="tns:AgendaRequest"/> </message> <message name="TravelAgendaOperationResponse"> <part name="results" element="tns:AgendaResponse"/> </message> <portType name="TravelAgendaPortType"> <operation name="TravelAgendaOperation"> <input name="input1" message="tns:TravelAgendaOperationRequest"/> <output name="output1" message="tns:TravelAgendaOperationResponse"/> </operation> </portType> <binding name="TravelAgendaBinding" type="tns:TravelAgendaPortType"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="TravelAgendaOperation"> <soap:operation soapAction="getTravelAgenda" style="document"/> <input name="input1"> <soap:body use="literal" namespace="http://j2ee.netbeans.org/wsdl/TravelAgenda"/> </input> <output name="output1"> <soap:body use="literal" namespace="http://j2ee.netbeans.org/wsdl/TravelAgenda"/> </output> </operation> </binding> <service name="TravelAgendaService"> <port name="TravelAgendaPort" binding="tns:TravelAgendaBinding"> <soap:address location="http://localhost:$ {HttpDefaultPort}/TravelAgendaService/TravelAgendaPort"/> </port>
</service> </definitions>
New items should be created in the project structure, under a folder called localhost_8080.
Expand the localhost_8080 folder and locate TravelAgendaPort.wsdl. Drag it onto the right column of the BPEL editor and fill out the partner link dialog as shown:
In order, drag the following components into the business process: Receive Assign Invoke ForEach (Containing an Assign) Reply
Double-click on the receive activity. Select the Partner Link "TravelAgendaPartnerLink" and the operation "TravelAgendaOperation". Create a new input variable, and take the default options.
Double-click on the reply activity. Again, select the Partner Link "TravelAgendaPartnerLink" and the operation "TravelAgendaOperation". Create a new output variable, taking the default options.
Double-click on the invoke activity. Select the Partner Link "ExtTravelAgendaPartnerLink", and the operation "getTravelAgenda". Create input and output variables, accepting the default options.
Click on the first assignment activity, and select the mapper. Map PersonId from TravelAgendaOperationIn to GetTravelAgendaIn The structure which returns is repeating, and we have to perform the mapping for each returned trip inside a loop. To do this, we will be using a predicate.
Click on the ForEach icon. Open the mapper, and perform the following mappings: 1. Drag a number literal "1" to the Start Value. 2. Map the count of the "trip" nodes to the Final Value. 3. Leave the Completion Condition empty.
Click on the second assignment activity, and select the mapper. Expand the output variable GetTravelAgendaOut and add a predicate to the "trip" node as shown:
Create a similar predicate for the Trip element of TravelAgendaOperationOut on the input side. Map the fields under GetTravelAgendaOut to TravelAgendaOperationOut as shown in the screenshot:
Note: The date which is returned from the underlying web service is in the format yyyy-mm-ddThh:mm:ssZ. By using the "Substring Before" BPEL function, we can extract just the date from the depDate field. Finally, you should have a business process which looks like this:
underneath the TravelAgendaSvc item. On the next screen, select TravelAgendaOperation. A new screen will open with the XML to be sent as the request to the service. Change the contents of the PersonId element to "1", or any valid entry from the database table. Right-click on the test in the project tab, and choose "Run". The testing framework requires an existing message to compare each run against to determine if the expected output was produced, and you may receive an alert asking if you wish to save your first run. Note: Connect to the database in NetBeans first, to ensure that it is running.