Version 4.0 - July, 2007
Contents
Introduction
Getting Set Up
Custom Function Framework
Simulating Custom Functions
Installing Custom Functions
Java Test Client

Creating and Using Custom XPath Functions

Introduction

Every now and then, while designing a process, most folks run into a situation where the standard XPath or other functions available to a BPEL process just don't do everything one might like.  A typical example of this is taking a valid XML string that describes a DOM object and using that to initialize or populate a BPEL variable.  This usage is a good candidate for a Custom Function, and this article describes creating, configuring, installing and using just such a function.

Custom functions can pretty much do anything you can code in Java.  You can think of custom functions as a library of methods that you create and make available during process runtime to the ActiveBPEL® server or during process simulation in the ActiveBPEL® Designer.

Objectives

Getting Set Up

The files provided with this article are contained in custom_functions.zip. You may extract this archive into a directory of your choosing. The archive contains numerous directories with an Eclipse Java Project, an ActiveBPEL Designer project and various support, test and deployment files.

If you plan to use the ActiveBPEL Designer to create, simulate or test your process, start the ActiveBPEL Designer, select File / Import / General / Existing Projects into Workspace and navigate to the following project directory

/custom_functions

This opens a project called custom_functions, which should appear in the Navigator View.

With the project successfully imported, switch to the Web References view and select the Add a Web Reference icon. Select the Browse Projects... button and navigate to the wsdl directory under Complex Exchange. Add the custom_function.wsdl file that you see there. It then appears in the Web References View.

If you’re using Eclipse for your client application development, run Eclipse, select File / Import / General / Existing Projects into Workspace and navigate to the following Eclipse project directory

/custom_functions

This opens a project called custom_functions, with various supporting directories. The project is self-sufficient and contains the source files needed to build our sample custom functions as well as a test client application.

One manual step you may need to perform is to create an AESAMPLES_LIB variable for the project's Java Build Path (i.e., you may see "Unbound classpath variable"; errors for the project). See the Sample on Java Library Dependencies for more information as it applies to your platform.

If you’re not using Eclipse, you can still use the files in this sample. There is a build.xml Ant script in the project directory root that contains build targets for compiling the source and running the client application - all described in detail below. Here’s the archive’s directory structure.

custom_functions      < build.xml
├─── doc
├─── src
│    └─── org
│         └─── activebpel
│              └─── samples
│                   └─── custom_function   \
│                        ├─── client        } < Source Files for
│                        ├─── type         /     Java test client
│                        └─── xmlstring       < Custom Function code
│                             └─── test       < Custom Function tester
└─── support           < Pick List file for ActiveBPEL Designer
     └─── bpel_process < Complex sample data for ActiveBPEL Designer simulation
          ├─── bpel    < BPEL source custom_function.bpel
          ├─── bpr     < Process Deployment Descriptor (PDD) file
          └─── wsdl    < Web Service definition custom_function.wsdl

Using the Custom Function Framework

The ActiveBPEL server is extended through the use of custom functions, which are typically evaluated in the context of a Copy Operation in an Assign activity.  Here is a typical example:

...
 <assign name="Test_ElementToXMLString_CustomFunction">
  <copy>
   <from>aecf-xmlstring:elementToXMLString
         ($brandYearTypeMessage.brandYearType)
   </from>
   <to variable="myString"/>
  </copy>
 </assign>
...

Note the expression contents - aecf-xmlstring:elementToXMLString(...).  This is one of the custom functions we're going to create and the syntax above shows how it is used in the BPEL process definition. Custom functions are identified by a QName comprising the namespace and the (local) function name - in this case, "aecf-xmlstring" and "elementToXMLString", respectively.

The ActiveBPEL server uses a Java framework that supports custom function extensions.  This framework is a set of Java interfaces and classes made available for your use. IAeFunction and IAeFunctionContext are implemented to create the objects that are accessible to the engine at run-time.  AeFunctionCallException and AeUnresolvableException are used to signal fault conditions.  These types are all resolved via the org.activebpel.rt package in the ae_rtbpel.jar file.

Your first task is to create a function context, which is a new class that implements the IAeFunctionContext interface. This class exposes one public method: getFunction( String ), which is responsible for returning an object of a type that implements the IAeFunction interface.  The method uses the String argument to indicate the local name of the function and then select that object.  You may implement multiple custom functions via one function context class.  getFunction() throws an AeUnresolvableException if it is unable to locate the desired function by name.

Your second task is to create the actual custom functions, each of which implements the Function interface. These also expose one single public method: call(IAeFunctionExecutionContext, List), which optionally parses the arguments passed to the function (via the List argument) and performs the function's task, which you will create.  This function may optionally use the function execution IAeFunctionExecutionContext as needed.  It is probably good organization to place the function context class and the functions it provides into a single .java file. The methods that do the actual work can be placed into another .java file, which can be separately tested.  Individual custom function methods should throw AeFunctionCallException, constructed to contain the root exception message, if they encounter a problem that prevents them from finishing their task.

Once you have created your function context and function classes, and tested the classes that do the actual custom function work, you'll create a .jar file that can be deployed to an ActiveBPEL server (and optionally made available to the ActiveBPEL Designer for simulation, if desired). 

For an example of the implementation described above, see the following source files found in this archive. Also see the aecf-xmlstring.jar file itself:

Note also the TestCustomXMLStringFunctions class that is used to test the functions independently of the engine or simulator.

Simulating Custom Functions

This section is optional.  If you're not using ActiveBPEL Designer, you can skip to the next section.

Once you have your custom functions coded and tested standalone, you'll need to test them in the context of a running BPEL Process.  You can create a process and deploy it immediately to the engine or, if you're using ActiveBPEL Designer for your process development, you can run the process in the simulator to verify that your custom functions are performing their task(s) correctly.

To use custom functions within the Expression or Query Builder dialogs, you can either type the function invocations by hand, or you can create a Pick List definition that tells ActiveBPEL Designer how to display the function syntax automatically.  This definition is an XML document that specifies the namespace, preferred prefix, function name and other ancillary information for each of your custom functions.  See the file custom_xpath_xmlstring_pickList.xml for an example of a Pick List definition.  The elements and attributes of particular note are:

To make the new custom functions available to ActiveBPEL Designer, run it and select Window / Preferences / ActiveBPEL / Custom Functions.  This brings up the Custom Functions dialog.  The top portion of this dialog is used to define parameters that make your custom functions available for selection when using the Expression Builder.  The lower portion is used to install the custom function jar file itself. To install the pick list, select the top Add... button and navigate to the location of your pick list definition document. Click Open to install the pick list.

To make your compiled functions accessible by ActiveBPEL Designer, select the bottom Add... button.  This brings up the Add Custom Function Details dialog.  Enter the Namespace defined in the Pick List definition file. Next, enter the full package/classname of your custom function context class (e.g., org.activebpel.samples.custom_function.xmlstring.XMLStringFunctionContext ). Finally, use the Browse... button to navigate to and Open the .jar file containing your custom function code.  Click OK and then select your custom function entry from the list to highlight it.  Select Test and verify that the system can locate and load your custom function context class.  If this step fails, select Edit... and ensure that you have entered the information correctly.

Now your custom functions are ready for use in the Simulator.

To get familiar, you can perform the steps above with the files included in the archive for this sample.  Install the Pick List and .jar file for the Custom XML String Functions and open the custom_function.bpel process to test.  Sample data for this process is located in the bpel_process directory of the archive.  If you see errors in the console, be sure to use Window / Preferences / ActiveBPEL / Simulation to un-check the "Validate Input/Output message against schema" option.


Installing Your Custom Functions in the ActiveBPEL Engine

NOTE: If you are using ActiveBPEL Enterprise, you can add custom functions from the Administration Console, and do not need to deploy the functions or edit aeEngineConfig.xml.

Installation of your custom functions into the ActiveBPEL Engine is a three-step process:

  1. Deploy (copy) the custom function .jar file to the shared/lib directory for your engine installation.
  2. Edit aeEngineConfig.xml to add the information about the custom functions.
  3. Restart the ActiveBPEL Engine.

To tell the ActiveBPEL Engine about the new custom XPath function, edit the file $CATALINA_HOME/bpr/aeEngineConfig.xmlaeEngineConfig.xml contains all the configuration information needed by the ActiveBPEL Engine at startup time, so be sure to make a backup copy of it before editing.

Locate the following line

<entry name="FunctionContexts"/>

and change it to

<entry name="FunctionContexts">
<entry name="Custom XML String Functions">
<entry name="Prefix" value="aecf-xmlstring"/>
<entry name="Namespace"
value="http://docs.active-endpoints.com/activebpel/sample/customFunction/2006/09"/>
 <entry name="Class"
value="org.activebpel.samples.custom_function.xmlstring.XMLStringFunctionContext"/>
</entry>
</entry>

You may add as many function sets as you like.  The Class value is your function context class.  The name is just an identifier for your context and only appears within the config file.  The Prefix should match the prefix specified in your Pick List definition.


Java Test Client Application

Required jars

To implement a Java client that we use to test our custom functions, we need resources that are defined in a number of Java library archives. All of these should be available via the AESAMPLES_LIB environment variable (or Eclipse classpath variable) discussed here.

Java Client Application Sample Source Code

To test the two custom functions in this archive, you can use the Java client application that is provided: org.activebpel.samples.custom_function.client.TestClient.  This application sends an xml string and a complex data message to a BPEL process and receives a complex data response.  If you've looked at the Complex Exchange Sample, this code and the associated data types should look familiar.

The significant section of code for this test is the following method:


public String callService() throws Exception {
  // Call BPEL process Web service using RPC
  Call call = createCall();
  String result = null;
  try {
    // This is the string our test BPEL will convert to DOM (and return).
    //
    String xmlString = "<idAmountInstock " +
     "xmlns:tns=\"http://docs.active-endpoints.com/activebpel/
     sample/wsdl/complex_exchange/2006/09/complex_exchange.wsdl\" " +
      "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
      "xsi:type=\"tns:IdAmountInstock\">" +
        "<id>ThisWillBeReplaced</id>" +
        "<amount>99</amount>" +
        "<instock>false</instock>" +
      "</idAmountInstock>" ;
    BrandYearType myID =
      new BrandYearType("Honda", 2004,
                            new Type("Sedan", "Red", "Automatic"));
    IdAmountInstock response =
        ((IdAmountInstock)call.invoke(new Object[] { xmlString, myID }));
    StringBuffer resBuf = new StringBuffer( "ID: " + response.getId());
    resBuf.append( "\nAmount: " + response.getAmount());
    resBuf.append( "\nInstock: " + response.isInStock());
    result = resBuf.toString();
  }
  catch (Exception e) {
    result = "Exception seen: " + e.toString();
    e.printStackTrace();
  }

  return result;
}

The xmlString value that is constructed contains an XML representation of a complex data type.  Our test process is going to use our custom functions to convert this string to a DOM object and return the result as a complex response.  Similarly, the myID value is sent as a complex type and will be converted to an XML string representation and placed into the response's <id> element, which is an xsd:string.  Thus we exercise both the xmlStringToElement() and elementToXMLString() methods with a single web service invocation of our process.

To run the test application, you must first deploy the custom_function.bpel process to an ActiveBPEL server, then run the TestClient.java application. Deploy using RPC and a service name of complexToBpelPartnerLinkService, or just export the PDD provided.

NOTE: if you have deployed BPEL Process for the complex_exchange sample, un-deploy that process before attempting to deploy this one, as they may conflict.

The output of our Java client application, when all is complete, looks as follows:

ID: <brandYearType
     xmlns:abpel-deser1="http://docs.active-endpoints.com/
     activebpel/sample/wsdl/custom_function/2006/09/custom_function.wsdl"
     xmlns:ns1="http://docs.active-endpoints.com/activebpel/
     sample/wsdl/custom_function/2006/09/custom_function.wsdl"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:type="abpel-deser1:BrandYearType">
      <brand>Honda</brand>
      <year>2004</year>
      <type xmlns:ns2="http://docs.active-endpoints.com/
      activebpel/sample/wsdl/custom_function/2006/09/custom_function.wsdl"
        xsi:type="abpel-deser1:Type">
        <style>Sedan</style>
        <color>Red</color>
        <transmissionType>Automatic</transmissionType>
      </type>
    </brandYearType>
Amount: 99.0
Instock: false

(note: linefeeds and indentation have been added for readability here - the ID: parameter should appear as one long line.)

Note that the ID value, which is specified as xsd:string in the WSDL that defines our process, has been populated with an XML string representation that corresponds to the value (object) we sent in our request. Likewise, the String value we sent (as the simpleString message part) has been converted to an object of type IdAmountInStock, and contains the amount (99) and instock (false) values we sent.

Naturally, we would want a more thorough test of our custom functions, passing in 'bad' values and test for various potential issues.  You can experiment by modifying this code for that purpose, and use that as a guide to testing your own custom functions.