Problem with HTTP Binding of BPEL Process 13346220 1019546254780813 3473390475847911149 o1

Problem with HTTP Binding of BPEL Process

BPEL processes are very Web Service centric, and a typical way to launch a BPEL process, or to communicate with a running BPEL process instance, is through SOAP web service calls. (That is, of course, unless the BPEL process uses File-, FTP-, Queue and/or Database Adapters to listen to and react on “external events” itself). But what if you need to invoke a BPEL process from a client that is not so Web Service savvy, and can only use HTTP requests to communicate with the outside world? An example of such a “client” would be an HTML page, but also many legacy systems offer limited or no support for web service calls but can issue HTTP requests.

A typical (and often used) solution is to create and deploy a Servlet that will receive the HTTP request from the client, construct a SOAP message based on the information in the HTTP Request parameters, and then invoke the BPEL process through a web service call. Such a servlet would therefore effectively “translate” the HTTP request to a SOAP call.

What not everyone might be aware of, is that the Oracle SOA Suite ships with a standard, generic Servlet that can perform just such a translation. By inspecting the request parameters, an XML SOAP message is created and passed on to the BPEL process. The way these request parameters are “mapped’ to the XML message has changed significantly from release 10.1.2 to 10.1.3.

A good description of how to use the http binding servlet in 10.1.2 can be found in this post on Antony Reynolds’ blog: http://blogs.oracle.com/reynolds/discuss/msgReader$31?mode=day. What it boils down to is that for every HTTP request parameter that needs to be passed to the BPEL process, a message part of type string must be declared in the WSDL file on the inbound message:

<message name="HTMLInvokedRequestMessage">
  <part name="firstname" type="xsd:string"/>
  <part name="lastname" type="xsd:string"/>
</message>

The HTTP request that would invoke the BPEL process and pass the firstname and lastname in, would look like this:

http://<yourserver>:<yourport>/httpbinding/default/HTMLInvoked/Hello?firstname=John&lastname=Do

This multipart message structure is, of course, not a very common way to define the inbound message in a WSDL, and therefore this approach would typically only work if the BPEL process is designed and build with this particularity of the HTTP Binding servlet in mind.

As mentioned before, in version 10.1.3, the HTTP binding servlet has changed. Instead of automatically creating a message part for each HTTP request parameter, you now get to specify the name, and namespace, of the message part through additional request parameters.

So in the WSDL, you could specify the inbound message like this:

 <message name="BPELProcess1RequestMessage">
  <part name="payload" element="client:Order"/>
 </message>

where the Order element could be defined like this:

 <element name="Order" type="string"/>

The HTTP request to invoke this BPEL process would now look like this (in HTML form format for easy reading):

    <form method="POST" action="http://<yourserver>:<yourport>/httpbinding/default/BPELProcess1/process">
      <input type="text" name="msg_part" value="payload"/><br>
      <input type="text" name="namespace" value="http://xmlns.oracle.com/BPELProcess1"/><br>
      <input type="text" name="Order" value="10"/><br>
      <input type="submit" value="invoke BPEL"/>
    </form>

Very neat, because this definition of the inbound message in the WSDL is much more common! At first sight, it looks like this will go a long way to invoke BPEL processes that were not “tailor made” to match the requirements of the http binding servlet. But there is a caveat. If the Order element is NOT a simple string, but a complex type, the only way to get it to work is to put the entire <Order> element, as XML text in the “Order” input field,, laying the burden of creating valid XML on the client. And what’s more, the “old” 10.1.2 approach with the multipart message does not seem to work any more. In fact, if you happen to have more than one request parameter on the request, only the last one processed “makes it” to the SOAP message, the others are ignored! Therefore, the only way to get automatic mapping from HTTP request parameters to the inbound message in the WSDL, is if you either have only one string field in the input message, OR you construct a valid XML message on the client side.

When I met Clemens Utschig recently at ODTUG, I had a brief conversation about this “one field only” behaviour, and he indicated that there should still be a loop in the binding servlet, looping over all the request parameters and inserting them into the SOAP message. I could not notice anything like this, and did the unspeakable (and what’s more, I’m right now even admitting and publicizing it!!)”: I decompiled the HttpBindingServlet class that ships with the SOA suite, and found that both Clemens and me were right (only I was righter 😉 )”

 

01      protected void doPost(HttpServletRequest req, HttpServletResponse resp)
02          throws ServletException, IOException
03      {
04          NormalizedMessage nm = new NormalizedMessage();
05          BufferedReader br = req.getReader();
06          String inputLine;
07          String payload;
08          for(payload = ""(inputLine = br.readLine()) != null; payload = payload + inputLine);
09          br.close();
10          String contentType = req.getContentType();
11          if(contentType.startsWith("application/x-www-form-urlencoded"))
12          {
13              Map parts = CXStringUtils.splitToMap(payload, '&');
14              parts.remove("Submit");
15              String part = (String)parts.get("msg_part"!= null (String)parts.get("msg_part""payload";
16              parts.remove("msg_part");
17              String ns = (String)parts.get("namespace");
18              parts.remove("namespace");
19              WSLogger.debug("Part? " + part + " Namespace: " + ns);
20              String xml;
21              for(Iterator iParts = parts.keySet().iterator(); iParts.hasNext(); nm.addPart(part, xml))
22              {
23                  String key = (String)
t>iParts.next();
24                  String value = (String)parts.get(key);
25                  xml = addXMLTagIfNecessary(key, ns, value);
26              }
27          
28          else
29          {
30              nm.setPayload(payload);
31          }
32          WSLogger.debug(“HTTPPOST: parts: ” + nm.getPayload());
33          BPELPrincipal princ = checkSecurity(req);
34          nm = populateHeader(nm, req);
35          nm = constructBaseMsg(nm, princ);
36          call(req, resp, nm);
37      }

As you can see, there is indeed a loop, looping over all the request parameters, in line 21. For each parameter, it call the “addPart” method on the NormalizedMessage. But the “key” it supplies is the “part” variable, that gets its value in line 15 and is not changed any more after that (basically, it gets the value of the msg_part request parameter if present, otherwise it defaults to “payload”. Therefore, only the last iteration in this loop will provide the definitive value for the “payload” part, all other request parameters are ignored.

Of course, it will not be possible to create a generic mapping from request parameters to just any arbitrary XML message structure. But I do feel that the use of the HttpBindingServlet would be greatly improved when a message payload like this could be mapped automatically:

 <message name="ChangeOrderStatusRequestMessage">
  <part name="payload" element="client:ChangeOrderStatusProcessRequest"/>
 </message> 
 
  <element name="ChangeOrderStatusProcessRequest">
  <complexType>
   <sequence>
    <element name="OrderId" type="string"/>
    <element name="CustomerId" type="string"/>
    <element name="Status" type="string"/>
   </sequence>
  </complexType>
 </element>

This is exactly as a new BPEL process is created in JDeveloper, and very, very common for payloads with only a few fields.

I would therefore like to suggest to modify the HttpBindingServlet to accept the following request and map it automatically to the above message structure:

    <form method="POST" action="http://<yourserver>:<yourport>/httpbinding/default/BPELProcess1/process">
      <input type="text" name="msg_part" value="payload"/><br>
      <input type="text" name="msg_namespace" value="http://xmlns.oracle.com/BPELProcess1"/><br>
      <input type="text" name="msg_root_element" value="ChangeOrderStatusProcessRequest"/><br>
      <input type="text" name="msg_root_element_namespace" value="http://xmlns.oracle.com/BPELProcess1"/><br>
      <input type="text" name="OrderId" value="10"/><br>
      <input type="text" name="CustomerId" value="10"/><br>
      <input type="text" name="Status" value="10"/><br>
      <input type="submit" value="invoke BPEL"/>
    </form>

It would only require minor changes to the HttpBindingServlet, and be much more powerful to use. Hope someone is listening 😉

3 Comments

  1. Rob de Haan August 29, 2008
  2. Lucas Jellema January 14, 2008
  3. Siva September 28, 2007