# HeaderHandlers and WSIF Hooks

Olivier Le Diouris
April 4th, 2006.

### Resources

- The samples used in the document

## 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.

| 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

...