HeaderHandlers and WSIF Hooks

Olivier Le Diouris
April 4th, 2006.

Resources

HeaderHandler

Oracle BPEL Process Manager has this concept of HeaderHandler, that can be implemented as some kind of filter - a bit like Servlet Filters - to interact with the WSIF Calls made from a BPEL Process.
This mechanism also happens to be very easy to use; a Java interface is available, and it is mostly about implementing it and registering the class that implements it.

Implementing the interface

The java interface to implement is the following one:
package com.collaxa.cube.ws;

public abstract interface HeaderHandler
{
  void invoke(com.collaxa.cube.engine.types.bpel.CXPartnerLink pLnk, 
              java.lang.String operation, 
              java.util.Map payload, 
              java.util.Map header, 
              java.util.Map callProperties);
}
      
If you are working with the BPEL Designer of JDeveloper, this interface is available from the library named OraBPEL.
It is a very simple interface, holding just one method, using as parameters all you need about the WSIF call being performed.

Registering the Handler

The registration happens in the BPEL Suitcase, as follow:
<BPELSuitcase>
   <BPELProcess id="ProcessWithHeaderHandler" src="ProcessWithHeaderHandler.bpel">
      <partnerLinkBindings>
         <partnerLinkBinding name="client">
            <property name="wsdlLocation">ProcessWithHeaderHandler.wsdl</property>
         </partnerLinkBinding>
         <partnerLinkBinding name="GreetingsPL">
            <property name="wsdlLocation">GreetingsPL.wsdl</property>
            <property name="requestHeaderHandlers">bpel.util.OutboundHandler</property>
            <property name="responseHeaderHandlers">bpel.util.InboundHandler</property>
         </partnerLinkBinding>
      </partnerLinkBindings>
   </BPELProcess>
</BPELSuitcase>
      
Notice that you can have distinct handlers for request and response, that makes it very convenient. Those two handlers can actually be implemented by the same Java class.
Once written and compiled, those classes must be dropped somewhere in the classpath of the BPEL Process Manager. Archiving them into a jar-file dropped into [BPEL_HOME]/integration/orabpel/system/appserver/oc4j/j2ee/home/applib is a way to do it. This being done, everytime the WSIF invocation described in the Suitcase displayed above, your custom implementation of the HeaderHandler will be processed.

Example

We are going to illustrate the above with some simple example, in order to show how simple this is. The HEaderHandler can be used for several purpose, we are going to briefly describe three options.
BPEL Process In this case, and for the following ones, we have a very simple BPEL Process, from which we invoke a ridiculously simple Web Service through a Partner Link.
The partner link we talk about is the one labeled GreetingsPL on the diagram.
Behind the Partner Link is a Java Web Service, the implementing class is like
			      
 package greetings;

 public class Greetings 
 {
   public static String sayHello(String name)
   { return "Hello " + name + "!"; }
 }
			      
No big deal indeed.
This Web Service if of course exposed through a WSDL document, available through HTTP.

Spying

Let us first try to see what is going back and forth during this WSIF invocation.
We start from a class like this one:
package bpel.util;

import com.collaxa.cube.ws.HeaderHandler;
import com.collaxa.cube.engine.types.bpel.CXPartnerLink;
import com.collaxa.cube.xml.dom.CubeDOMElement;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class CustomHeaderHandler implements HeaderHandler
{
  public CustomHeaderHandler()
  {
    System.out.println("Creating the CustomHeaderHandler");
  }
  
  public void invoke(CXPartnerLink pLink, 
                     String operation, 
                     Map payload, 
                     Map header, 
                     Map callProps)
  {
    System.out.println("in the CustomHeaderHandler, operation is [" + operation + "]");

    if (pLink != null)
    {
      System.out.println("Partner Link:" + pLink.toString());
      Map plProps = pLink.getProperties();
      if (plProps != null)
      {
        try { dumpMap(plProps, "PartnerLink"); }
        catch (Exception e) 
        { System.out.println("PartnerLink dump:" + e.toString()); }
      }
      else
        System.out.println("PartnerLink map is null");
    }
    else
      System.out.println("Partner Link is null!");
    
    if (payload != null)
      System.out.println("The payload has " + payload.size() + " elements");
    else
      System.out.println("payload is null");
    if (header != null)
      System.out.println("The header has " + header.size() + " elements");
    else
      System.out.println("header is null");
    if (callProps != null)
      System.out.println("The callProps has " + callProps.size() + " elements");
    else
      System.out.println("callProps is null");
    
    try { dumpMap(payload, "payload"); } 
    catch (Exception e)
    {
      System.out.println("Error dumping payload:");  
      System.out.println(e.toString());
    }
    try { dumpMap(header, "header"); } 
    catch (Exception e)
    {
      System.out.println("Error dumping header:");  
      System.out.println(e.toString());
    }
    try { dumpMap(callProps, "callProps"); } 
    catch (Exception e)
    {
      System.out.println("Error dumping callProps:");  
      System.out.println(e.toString());
    }
  }
  
  private void dumpMap(Map map, String mapName) throws Exception
  {
    ...
  }
}
      
This class is basically spitting out the content of the different parameters it receives.
Let us extend this class in two ways:
package bpel.util;
import com.collaxa.cube.engine.types.bpel.CXPartnerLink;
import com.collaxa.cube.xml.dom.CubeDOMElement;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Node;

public class OutboundHandler extends CustomHeaderHandler 
{
  public OutboundHandler()
  {
    super();
  }
  public void invoke(CXPartnerLink pLink, 
                     String operation, 
                     Map payload, 
                     Map header, 
                     Map callProps)
  {
    System.out.println("On the way OUT");
    System.out.println("=================================>>>");
    super.invoke(pLink, operation, payload, header, callProps);
  }
}
      
and
package bpel.util;
import com.collaxa.cube.engine.types.bpel.CXPartnerLink;
import java.util.Map;

public class InboundHandler extends CustomHeaderHandler 
{
  public InboundHandler()
  {
    super();

  }
  public void invoke(CXPartnerLink pLink, 
                     String operation, 
                     Map payload, 
                     Map header, 
                     Map callProps)
  {
    System.out.println("On the way IN");
    System.out.println("<<<=================================");
    super.invoke(pLink, operation, payload, header, callProps);
  }
}
      
Such handlers would be registered in the BPEL Suitcase, just like already mentionned above.
The execution would produce an output like this one:
06/04/04 10:38:38 Creating the CustomHeaderHandler
06/04/04 10:38:38 On the way OUT
06/04/04 10:38:38 =================================>>>
06/04/04 10:38:38 in the CustomHeaderHandler, operation is [sayHello]
06/04/04 10:38:38 Partner Link:<partnerLink name="GreetingsPL" partnerLinkType="
{http://greetings/Greetings.wsdl}GreetingsPortType_PL">
 <myRole name="null">
  <ServiceName>null</ServiceName>
  <PortType>null</PortType>
  <Address>null</Address>
 </myRole>
 <partnerRole name="GreetingsPortType_Role">
  <ServiceName>{http://greetings/Greetings.wsdl}GreetingsWS</ServiceName>
  <PortType>{http://greetings/Greetings.wsdl}GreetingsPortType</PortType>
  <Address>null</Address>
 </partnerRole>
 <conversationId>bpel://localhost/default/ProcessWithHeaderHandler~1.0/19401-BpInv0-BpSeq0.3-3</conversationId>
 <properties>{}</properties>
</partnerLink>
06/04/04 10:38:38 Dumping PartnerLink
06/04/04 10:38:38 The payload has 1 elements
06/04/04 10:38:38 The header has 0 elements
06/04/04 10:38:38 The callProps has 10 elements
06/04/04 10:38:38 Dumping payload
06/04/04 10:38:38 Key:name
06/04/04 10:38:38 Value is a com.collaxa.cube.xml.dom.CubeDOMElement
06/04/04 10:38:38 Value:
<name>Oracle</name>
06/04/04 10:38:38 Dumping header
06/04/04 10:38:38 Dumping callProps
06/04/04 10:38:38 Key:is-initial-call
06/04/04 10:38:38 Value is a java.lang.Boolean
06/04/04 10:38:38 Value:true
06/04/04 10:38:38 Key:input-header-variables
06/04/04 10:38:38 Value is a java.util.HashMap
06/04/04 10:38:38   ** Hashmap of 0 entries **
06/04/04 10:38:38 Dumping input-header-variables
06/04/04 10:38:38   ** End of dump for input-header-variables **
06/04/04 10:38:38 Key:parentId
06/04/04 10:38:38 Value:19401
06/04/04 10:38:38 Key:completionPersistPolicy
06/04/04 10:38:38 Null value for completionPersistPolicy
06/04/04 10:38:38 Key:process-id
06/04/04 10:38:38 Value is a com.oracle.bpel.client.BPELProcessId
06/04/04 10:38:38 Value:bpel://localhost/default/ProcessWithHeaderHandler~1.0/
06/04/04 10:38:38 Key:rootId
06/04/04 10:38:38 Value:19401
06/04/04 10:38:38 Key:conversationId
06/04/04 10:38:38 Value:bpel://localhost/default/ProcessWithHeaderHandler~1.0/19401-BpInv0-BpSeq0.3-3
06/04/04 10:38:38 Key:location
06/04/04 10:38:38 Value:http://oliv-lap:9700/very-usefull/GreetingsWS
06/04/04 10:38:38 Key:priority
06/04/04 10:38:38 Value:3
06/04/04 10:38:38 Key:work-item-key
06/04/04 10:38:38 Value is a com.collaxa.cube.engine.core.WorkItemKey
06/04/04 10:38:38 Value:19401-BpInv0-BpSeq0.3-3
06/04/04 10:38:38 httpHeaders is null
06/04/04 10:38:39 Creating the CustomHeaderHandler
06/04/04 10:38:39 On the way IN
06/04/04 10:38:39 <<<=================================
06/04/04 10:38:39 in the CustomHeaderHandler, operation is [sayHello]
06/04/04 10:38:39 Partner Link:<partnerLink name="GreetingsPL" partnerLinkType="
{http://greetings/Greetings.wsdl}GreetingsPortType_PL">
 <myRole name="null">
  <ServiceName>null</ServiceName>
  <PortType>null</PortType>
  <Address>null</Address>
 </myRole>
 <partnerRole name="GreetingsPortType_Role">
  <ServiceName>{http://greetings/Greetings.wsdl}GreetingsWS</ServiceName>
  <PortType>{http://greetings/Greetings.wsdl}GreetingsPortType</PortType>
  <Address>null</Address>
 </partnerRole>
 <conversationId>bpel://localhost/default/ProcessWithHeaderHandler~1.0/19401-BpInv0-BpSeq0.3-3</conversationId>
 <properties>{}</properties>
</partnerLink>
06/04/04 10:38:39 Dumping PartnerLink
06/04/04 10:38:39 The payload has 1 elements
06/04/04 10:38:39 header is null
06/04/04 10:38:39 The callProps has 11 elements
06/04/04 10:38:39 Dumping payload
06/04/04 10:38:39 Key:name
06/04/04 10:38:39 Value is a com.collaxa.cube.xml.dom.CubeDOMElement
06/04/04 10:38:39 Value:
<name>Oracle</name>
06/04/04 10:38:39 header is null
06/04/04 10:38:39 Dumping callProps
06/04/04 10:38:39 Key:is-initial-call
06/04/04 10:38:39 Value is a java.lang.Boolean
06/04/04 10:38:39 Value:true
06/04/04 10:38:39 Key:input-header-variables
06/04/04 10:38:39 Value is a java.util.HashMap
06/04/04 10:38:39   ** Hashmap of 0 entries **
06/04/04 10:38:39 Dumping input-header-variables
06/04/04 10:38:39   ** End of dump for input-header-variables **
06/04/04 10:38:39 Key:parentId
06/04/04 10:38:39 Value:19401
06/04/04 10:38:39 Key:completionPersistPolicy
06/04/04 10:38:39 Null value for completionPersistPolicy
06/04/04 10:38:39 Key:process-id
06/04/04 10:38:39 Value is a com.oracle.bpel.client.BPELProcessId
06/04/04 10:38:39 Value:bpel://localhost/default/ProcessWithHeaderHandler~1.0/
06/04/04 10:38:39 Key:rootId
06/04/04 10:38:39 Value:19401
06/04/04 10:38:39 Key:conversationId
06/04/04 10:38:39 Value:bpel://localhost/default/ProcessWithHeaderHandler~1.0/19401-BpInv0-BpSeq0.3-3
06/04/04 10:38:39 Key:location
06/04/04 10:38:39 Value:http://oliv-lap:9700/very-usefull/GreetingsWS
06/04/04 10:38:39 Key:httpResponseHeaders
06/04/04 10:38:39 Value is a java.util.HashMap
06/04/04 10:38:39   ** Hashmap of 5 entries **
06/04/04 10:38:39 Dumping httpResponseHeaders
06/04/04 10:38:39 Key:content-length
06/04/04 10:38:39 Value: 514
06/04/04 10:38:39 Key:connection
06/04/04 10:38:39 Value: Close
06/04/04 10:38:39 Key:content-type
06/04/04 10:38:39 Value: text/xml; charset=utf-8
06/04/04 10:38:39 Key:server
06/04/04 10:38:39 Value: Oracle Application Server Containers for J2EE 10g (10.1.2.0.2)
06/04/04 10:38:39 Key:date
06/04/04 10:38:39 Value: Tue, 04 Apr 2006 17:38:39 GMT
06/04/04 10:38:39   ** End of dump for httpResponseHeaders **
06/04/04 10:38:39 Key:priority
06/04/04 10:38:39 Value:3
06/04/04 10:38:39 Key:work-item-key
06/04/04 10:38:39 Value is a com.collaxa.cube.engine.core.WorkItemKey
06/04/04 10:38:39 Value:19401-BpInv0-BpSeq0.3-3
06/04/04 10:38:39 httpHeaders is null
      
Notice the method name (operation), the content of the Partner Link, and the values of the payloads.

Overriding

Now, from the same kind of code, we are going to modify the parameters of the call, before they reach their target.
package bpel.util;
import com.collaxa.cube.engine.types.bpel.CXPartnerLink;
import com.collaxa.cube.xml.dom.CubeDOMElement;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Node;

public class OutboundHandler extends CustomHeaderHandler 
{
  public OutboundHandler()
  {
    super();
  }
  public void invoke(CXPartnerLink pLink, 
                     String operation, 
                     Map payload, 
                     Map header, 
                     Map callProps)
  {
    System.out.println("On the way OUT");
    System.out.println("=================================>>>");
    super.invoke(pLink, operation, payload, header, callProps);
    
    System.out.println("===== Now, something specific =====");
    // Overriding the payload...
    if (pLink.getName().equals("GreetingsPL")) // Then we override
    {
      CubeDOMElement cde = (CubeDOMElement)payload.get("name");
      Node txt = cde.getFirstChild();
      txt.setNodeValue("Oracle BPEL PM");
      payload.put("name", cde); // Here we are
    }        
    System.out.println("===== End of the Dump =====");
  }
}
      

Redirecting

Applying the same kind of patterns, you can also modify parameters that will change the actual behavior of the call itself.
Let's say we have published another Web Service, exposing a java class like this:
package greetings;

public class Greetings 
{
  public static String sayHello(String name)
  { return "This is a dummy WS, " + name + ". Implemented for tests."; }
}      
      
We have published this web service in such a way that its url is http://oliv-lap:9700/very-very-usefull/ServiceForRedirection. We have the possibility to change the location parameter of the call properties, so that this one will be substituted to the one originally targeted.
package bpel.util;
import com.collaxa.cube.engine.types.bpel.CXPartnerLink;
import com.collaxa.cube.xml.dom.CubeDOMElement;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Node;

public class OutboundHandler extends CustomHeaderHandler 
{
  public OutboundHandler()
  {
    super();
  }
  public void invoke(CXPartnerLink pLink, 
                     String operation, 
                     Map payload, 
                     Map header, 
                     Map callProps)
  {
    System.out.println("On the way OUT");
    System.out.println("=================================>>>");
    super.invoke(pLink, operation, payload, header, callProps);
    
    System.out.println("===== Now, something specific =====");
    // Overriding the payload...
    if (pLink.getName().equals("GreetingsPL")) // Then we override
    {
      CubeDOMElement cde = (CubeDOMElement)payload.get("name");
      Node txt = cde.getFirstChild();
      txt.setNodeValue("Oracle BPEL PM");
      payload.put("name", cde); // Here we are
      
      // Changing the location
      String location = (String)callProps.get("location");
      System.out.println("Original location is [" + location + "]");
      callProps.put("location", "http://oliv-lap:9700/very-very-usefull/ServiceForRedirection");
    }        
    System.out.println("===== End of the Dump =====");
  }
}
      

More

...


End of Document