BPEL Process definitions frequently include parameters that influence the flow through the process. Sometimes these parameters are embedded as hard coded values sometimes variables dynamically retrieved from a PartnerLink or as part of the initial request. These parameters can be used for various purposes:
- currency exchange rate for calculating prices in specific target currencies
- threshold values that determine whether or not human intervention (workflow task) is required or a notification should be sent
- decision input: parameters used for deciding when a certain branch should be executed or not
- a nicely formatted string with the current date to be used in email notifications
and many more usages that influence the internal logic or interaction of the BPEL process.
When such parameters are hard-coded, the only way to apply a change to them is by changing the process definition and redeploying it to the SOA engine. Parameters can also be dynamically retrieved in every single process instance, for example by reading them from a file or out of a database.
Depending on the rate of change – and the ratio of parameter changes and process instances – we choose either approach. Changing the process and redeploying it requires manual intervention – clearly something we would not like to do very often. Retrieving the values in every instance can add up to a lot of overhead, depending on the number of process instances and the performance requirements.
I was thinking about a way that allows me to change dynamic process parameters without having to change and redeploy the process while minimizing the overhead. Is there a way to keep those parameter values in memory, have them quickly available to all process instances yet be able to modify them.
I have come up with a solution that I thought satisfied these conditions. The solution is based on a Singleton Java Object that holds the configuration parameters and can be accessed by all BPEL process instances using a WSIF JavaBinding – that provides much faster invocations than JCA/JDBC/Database, EJB or WebService solutions. The values in the singleton object can be set through a BPEL process or if so desired through JMX/MBeans. After I had created most of the solution, I ran into Matt Wright’s blog – Using BPEL for Dynamic Content Based Routing – that describes a similar solution to different challenge (how to dynamically set the end point for a service call).
The steps for implementing my solution are these:
- create the Java classes that implement the Configuration Parameters Manager
- create a WSDL for the Java based ConfigurationManager service, using WSIF to create a binding to the Java class
- copy the Java classes to the BPEL_HOME\bpel\system\classes directory
- create a BPEL process that can set the value of Configuration Parameters
- make use of the ConfigurationManager singleton Java object to get the values of dynamic parameters to be used in BPEL process instances
1. Create the Java Classes
First, create the Java Class that implements the singleton pattern and holds the values of Configuration Parameters:
package nl.amis.configuration;
import java.util.HashMap;
import java.util.Map;
/**
* This class implements the Singleton pattern.
* There will only be a single instance of this class in the JVM. Anyone can call ConfigurationManager.getConfigurationManager to
* get hold of that instance.
*/
public class ConfigurationManagerServer {
Map<String,String> settings = new HashMap();
public String getSetting(String key) {
return settings.get(key);
}
public void setSetting(String key, String value) {
settings.put(key, value);
}
private ConfigurationManagerServer()
{
}
public static ConfigurationManagerServer getConfigurationManagerServer()
{
return ref;
}
private static ConfigurationManagerServer ref= new ConfigurationManagerServer();
}
The WSIF invocations do not work for a private constructor nor can they cater for the getConfigurationManager() server factory method. They need access to a ‘normal’ class. So we create a helper class ConfigurationManager that provides the indirect access:
package nl.amis.configuration;
public class ConfigurationManager {
public String getSetting(String key) {
return ConfigurationManagerServer.getConfigurationManagerServer().getSetting(key);
}
public void setSetting(String key, String value) {
ConfigurationManagerServer.getConfigurationManagerServer().setSetting(key, value);
}
}
The desired functionality to set Configuration Manager parameter values and retrieve them are provided by instances of this ‘normal’ class that rely on the singleton ConfigurationManagerServer to make them happen.
2. Create a WSDL for the Java based service, using WSIF to create a binding to the Java class
In order to invoke the ConfigurationManager from a BPEL process, we need to associate it with the process through a PartnerLinkType definition. For that, we need a WSDL document that describes the ConfigurationManager service. Typically WSDL documents describe SOAP based WebServices. However, through the Apache WSIF extensions to WSDL, we can define a WSDL document that describes a service implemented and accessed in various technologies and protocols.
The WSIF based WSDL for the ConfigurationManager service could look like this:
<definitions
name="DynamicConfigurationManager"
targetNamespace="http://configuration.amis.nl/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://configuration.amis.nl/"
xmlns:java="http://schemas.xmlsoap.org/wsdl/java/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
>
<message name="DynamicConfigurationManager_getSetting">
<part name="key" type="xsd:string"/>
</message>
<message name="DynamicConfigurationManager_getSettingResponse">
<part name="value" type="xsd:string"/>
</message>
<message name="DynamicConfigurationManager_setSetting">
<part name="key" type="xsd:string"/>
<part name="value" type="xsd:string"/>
</message>
<portType name="DynamicConfigurationManager">
<operation name="getSetting">
<input message="tns:DynamicConfigurationManager_getSetting"/>
<output message="tns:DynamicConfigurationManager_getSettingResponse"/>
</operation>
<operation name="setSetting">
<input message="tns:DynamicConfigurationManager_setSetting"/>
</operation>
</portType>
<binding name="DynamicConfigurationManagerWSIF" type="tns:DynamicConfigurationManager">
<java:binding/>
<format:typeMapping encoding="Java" style="Java">
<format:typeMap typeName="xsd:string" formatType="java.lang.String&qu ot;/>
</f ormat:typeMapping>
<operation name="getSetting">
<java:operation methodName="getSetting" methodType="instance" parameterOrder="key" returnPart="value"/>
<input/>
<output/>
</operation>
<operation name="setSetting">
<java:operation methodName="setSetting" methodType="instance"/>
<input/>
</operation>
</binding>
<service name="DynamicConfigurationManager">
<port name="DynamicConfigurationManagerWSIFPort" binding="tns:DynamicConfigurationManagerWSIF">
<java:address className="nl.amis.configuration.ConfigurationManager"/>
</port>
</service>
</definitions>
The service element offers a single port that associates the DynamicConfigurationManagerWSIF binding with the Java Class nl.amis.configuration.ConfigurationManager that we introduced in the previous section. The binding element in turn associates the technology agnostic portType DynamicConfigurationManager with the Java based implementation of the operations and the parameters. The java:binding element is a WSIF extension to WSDL, that specifies for this binding to be based on the WSIF-Java binding – WSIF supports other binding types as well, for example EJB, JCA and HTTP. In the binding element, we have used format:typeMap inside a format:typeMapping element to connect Java data types with corresponding XSD (WSDL speak) types. Here we only use the xsd:string that we map to java.lang.String. The binding element also associates operations with Java methods.
The portType is not WSIF or Java specific. It defines the getSetting and setSetting operations, specifying the input and output parameters (or messages) as well. The message elements finally each describe the structure of the input and output parameters. These structures are described in the normal WSDL, XSD based manner, in this case using xsd:string.
Now that we have this technology independent service description, we can start using it in our BPEL processes.
Note: we can use the Java Web Service wizard in JDeveloper to create most of the WSIF-WSDL for us. From the New Gallery, pick Java Web Service:
Select the class to publish as a Web Service. And check the WSIF Binding checkbox.
Select the public methods on the class that we want to publish as operations in the Web Service:
And go to the Finish page – accepting all default values on all other pages.
The generated WSDL file looks like this when presented in visual diagrammer:
However, the generated WSDL file is a little too complex. It works with complex type definitions for the input and output messages that require custom XML-Java mappings – overhead we do not really need for the simple service at hand. So make some modifications to the WSDL and turn it into the document presented above and below:
3. Copy the Java classes to the BPEL_HOME\bpel\system\classes directory
When we invoke a service in a BPEL process that is implemented through Java classes, the BPEL PM runtime engine needs to be able to find those classes, and instantiate them through a public, no argument constructor. All classes used from the directly accessed classes must be available as well. The classes have to be copied to the BPEL_HOME\bpel\system\classes directory.
Note: if it is already running, the BPEL engine will only be able to access the classes after it has been restarted.
4. Create a BPEL process that can set the value of Configuration Parameters
Now that we have the service set up – we have the WSDL and the classes are loaded in the BPEL PM engine – we can start creating, deploying and running services that leverage it.
Let’s first create a service to set Configuration Parameter values in the Configuration Manager. Note that these values are retained until overwritten by new values or until the BPEL engine (the JVM) is shut down.
The steps are simple:
a. create a new BPEL Project (RegisterConfigurationValue) in JDeveloper
b. create a PartnerLink for the DynamicConfigurationManager service
simply drag & drop the PartnerLink Service from the Service tab in the Component Palette. Enter the name for the PartnerLink. Then browse the file system for the WSDL we created in step 2. Accept the proposal to copy the WSDL to the project and to create a separate WSDL with the PartnerLink type and a reference to the copied WSDL. Then choose DynamicConfigurationManager_Role for the Partner Role and leave My Role empty.
c. create an invoke activity that calls this PartnerLink (create default input parameter); make sure that you select the setSetting operation.
d. create an assign activity that copies the key and value to the input parameter for the invoke activity
e. remove the reply activity (as this service will not return anything)
The overall BPEL process now looks as follows:
f. deploy the process to the BPEL PM engine. You can run it to verify that it does not fail. However, if a call succeeds, an invisible value is set in the Singleton ConfigurationManagerServer – hardly spectacular!
5. Make use of the ConfigurationManager singleton Java object to get the values of dynamic parameters
As an example of a service that requires dynamic parameters in order to perform its function, let’s look at the DollarEuroConverterService. It is a service that is called with a string that contains a number, interpreted as a dollar amount, and the service returns that amount converted into the Euro currency. Needless to say that this service in order to do its job, requires the current Dollar to Euro currency exchange rate.
There are several WebServices that offer currency exchange rates, and we could have our DollarEuroConverterService, that we implement as a BPEL process, make a call to one of those services every time the exchange rate is required. However, we know calling such external SOAP based WebServices is expensive in terms of performance. We also know that our service will be invoked hundreds of times per hour, expecting blazing fast performance, while we only need to update the exchange rate on a daily basis.
Here we have a good use case for the dynamic configuration manager: we schedule a process – possibly a BPEL proces
s – to call a Currency Exchang
e WebService once every 24 hours, and send the result to the RegisterConfigurationValue service to load on the ConfigurationManagerServer bean. All the thousands of process instances that need the exchange rate will get it with low performance overhead through the WSIF JavaBinding from this bean, rather than call an expensive WebService.
A simple implementation of the DollarEuroConverterService BPEL Process:
The DynamicConfigurationManager PartnerLink was created in the same way as described before for the RegisterConfigurationValue process. The Invoke activity now calls the getSetting operation, rather than setSetting. This call has both input and output parameters.
The two Assign activities respectively:
– copy the key of the Dollar to Euro Conversion rate to the input parameter of the Invoke activity
– copy the outcome of the multiplication of the Dollar to Euro conversion rate (as returned from the DynamicConfigurationManager service) and the service input (the dollar amount) to the output variable:
The service is complete. We can deploy the BPEL process to the runtime BPEL PM engine, and start testing it.
We are for example hugely interested in the Oil price. It has a great impact on world economy and it is definitively moving to uncharted territory, hitting $100 last week (see for example http://biz.yahoo.com/ap/080102/oil_prices.html). However, I am from Europe and we in The Netherlands earn and pay our money in Euro. So a record oil price in dollars is only really big news for us if the euro price is also at record levels.
Well, we have our greate DollarEuroConverter to analyze the price in Euro. However , after firing up the BPEL engine, we first need to set today’s Dollar to Euro exchange rate (http://finance.yahoo.com/currency/convert?amt=1&from=USD&to=EUR&submit=Convert ) in the Dynamic Configuration Manager.
After we post the message
We can see how the key/value pair was sent to the ConfigurationManager.
Let’s find out what we would have to pay in Euros for a $100 barrel of oil:
and the response after posting the message:
Suppose the dollar would slide a little further against the Euro:
By posting this message we invoke the RegisterConfigurationValue service that will send the dollarEuroRate=0.632 key=value pair to the ConfigurationManager. Consider this the daily currency exchange rate update.
What is a $100 barrel of oil now in the Euro currency?
and the result:
Conclusion
Through the use of a singleton Java object that holds a range of dynamically configurable parameter values, we have achieved our objectives: making parameters available to all process instances with minimal overhead for retrieving the values of those parameters while retaining the run-time flexibility to change the values of those parameters. Through the use of WSIF-Java Binding, we are able to communicate from the BPEL process with configuration manager as if it were a normal WebService- using all the design time and run time benefits available in the tool and technology stack.
Resources
Download JDeveloper 10.1.3.3 Application (DynamicBPELConfigurator.zip) with four projects:
- DynamicConfigurationManager – Java Classes implementing the service
- RegisterConfigurationValue – BPEL process that sets values in the ConfigurationManager
- RetrieveConfigSetting – BPEL process to test the retrieval of values from the ConfigurationManager
- DollarEuroConverter – an example of a Service that leverages the DynamicConfigurationManager to absorb new Currency Exchange rates without going down or suffering large performance penalty due to service call overhead
I like this post, enjoyed this one appreciate it for posting .
Interesting though this story is, it would not be complete without mentioning the Oracle BPEL Deployment Descriptor Properties (Preferences and Configuration Properties). See BPEL Developer’s Guide – Appendix C for details on this very useful feature.
Umesh
Thanks for your comment. I suppose that in a multi-JVM environment I will not get away with the singleton without having it regularly check in either a file or database table whether or not a refresh is required – by comparing its own latest-refresh-timestamp with the latest-update-timestamp in either file or database table. Depending on how quickly confguration changes should take effect, the singleton has to poll regularly. Altenatively, we could perhaps have each instance (node) initiate the singleton from its own BPEL process instance – and continue to have that process instance around for pushing new configuration settings to the singleton. I do not know at this point however if and how I can create a BPEL Process instance at a specific node in the cluster or instance in OC4J.
Lucas
Quite a nifty technique. Lookups are always important and going to DB or reading from a file etc will always be costly affair. Just out of curiosity, how will this work when you have multiple JVMs for your OC4J or have a clustered installation of SOA suite?