Tuesday, November 27, 2012

BPEL and Fire-and-Forget Web Services

The other day I was discussing calling a Web Service from BPEL that does not return a result or returns a result that you don't care about: a Fire-and-Forget Web Service.
This is not a very common use case. Usually, you call a Web Service from BPEL and you use the result to determine what the next step will be, or you use the result to check that the call was successful. But that doesn't mean it never happens.

I decided to try two different use cases:
  1. Calling a Web Service that was generated with JAX WS annotations from a Java class that has a 'void' return type. 
  2. Calling a Web Service that was defined top down with a WSDL with no output message for the operation. 

Bottom up

The example is very simple, a Java class with JAX WS annotation with one operation:

package nl.vennster.demo.blog;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService(name="HelloWorldService")
public class HelloWorld {
    
    @WebMethod
    public void helloWorld(String greeting){
        System.out.println("I do nothing with this " + greeting);
    }
}

The resulting WSDL snippet looks like this:
...
</types>
<message name="helloWorld">
  <part name="parameters" element="tns:helloWorld"/>
</message>
<message name="helloWorldResponse">
  <part name="parameters" element="tns:helloWorldResponse"/>
</message>
<portType name="HelloWorldService">
  <operation name="helloWorld">
    <input message="tns:helloWorld"/>
      <output message="tns:helloWorldResponse"/>
  </operation>
</portType>
<binding name="HelloWorldServicePortBinding" type="tns:HelloWorldService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
  <operation name="helloWorld">
    <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
</operation>
</binding>
....

As you can see, an empty response is returned, if you call this Web Service with this request:

POST http://localhost:7101/Blog-HelloWorldService-context-root/HelloWorldServicePort HTTP/1.1
Content-Type: text/xml; charset=UTF-8
SOAPAction: ""
Host: localhost:7101
Content-Length: 212
X-HTTPAnalyzer-Rules: 1@localhost:8099

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://blog.demo.vennster.nl/">
   <env:Header/>
   <env:Body>
      <ns1:helloWorld>
         <arg0>Lonneke</arg0>
      </ns1:helloWorld>
   </env:Body>
</env:Envelope>

You get the following response:

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
X-Powered-By: Servlet/2.5 JSP/2.1
Date: Mon, 26 Nov 2012 20:55:04 GMT
X-ORACLE-DMS-ECID: 11d1def534ea1be0:-6225fc35:13b3e7a6f94:-8000-000000000000002a
Content-Length: 199
X-HTTPAnalyzer-RuleName: Pass through :

<?xml version = '1.0' encoding = 'UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:helloWorldResponse xmlns:ns2="http://blog.demo.vennster.nl/"/>
   </S:Body>
</S:Envelope>

Obviously, you don't care about this answer in your BPEL process: it is always the same and has nothing you can use.  However, if you invoke this service without defining the output variable BPEL will throw an error: 
Error(77): <invoke/> missing output variable specification


Top Down

Now let's see what happens if we define a WSDL with no output message:

...
    <message name="helloWorld">
        <part name="parameters" element="tns:helloWorld"/>
    </message>
    <portType name="HelloWorldService">
        <operation name="helloWorld">
            <input message="tns:helloWorld"/>
        </operation>
    </portType>
    <binding name="HelloWorldServicePortBinding" type="tns:HelloWorldService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="helloWorld">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
        </operation>
    </binding>
    <service name="HelloWorldService">
        <port name="HelloWorldServicePort" binding="tns:HelloWorldServicePortBinding">
            <soap:address location="http://localhost:7101/Blog-HelloWorldService-context-root/HelloWorldServicePort"/>
        </port>
    </service>
</definitions>

If you invoke this BPEL, there is no option to define an output variable, this field is disabled.



Conclusion

Fire-and-forget Web Services are supported in BPEL, but only if the WSDL is defined as such. If there is a response, then you have to create an output variable. Which you can then use, or ignore.




4 comments:

  1. Hi,

    if you really want an one-way BPEL then you can better use JMS. With the HTTP adapter BPEL does not report back directly and waits for the execution and reports back with an empty HTTP response with status code 202 (accepted) to the client

    also with the mediator you also got a one.way.returns.fault property in which you can control to report back the soap fault, beside http code 500.
    http://docs.oracle.com/cd/E21764_01/integration.1111/e10224/med_interactions.htm#CJAGACJH

    thanks Edwin

    ReplyDelete
    Replies
    1. I am not giving advice in this post about when to use what, I was merely showing that you can use fire-and-forget web services in BPEL and that BPEL handles that as expected.

      Maybe I should write a post about JMS versus web Services. That would be a MUCH longer one though ;)

      Delete
  2. You can get a better fire-and-forget behaviour from a JAX-WS service if you put a @OneWay annotation on the service method. This should result in the service returning a http status code reply only, instead of an empty soap envelope.

    ReplyDelete
    Replies
    1. thanks, that is a good point. In that case BPEL would not require you to create an output variable on the invoke of the service.

      Delete