Saturday, August 9, 2008

Oracle BPEL : JAX-WS Web Service asynchronous call

Today we are going to create an asynchronous JAX-WS web service to interact with Oracle BPEL 10.1.3.

Asynchronous web services are very useful when you want to provide long-running treatments : the client, contrary to synchronous processes, has not to wait actively for the response, that is the server which sends a callback when the operation is finished indeed (the client has just to start a listener which will handle the response).

Oracle BPEL is providing many mechanisms to manage asynchronous web services efficiently :
  • dehydration store : this database is the Oracle BPEL core. In the asynchronous case, when a process is waiting for the response, the process is "dehydrated" (ie. the process context is maintained in database, this allows to release resources during response waiting). An internal listener is responsible to receive asynchronous responses and to wake up corresponding processes.
  • WS-Addressing : this specification is used by Oracle BPEL to provide to web services the callback informations. When a request is sent, the callback address (ie. callback listener) and a correlation id are provided in the SOAP header. When the service has finished the treatment, it has to use these informations to send the callback. Finally, Oracle BPEL uses the correlation id to identify and wake up the process instance which is the request source.
First of all, we are going to create the web service. It will simulate a long-running treatment (delay will be sent in the web service request).

Here is the WSDL file :

<definitions name="AsyncBPEL" targetNamespace="http://java-soa.blogspot.com/"
xmlns:tns="http://java-soa.blogspot.com/" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

<!-- types -->
<types>
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://schemas.xmlsoap.org/ws/2003/03/addressing"
schemaLocation="ws-addressing.xsd" />
</xsd:schema>
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" targetNamespace="http://java-soa.blogspot.com/">
<element name="request">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="delay" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
</element>
<element name="response" type="xsd:string" />
</xsd:schema>
</types>

<!-- messages -->
<message name="WSAReplyToHeader">
<part name="ReplyTo" element="wsa:ReplyTo" />
</message>
<message name="WSAMessageIDHeader">
<part name="MessageID" element="wsa:MessageID" />
</message>
<message name="WSARelatesToHeader">
<part name="RelatesTo" element="wsa:RelatesTo" />
</message>
<message name="requestMessage">
<part name="in" element="tns:request" />
</message>
<message name="responseMessage">
<part name="out" element="tns:response" />
</message>

<!-- operations -->
<portType name="RequestPortType">
<operation name="request">
<input message="tns:requestMessage" />
</operation>
</portType>
<portType name="ResponsePortType">
<operation name="response">
<input message="tns:responseMessage" />
</operation>
</portType>

<!-- binding -->
<binding name="RequestBinding" type="tns:RequestPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="request">
<soap:operation soapAction="" />
<input>
<soap:header message="tns:WSAReplyToHeader" part="ReplyTo" use="literal"
encodingStyle="" />
<soap:header message="tns:WSAMessageIDHeader" part="MessageID"
use="literal" encodingStyle="" />
<soap:body use="literal" />
</input>
</operation>
</binding>
<binding name="ResponseBinding" type="tns:ResponsePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="response">
<soap:operation soapAction="" />
<input>
<soap:header message="tns:WSARelatesToHeader" part="RelatesTo"
use="literal" encodingStyle="" />
<soap:body use="literal" />
</input>
</operation>
</binding>

<!-- services -->
<service name="RequestService">
<port name="RequestPort" binding="tns:RequestBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL" />
</port>
</service>
<service name="ResponseService">
<port name="ResponsePort" binding="tns:ResponseBinding">
<soap:address location="http://set.by.caller" />
</port>
</service>

<!-- partnerlink -->
<plnk:partnerLinkType name="asyncPartnerLink">
<plnk:role name="asyncProvider">
<plnk:portType name="tns:RequestPortType" />
</plnk:role>
<plnk:role name="asyncRequester">
<plnk:portType name="tns:ResponsePortType" />
</plnk:role>
</plnk:partnerLinkType>
</definitions>


As you can see, this wsdl file provides 2 messages in the request header :
  • WSAReplyToHeader : contains callback address, WS port name and service name.
  • WSAMessageIDHeader : contains the correlation id
and 1 message in the response header :
  • RelatesTo : will contain the same correlation id, which allows Oracle BPEL to associate the request and the response
The PartnerLink element allows Oracle BPEL to connect to the web service. It indicates which portType (ie. operations) to use for request and which portType to use for response.

After generating JAX-WS classes, we have now to implement the web service :

package com.javasoa.ws.impl;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.ws.WebServiceContext;

import com.javasoa.ws.RequestPortType;
import com.javasoa.ws.ResponsePortType;
import com.javasoa.ws.ResponseService;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.developer.JAXWSProperties;
import com.sun.xml.ws.developer.WSBindingProvider;

/**
* Request Web Service (implementation).
*
* @author greg
*/
@WebService(endpointInterface = "com.javasoa.ws.RequestPortType")
public class RequestPortTypeImpl implements RequestPortType {

/** namespace : addressing 2003. */
private static final String NS_ADDRESSING_2003 =
"http://schemas.xmlsoap.org/ws/2003/03/addressing";

/** header : reply to. */
private static final String HEADER_REPLYTO = "ReplyTo";

/** header : address. */
private static final String HEADER_ADDRESS = "Address";

/** header : message id. */
private static final String HEADER_MESSAGEID = "MessageID";

/** header : relates to. */
private static final String HEADER_RELATESTO = "RelatesTo";

/** response message. */
private static final String RESPONSE_MSG =
"%s has waited during %d seconds";

/** number of milliseconds in one second. */
private static final int MILLI_SECONDS = 1000;

// ///////////////////////////////////////////////////////////////////

@Resource
private WebServiceContext context;

// ///////////////////////////////////////////////////////////////////

/*
* (non-Javadoc)
*
* @see com.javasoa.ws.RequestPortType#request(java.lang.String, int)
*/
public void request(String name, int delay) {
// gets the addressing informations in the SOAP header
HeaderList hl =
(HeaderList) context.getMessageContext().get(
JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);

String address = null;
try {
Header replyTo = hl.get(NS_ADDRESSING_2003, HEADER_REPLYTO, false);
XMLStreamReader replyToReader = replyTo.readHeader();
while ((address == null)
&& (replyToReader.getEventType() != XMLStreamConstants.END_DOCUMENT)) {
if (replyToReader.next() == XMLStreamConstants.START_ELEMENT) {
if (replyToReader.getLocalName().equals(HEADER_ADDRESS)) {
replyToReader.next();
address = replyToReader.getText();
}
}
}
} catch (XMLStreamException xe) {
xe.printStackTrace();
return;
}
String messageId =
hl.get(NS_ADDRESSING_2003, HEADER_MESSAGEID, false)
.getStringContent();

// waits during <delay> seconds
try {
Thread.sleep(delay * MILLI_SECONDS);
} catch (InterruptedException ie) {
ie.printStackTrace();
return;
}

// response
String message = String.format(RESPONSE_MSG, name, delay);

// callback service
ResponseService service = new ResponseService();
ResponsePortType portType = service.getResponsePort();
WSBindingProvider bp = (WSBindingProvider) portType;

// sets the callback address
bp.setAddress(address);

// set the WS-addressing correlation id (RelatesTo)
bp.setOutboundHeaders(Headers.create(new QName(NS_ADDRESSING_2003,
HEADER_RELATESTO), messageId));

// sends the callback
portType.response(message);

}
}


As you can see, in a first time, we recover WS-Addressing headers : reply address (ie. RelatesTo element) and correlation id (ie. MessageID element). After that, we are simulating a long operation by starting a wait function. Once the waiting over, we are sending the callback response without forgetting to set the address and the correlation id (ie. RelatesTo element).

The JAX-WS process is now ready, we have got to link it to Oracle BPEL. For this operation, we will just create a simple BPEL process which will call our new web service.



Concerning the WS-Addressing management you have to do anything : Oracle BPEL sets the different informations in the request header and handles the relatesTo header element for correlating responses and requests.

To finish, we have just to test the BPEL process. In the BPEL Console, we are starting this process by setting input informations : per example "Greg" for name and "10" for delay.



And normally, if everything is OK, the BPEL process calls the JAX-WS service, waits for the response and finally receives the callback response.




Download sources

10 comments:

Anonymous said...

Hi Greg,

The java.zip file is empty when I downloaded the source.Please update the zip file.

Thanks

Greg said...

Hi,

The issue has been resolved !
Thank you for your remark;-)

Bye

Void said...

Hi Greg,

The bpel process is unable to receive the call back message. the process is put in wait at the receive activity. But the web service does not respond.

What could be the prolem?

Thanks

Grégory Le Bonniec said...

Do you receive the BPEL request ?
Is the callback address is OK ?
To pursue the BPEL process, you can try to send a callback request from the BPEL Console or from a SOAP Client like SOAPui.

If you don't find a solution give your email it will be simpler.

Bye

Void said...

Hi Greg,

I dont receive a bpel request. I tried sending msg from sopaUI but i am not sure to which address and port to end the reply. When i try to send the reply back using soapUI i get the following error msg at app server

Malformed Request Message: unexpected element name: expected={http://java-soa.blogspot.com/}request, actual={http://java-soa.blogspot.com/}response

I am new to this technology. Your help will be much appreciated.

My mail id is : vicsanan@gmail.com

Thanks,
Vics

Dietrich said...

Hi Greg,

can you please explain how to use the jax-ws? Can this be done via JDeveloper?

Gerard Davison said...

FYI,

A step by step doing this using the wizards in JDeveloper:

http://kingsfleet.blogspot.com/2008/11/invoke-asychronous-1013-bpel-service.html

Note that out wizard generates both parts for you which can use a real time saver.

Gerard

Anonymous said...

Hi Greg,
Thanks for the example. I am using JDeveloper 11g with Oracle SOA Suite 11g. Even though I downloaded your zip file, I could not get it run in Oracle SOA Suite 11g (due to the fact that I am really new to this stuff). Could you provide detailed steps about how to achieve the same thing in JDeveloper 11g and Oracle SOA Suite 11g?

Thanks,
Fang

reyt said...

I link Wow Power Leveling and wow power leveling wow power leveling

Anonymous said...

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!