Call a COM Component from BPEL

Olivier Le Diouris
May 4th, 2006.

Resources

Description

The goal here is to be able to call a COM component (dll, ocx, etc) from BPEL.
We are going to build a Java wrapper to call the COM components, using JACOB (JAva-COM-Bridge), that can be downloaded from the link above. We just need - for the purpose described in this document - jacob 1.10.1, and not necessary jacobgen.
We will assume that we know the name of the components we want to use, as well as the names of the methods we want to call.

How it works

We are going to follow the following steps:

The COM Component

We are going to illustrate this document with an example, starting from a dll called PhysServer.dll. It converts Celsius degrees to Fahrenheit and vice-versa. It is written in Visual Basic, here is the code.
This dll needs to be registered:
      
 Prompt> regsvr32 .\PhysServer.dll      
      
In case you need to unregister it, use
      
 Prompt> regsvr32 /u .\PhysServer.dll      
      

The Java wrapper

To write, compile and run the Java wrapper around the COM component, you need to put jacob.jar in your classpath, and - for the runtime - you need to have jacob.dll in the path.
Setting the java.library.path variable at the VM level would take care of that.
A basic wrapper around this COM component would look like this:
      
 package wrapper;
 import com.jacob.com.Dispatch;
 import com.jacob.com.Variant;

 public class Main 
 {
   public static void main(String[] args)
   {
     Dispatch test = new Dispatch("PhysServer.Temperature");
     Dispatch.put(test, "Fahrenheit", new Variant(70));
     System.out.println("Calling COM function GetCelsius for 70...");
     System.out.println("70 Fahrenheit = " + Dispatch.call(test, "GetCelsius") + " Celsius"); 
     
     Dispatch.put(test, "Celsius", new Variant(120));
     System.out.println("120 Celsius   = " + Dispatch.call(test, "GetFahrenheit") + " Fahrenheit"); 
   }
 }
      
Such a class would be run as follow:
 java -classpath .\classes;[JACOB_HOME]\jacob.jar -Djava.library.path=[JACOB_HOME] wrapper.Main 
 Calling COM function GetCelsius for 70...
 70 Fahrenheit = 21.1111111111111 Celsius
 120 Celsius   = 248 Fahrenheit
      

The Web Service

Now we can run the COM component from Java, we can write a class, to be ultimately exposed as a Web Service. We want to expose 2 methods, one convert Celsius to Fahrenheit, and another one for Fahrenheit to Celsius.
      
 package wrapper;
 import com.jacob.com.Dispatch;
 import com.jacob.com.Variant;

 public class TempUtil
 {
   public static double fahrenheit2celsius(double f)
   {
     Dispatch test = new Dispatch("PhysServer.Temperature");
     Dispatch.put(test, "Fahrenheit", new Variant(f));
     return Dispatch.call(test, "GetCelsius").getDouble();
   }
  
   public static double celsius2fahrenheit(double c)
   {
     Dispatch test = new Dispatch("PhysServer.Temperature");
     Dispatch.put(test, "Celsius", new Variant(c));
     return Dispatch.call(test, "GetFahrenheit").getDouble();
   }
 }
      
There is nothing in such a class that would prevent it from being exposed as a Java Web Service. It would come with a WSDL document, and from now on, it is just another regular Web Service. As such, it can be consumed from BPEL through a Partner Link.
The only thing not to forget is to start the server where this service is deployed on with the appropriate java.library.path variable, for jacob.dll to be found properly.

The rest of the process

The rest of the process is absolutely identical to any other BPEL Process consuming any Web Service. No difference.

Not so standard, but less overhead: bpelx:exec

Java code can also be called directly from the BPEL Process, using the bpelx:exec extension:
      
  <bpelx:exec import="org.w3c.dom.Element"/>
  <bpelx:exec import="com.jacob.com.Dispatch"/>
  <bpelx:exec import="com.jacob.com.Variant"/>
  <bpelx:exec name="DirectCOMCall" language="Java" version="1.4">
    <![CDATA[
    try
    {
      Element input = (Element)getVariableData("inputVariable",
                                               "payload",
                                               "/client:BPELcallsCOMProcessRequest/client:input");
      String value = input.getNodeValue();
      double celsius = Double.parseDouble(value);
      Dispatch test = new Dispatch("PhysServer.Temperature");
      Dispatch.put(test, "Celsius", new Variant(celsius));
      double fahrenheit = com.jacob.com.Dispatch.call(test, "GetFahrenheit").getDouble();  
      setVariableData("outputVariable", 
                      "payload", 
                      "/client:BPELcallsCOMProcessResponse/client:result", 
                      value + " Celsius = " + Double.toString(fahrenheit) + " Fahrenheit");
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }]]>
  </bpelx:exec>
      
This is obviously not as flexible as before, but it avoid the SOAP/HTTP overhead.
Note: I unarchived the jacob.jar file into [BPEL_HOME]/integration/orabpel/system/classes


JacobGen

JacobGen provides another approach. It generates java wrapper for a COM component, providing the possibility not to mess with the COM Component itself. You feel like working in the Java world only. We will talk about that in another document.


End of Document