BPD Integrations

IBM BPM has the ability to call out from a process to invoke the services of an external service provider running outside of BPM. Unless the processes being built (and even then) are solely composed of Human Services, it is extremely likely that interaction with external applications and systems will be needed. There are a number of technologies that can be used to interact with back-end systems. The following describe some of the more commonly used.

Outbound Web Services

One of the more common ways that a service is exposed is through the technology known as Web Services. Web Services is an encoding of an XML document sent over an HTTP transport. Web Services is an open an industry standard composed of a number of key specifications including SOAP and WSDL.

In the IBPM PD, we can create an IBPM service that encapsulates the invocation of a Web Service. When this IBPM Service is called, the result is a call to the Web Service provider that the IBPM service is configured to call.

To achieve this, select the Implementation category in the Library and in the Create New menu, select Integration Service.

Once selected, a new dialog will appear asking for a name for the new integration service component.

Once completed, an empty service diagram is shown. From the palette, a Web Service Integration primitive can be dragged and dropped onto the canvas and wired into the flow. It is this component that will perform the call to the external Web Service. Notice the icon representing an external integration on the component. Learn this icon so that when you see it again you will know that this service component represents an external call.

Simply placing a Web Service component on the canvas isn't the end of the story. The component doesn't yet know which external service to call. The next step on our journey is to select the Web Service calling component on the canvas and examine its Implementation properties.

Now we need to pause for a moment. Prior to v8.5 of IBM BPM, the mechanism for defining how we call a Web Service was performed one way but from v8.5 onwards, it is performed a very different way. Both styles are fully supported. The two styles are called "From process application settings" and "Provide in-line configuration". The selection is made from the "Discovery Scheme" selection at the top of the implementation section.

The primary difference between the two styles is that in one, the definition of the Web Service is made globally and then any integration service can call it by an alias name while in the other style, the target web service is defined there and then. The new style was introduced to allow a single place to define a Web Service that can then be leveraged throughout the solution. If the web service definition needs to be changed, it can be changed in one place rather than having to hunt through all the project artifacts.

Let us first look at the process application settings style.

From the Process App Settings of our Process Application, we can define a new server definition of type Web Service:

In this section, we can define an external Web Service by supplying the URL to its WSDL. This web service then becomes a "named" entity.

In the Integration Service, when we wish to invoke the Web Service, we can now select it from a pull-down list:

Here is the inline configuration style.

The description of an external Web Service is commonly described in a Web Services Definition Language (WSDL) file. In order for IBPM to know how and where to call a Web Service, we need to make the WSDL document that describes that service known. A common convention is that when a Web Service is published a URL can be supplied which will return the WSDL for that Web Service. This is usually the URL of the web service with the suffix ?WSDL added.

For example, if a service is available at

http://localhost:9080/MyProject/MyWebService

then sending a request to

http://localhost:9080/MyProject/MyWebService?WSDL

will return the WSDL description for that service. In the WSDL URI: entry field in the editor, the URL pointer to the WSDL can be entered. One entered, the Discover button can be pressed to dynamically retrieve the WSDL from the remote server and have IBPM parse the content to determine what operations are available and what data types are expected for those operations.

Note: If the service is provided by IBPM Advanced SCA Exports, you can not use this mechanism to properly discover the artifacts. Fortunately, there is a solution. Open a web browser and enter the URL of the service description as the page to load. You will find that the WSDL returned has a new URL in the address bar of the browser.

For example:

Entering:

http://localhost:9080/SampleWebServiceProviderWeb/sca/MyWebService

resulted in a redirect to:

http://localhost:9080/SampleWebServiceProviderWeb/sca/MyWebService/WEB-INF/wsdl/SampleWebServiceProvider_MyWebService.wsdl

Using this URL will work with IBPM|

When the Discover button is pressed, a new dialog is presented. This allows you to enter a username/password combination if the access to the target URL is restricted.

A button called View can be used to open a browser at the defined URL so that the WSDL file can be viewed in its source form.

Once this step has been completed, the development tooling is now aware of all the operations that are available at the target service. From the Operations: pull-down, select the operation you wish to call. Also notice that the endpoint information for the service has now been determined.

The next step is to generate the data types (if any) that are expected for the operations you wish to call. Click on the Generate Types button to create data type definitions for the data types used as inputs and outputs from the service. A dialog is presented to allow you to create some or all of the data types. Typically you would only need to select the operations that you plan to use and only data types associated with those operations are created.

A list of the data types to be created or mapped is shown.

A second dialog is produced before completion.

Once done, all that is left is to map variables to the data to be passed in and returned from the service call.

The names of the formal parameters are shown. If one hovers a mouse over these names, the associated data types can also be seen:

If the operation has multiple input and output parameters, each of the input and output parameters can be individually mapped:

If the service being called throws an exception, this can be caught by adding a Catch handler to the service:

The target of the Web Service request is defined in the Endpoint Address URL box. By default, it has the value defined in the WSDL file. By checking the Override Endpoint Address check box, a new URL value can be entered and that will be the target of the request. The URL string can be retrieved from a variable's value with the <#=variable name #> technique.

A good service for testing Web Service interactions can be found here. This public web service returns weather forecast data for US cities.

It is important to note that 7.5 and prior releases of the product only support SOAP 1.1 as the transport protocol. Putting this another way, SOAP 1.2 is not yet supported. If SOAP 1.2 is required, consider using the IBM BPM Advanced edition.

There are times when the Web Service target is defined over an HTTPS protocol as opposed to an HTTP protocol. This means that SSL certificate exchanges need to also occur. Fortunately, if a problem is encountered, IBM BPM generates this extremely useful guidance message:

[10/23/12 15:04:07:411 CDT] 00000007 SystemOut O CWPKI0428I:

The signer might need to be added to the local trust store. You can use the Retrieve from port option in the administrative console to retrieve the certificate and resolve the problem. If you determine that the request is trusted, complete the following steps:

If the instructions contained within this message are followed, the problems with SSL will be resolved.

See also:

Setting up security for outbound Web Services

It is often the case that a Web Service that is being called needs to know the identity of the caller in order to authorize the request to be performed. Settings in the Web Service integration component can be used to provide such information:

There may be a potential "bug" in this area. Setting security parameters here appeared to be ignored unless security was also set in the Implementation screen for accessing the raw wsdl.

Inbound Web Services

An inbound Web Service is the notion that a IBPM solution can "advertise" or "expose" itself as a Web Service allowing it to be called from an external Web Service caller or client. In a typical Web Service, there can be multiple "operations" defined on a single service. IBPM's mapping to the concept of Web Services is that when a web service definition is made, one or more operations may be defined. For each operation defined on that Web Service, an IBPM Service definition is mapped to it.

This is illustrated in the following diagram. There we see a IBPM Web Service definition with three operations defined upon it. For each operation, there is a reference to a corresponding IBPM Service definition.

To construct an inbound Web Service definition, the selection can be made from the Implementation category and selecting Web Service.

2017 11 30 12 24 13

A dialog will appear allowing us to name the Web Service definition:

2017 11 30 12 32 41

Once completed, the properties of the Web Service definition can be completed.

2017 11 30 12 35 08

In the Operations section, press Add for each operation that is desired to be exposed on the Web Service. For each operation added, an IBPM Service needs to be associated with it. This implies that the service must be created before adding the operation. When deployed, a call to a named operation will result in the invocation of the corresponding IBPM Service.

Any parameters defined as input on the IBPM Service will be exposed as parameters on the Web Service call. Any parameters defined as output on the IBPM Service will be returned as parameters on the Web Service call.

Take special note of the WSDL URI in the behavior section. This is the Web URL that can be used to retrieve the WSDL file that describes the exposed Web Service.

Testing an Inbound Web Service with soapUI

One of the most popular Web Service testing tools on the Internet is called soapUI. It can be found at the following web address:

http://www.soapui.org/

This tool/package can be used to exercise calls to IBPM that are exposed as Web Services. The following is the recipe used to achieve this task.

Launch the soapUI tool and select File > New soapUI Project

Give the new project a name and click OK to complete

Right click the new project and select Add WSDL

In the WSDL location text box, enter the URL for the WSDL describing the Web Service. This can be obtained from the IBPM PD Web Service definition in the "Behavior" section.

Clicking on this URL opens a Web Page to the WSDL content. The URL can then be copied from the address bar of the newly opened browser.

Once entered, soapUI will retrieve the WSDL description and parse it. From the WSDL, a sample request message will be built. This request message is a SOAP message that, if sent to IBPM, will cause the service to execute. Drilling down into the request will show the details of the SOAP message which can be modified.

Pressing the submit button (green arrow) will send the SOAP message to IBPM for processing. Multiple soap Request messages can be created representing different tests. The project can be saved for later replay of these requests.

If the service associated with the Web Service definition is changed, the soapUI WSDL will have to be reloaded before testing continues otherwise an out-of-date WSDL will be used with unpredictable results.

The Update Definition menu option will re-load the WSDL:

Invoking a BPD exposed as a Web Service

It is not uncommon to want to expose a BPD to be invoked as a Web Service. To achieve this, a few steps are involved. Working backwards from the BPD, it must first be defined as being started by Start Message Event node. Here is an example BPD:

Associated with a Message Start Event node is a UCA. A UCA acts as the "trigger" to the Start Message Event. Before we can define a UCA, we need a General Service that is to be associated with the UCA. The result (output) of the General Service is the "value" passed by the UCA to its associated partner which in this case will be the Start Message Event Node in the BPD.

A suitable General Service may just pass the input to the output. For example:

Finally, a Web Service definition is also associated with a second General Service. It is this General Service that will be called when the Web Service is invoked. This General Service should cause the invocation of the UCA. A suitable General Service may be:

So, putting it all together we have:

This looks pretty scary so it won't do us any harm to speak about it some more. From the bottom left, we have a Web Service definition which refers to a General Service. This General Service includes an invocation of a UCA. The UCA has to have an associated General Service (because that is the rules) that knows how to build the data to be passed onwards by the UCA. The UCA is used as the trigger to the Start Message Event in the BPD.

Invoking an IBPM Web Service from a Java POJO

The following is a recipe for building a Java application that calls an exposed IBPM Web Service. Although not strictly related just to IBPM, it is useful to see the steps here to see how simple it is and for future reference. The sample here utilizes IBM's RAD/WID for the Java and Web Service client development.

Create a new Java Project to host the Java code that will call the Web Service.

Using the File > New, start a wizard for creating a new Web Service Client:

At the conclusion of this step, a series of Java classes will have been created conforming to the JAX-WS specification. In these classes will be one named for the service.

Create an instance of that class and then use the getter to retrieve the SOAP object. That object has methods on it corresponding to the operations on the service.

A test caller can then be written:

import win7\_x64.\_19086.teamworks.webservices.tests.myinboundwebservice.MyInboundWebService;
 public class Main {
 public static void main(String[] args) {
 MyInboundWebService miws = new MyInboundWebService();
 miws.getMyInboundWebServiceSoap().op1("Hello");
 }
 }

Web Services and data types

When we talk about passing data to and from a Web Service, we are implicitly talking about converting data types from one format to another. In IBPM, we have simple data types and complex data types. In Web Services, we have similar concepts but the data types are described by XML Schema Definitions (XSDs). The question now raised is just how compatible are these two environments? Are there data types in IBPM that don't map to XSDs and/or visa versa. In the following section we work through the different permutations of data of interest to us and execute tests against Web Services that expose those types and see how IBPM handles them.

In these tests, we have a variety of back-end Web Service that we wish to call from IBPM. Each Web Service differs from the other by the data types it returns.

Simple Data Type

Interface: ISimple

Endpoint: http://localhost:9080/WLEDataTypeTestsWeb/sca/ISimpleExport

Generated data types:

Results

Conclusion

No issues at all with a simple data type. Surprised to see "String" being offered as a data type mapping as it seems to say that will create a new data type … but it appears that the table shows mappings as well as new types to be created.

Complex Data Type

Interface: IComplex

Endpoint: http://localhost:9080/WLEDataTypeTestsWeb/sca/IComplexExport

Generated data types:

Results:

Conclusion:

No issues. Complex data type created as expected.

Nested Complex Data Type

Interface: INestedComplex

Endpoint: http://localhost:9080/WLEDataTypeTestsWeb/sca/INestedComplexExport

Generated data types:

Results:

Conclusion:

No issues … again all as expected.

Array of Complex Data Type

Interface: IArray

Endpoint: http://localhost:9080/WLEDataTypeTestsWeb/sca/IArrayExport

Generated data types:

Results:

An error was received.

Runtime error in script ("Process: 'WLE IArray' ProcessItem: 'Call IArray' Type: 'ITEM'" 1:0).Internal Script error: com.lombardisoftware.core.TeamWorksRuntimeException: Property field1 in class ComplexArrayHolderType is not declared. It must be declared to be used.
 Script (line 1):
 1 : tw.local.outVar = \_\_teamworks\_temp\_wsconnector\_variable\_;

The full stack trace was:

com.lombardisoftware.core.TeamWorksException: Runtime error in script ("Process: 'WLE IArray' ProcessItem: 'Call IArray' Type: 'ITEM'" 1:0).Internal Script error: com.lombardisoftware.core.TeamWorksRuntimeException: Property field1 in class ComplexArrayHolderType is not declared. It must be declared to be used.
 Script (line 1):
 1 : tw.local.outVar = \_\_teamworks\_temp\_wsconnector\_variable\_;
 at
 com.lombardisoftware.core.TeamWorksException.asTeamWorksException(TeamWorksException.java:129)
 at com.lombardisoftware.core.RegexExceptionRewriter.rewrite(RegexExceptionRewriter.java:73)
 at com.lombardisoftware.core.ExceptionHandler.returnProcessedException(ExceptionHandler.java:305)
 at com.lombardisoftware.servlet.ControllerServlet.doError(ControllerServlet.java:146)
 at com.lombardisoftware.servlet.ControllerServlet.doCommon(ControllerServlet.java:409)
 at com.lombardisoftware.servlet.ControllerServlet.doPost(ControllerServlet.java:130)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:738)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
 at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1657)
 at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1597)
 at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:131)
 at com.lombardisoftware.servlet.ClearThreadCachesFilter.doFilter(ClearThreadCachesFilter.java:24)
 at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
 at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
 at com.lombardisoftware.servlet.CrossSiteScriptingFilter.doFilter(CrossSiteScriptingFilter.java:29)
 at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
 at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
 at com.lombardisoftware.servlet.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:35)
 at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
 at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
 at com.ibm.ws.webcontainer.filter.WebAppFilterChain.\_doFilter(WebAppFilterChain.java:77)
 at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:908)
 at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:934)
 at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:502)
 at com.ibm.ws.webcontainer.servlet.ServletWrapperImpl.handleRequest(ServletWrapperImpl.java:179)
 at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:91)
 at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:864)
 at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1583)
 at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:186)
 at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:445)
 at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:504)
 at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:301)
 at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:83)
 at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
 at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
 at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
 at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138)
 at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:204)
 at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:775)
 at com.ibm.io.async.ResultHandler\$2.run(ResultHandler.java:905)
 at com.ibm.ws.util.ThreadPool\$Worker.run(ThreadPool.java:1563)
 Caused by: [TeamworksException name='Process: 'WLE IArray' ProcessItem: 'Call IArray' Type: 'ITEM'', message='Internal Script error: com.lombardisoftware.core.TeamWorksRuntimeException: Property field1 in class ComplexArrayHolderType is not declared. It must be declared to be used.', line=1, pos=0 nested=]
 at com.lombardisoftware.core.script.js.JavaScriptRunner.execute(JavaScriptRunner.java:283)
 at com.lombardisoftware.core.script.js.JavaScriptRunner.evalExpression(JavaScriptRunner.java:338)
 at com.lombardisoftware.component.common.workflow.ExecutionContext\$3.call(ExecutionContext.java:563)
 at com.lombardisoftware.component.common.workflow.ExecutionContext.doWithParams(ExecutionContext.java:620)
 at com.lombardisoftware.component.common.workflow.ExecutionContext.evaluateJSScript(ExecutionContext.java:561)
 at com.lombardisoftware.component.common.workflow.ExecutionContext.evaluateJSScript(ExecutionContext.java:584)
 at com.lombardisoftware.component.common.workflow.ExecutionContext.executeJSScript(ExecutionContext.java:548)
 at com.lombardisoftware.component.wsconnector.worker.WSConnectorWorker.bindOutput(WSConnectorWorker.java:164)
 at com.lombardisoftware.component.wsconnector.worker.WSConnectorWorker.doJob(WSConnectorWorker.java:125)
 at com.lombardisoftware.component.common.workflow.ExecutionJob.doJob(ExecutionJob.java:399)
 at com.lombardisoftware.server.ejb.workflow.EJBWorkflowManagerBean.doResumeWorkflowEngine(EJBWorkflowManagerBean.java:942)
 at com.lombardisoftware.server.ejb.workflow.EJBWorkflowManagerBean.resumeProcess(EJBWorkflowManagerBean.java:337)
 at com.lombardisoftware.server.ejb.workflow.EJSRemoteStatefulEJBWorkflowManager\_82478d70.resumeProcess(Unknown Source)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:600)
 at com.ibm.rmi.util.ProxyUtil\$2.run(ProxyUtil.java:528)
 at java.security.AccessController.doPrivileged(AccessController.java:251)
 at com.ibm.rmi.util.ProxyUtil.invokeWithPrivilege(ProxyUtil.java:514)
 at com.ibm.CORBA.iiop.ClientDelegate.invoke(ClientDelegate.java:1156)
 at \$Proxy93.resumeProcess(Unknown Source)
 at com.lombardisoftware.server.ejb.workflow.\_EJBWorkflowManagerInterface\_Stub.resumeProcess(\_EJBWorkflowManagerInterface\_Stub.java:518)
 at com.lombardisoftware.component.common.workflow.EJBWorkflowManagerDelegateDefault.resumeProcess(EJBWorkflowManagerDelegateDefault.java:142)
 at com.lombardisoftware.component.common.workflow.EJBWorkflowManagerDelegateWebSphere\$6.run(EJBWorkflowManagerDelegateWebSphere.java:84)
 at com.lombardisoftware.client.delegate.common.WebsphereDelegateHelper.doAsCurrentSubjectContextSensitive(WebsphereDelegateHelper.java:128)
 at com.lombardisoftware.client.delegate.common.WebsphereDelegateHelper.doAsCurrentSubjectContextSensitive(WebsphereDelegateHelper.java:116)
 at com.lombardisoftware.component.common.workflow.EJBWorkflowManagerDelegateWebSphere.resumeProcess(EJBWorkflowManagerDelegateWebSphere.java:82)
 at com.lombardisoftware.component.common.web.WebWorkflowManager.callEJBWorkflowManager(WebWorkflowManager.java:686)
 at com.lombardisoftware.component.common.web.WebWorkflowManager.processRequest(WebWorkflowManager.java:238)
 at com.lombardisoftware.servlet.ControllerServlet.doCommon(ControllerServlet.java:316)
 ... 36 more
 Caused by: com.lombardisoftware.core.TeamWorksRuntimeException: Property field1 in class ComplexArrayHolderType is not declared. It must be declared to be used.
 at com.lombardisoftware.core.TWObject.getDeclaredPropertyByName(TWObject.java:548)
 at com.lombardisoftware.core.TWObject.setTWClass(TWObject.java:344)
 at com.lombardisoftware.core.TWObject.cast(TWObject.java:317)
 at com.lombardisoftware.core.TWObject.setArrayItemTWClass(TWObject.java:434)
 at com.lombardisoftware.core.TWObject.cast(TWObject.java:315)
 at com.lombardisoftware.server.core.SymbolTable.set(SymbolTable.java:366)
 at com.lombardisoftware.core.script.js.AbstractTWSymbolTableScriptable.put(AbstractTWSymbolTableScriptable.java:170)
 at org.mozilla.javascript.ScriptableObject.putProperty(ScriptableObject.java:1729)
 at org.mozilla.javascript.ScriptRuntime.setObjectProp(ScriptRuntime.java:1557)
 at org.mozilla.javascript.ScriptRuntime.setObjectProp(ScriptRuntime.java:1547)
 at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:3036)
 at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2487)
 at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:164)
 at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:398)
 at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3070)
 at org.mozilla.javascript.InterpretedFunction.exec(InterpretedFunction.java:175)
 at com.lombardisoftware.core.script.js.JSScript.exec(JSScript.java:56)
 at com.lombardisoftware.core.script.js.JavaScriptRunner\$2.execute(JavaScriptRunner.java:242)
 at com.lombardisoftware.core.script.js.PreparedScope.executeWithScope(PreparedScope.java:199)
 at com.lombardisoftware.core.script.js.JavaScriptRunner.execute(JavaScriptRunner.java:240)
 ... 66 more

Investigation of this issue turned up an interesting situation. Let us break down the scenario into more detail.

The Web Service we are calling returns a complex data type that I called "ComplexArrayHolderType". This data type contains a single field called "arrayHolder" which is defined as a 0..* (an array/list) of data types of type ComplexDataType2. In WID as a Business Object, this looks as follows:

From a raw XSD perspective, it looks like:

\<?xml version="1.0" encoding="UTF-8"?\>
 \<xsd:schema targetNamespace="http://WLEDataTypeTests"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:bons0="http://WLEDataTypeTests"\>
 \<xsd:include schemaLocation="ComplexDataType2.xsd"\>\</xsd:include\>
 \<xsd:complexType name="ComplexArrayHolderType"\>
 \<xsd:sequence\>
 \<xsd:element minOccurs="0" name="arrayHolder"
 type="bons0:ComplexDataType2" maxOccurs="unbounded"\>
 \</xsd:element\>
 \</xsd:sequence\>
 \</xsd:complexType\>
 \</xsd:schema\>

The XSD is consistent with the model Business Object representation model.

When a request is made to the Web Service, we see the following SOAP message being returned:

\<?xml version="1.0" encoding="UTF-8" standalone="no"?\>
 \<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"\>
 \<soapenv:Body xmlns:axis2ns8="http://WLEDataTypeTests/IArray"\>
 \<io:opIArrayResponse xmlns:io="http://WLEDataTypeTests/IArray"
 xmlns:io2="http://www.w3.org/2003/05/soap-envelope"
 xmlns:io3="http://www.ibm.com/websphere/sibx/smo/v6.0.1"
 xmlns:io4="http://www.ibm.com/xmlns/prod/websphere/mq/sca/6.0.0"
 xmlns:io5="http://schemas.xmlsoap.org/ws/2004/08/addressing"
 xmlns:io6="http://www.ibm.com/xmlns/prod/websphere/http/sca/6.1.0"
 xmlns:io7="wsdl.http://WLEDataTypeTests/IArray"
 xmlns:io8="http://www.w3.org/2005/08/addressing"
 xmlns:io9="http://WLEDataTypeTests" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\>
 \<iArrayOut\>
 \<arrayHolder\>
 \<field1\>Index 1 Value 1\</field1\>
 \<field2\>Index 1 Value 2\</field2\>
 \<field3\>Index 1 Value 3\</field3\>
 \</arrayHolder\>
 \<arrayHolder\>
 \<field1\>Index 2 Value 1\</field1\>
 \<field2\>Index 2 Value 2\</field2\>
 \<field3\>Index 2 Value 3\</field3\>
 \</arrayHolder\>
 \<arrayHolder\>
 \<field1\>Index 3 Value 1\</field1\>
 \<field2\>Index 3 Value 2\</field2\>
 \<field3\>Index 3 Value 3\</field3\>
 \</arrayHolder\>
 \</iArrayOut\>
 \</io:opIArrayResponse\>
 \</soapenv:Body\>
 \</soapenv:Envelope\>
 

Again, this appears consistent. The tag called <iArrayOut> is the name of the output variable which does indeed appear to be an instance of "ComplexArrayHolderType" as evidenced by the repeating occurrences of the tag called "arrayHolder" which is the only field in "ComplexArrayHolderType".

Looking for a IBPM perspective, we find that the generation of types from IBPM created two IBPM types. The first is called ComplexArrayHolderType and matches the XSD. This data type, as expected, has a single field called "arrayHolder" that is defined as a list of ComplexDataType2.

ComplexDataType2 looks as follows:

In the IBPM Integration Service, the output parameter of the following integration:

In the data mappings, the following is shown:

The "outVar" was defined to be of data type "ComplexArrayHolderType". Hovering the mouse over "iArrayOut" shows the expected data type:

Which again does seem to show that it is expecting a ComplexArrayHolderType. So, to recap, in this environment it fails with the previous errors.

Now … here is where things get interesting. If I click the "autoMap" button for this variable, a variable called "iArrayOut" is created:

The data type of this variable is:

Which in description form is "a list of ComplexDataType2".

When this variable and data type are used, the solution executes.

Conclusion:

There is a mystery at play here.

Java Message Service – JMS

Java Message Service (JMS) is a messaging and queuing system available in the Java world. It allows applications to asynchronously put messages to a queue and for other applications to subsequently retrieve them.

JMS – Sending and receiving from queues

IBPM provides a Java Class as part of the integration.jar managed file called teamworks.JMSMessage. This class has methods to put and get messages from a JMS queue. To use this capability, create an Integration Service and insert a Java Integration step. For the implementation of that step, select teamworks.JMSMessage from the integration.jar. The methods available are:

The putMessage parameters look like:

The getMessage parameters look like:

Now we can start to expand on these parameters.

|| |Parameter|Description| |initialContext|This is the name of a Java class that provides the JNDI lookup context. This will be the string: "com.ibm.websphere.naming.WsnInitialContextFactory"| |providerUrl|This is the URL to the JMS provider. For IBM's SIBus, this will be: "iiop://<hostname>:<port>". The port number will be the bootstrap port of the WAS server. This can be found in the WAS admin console under the server definition. The default port value for a normal configuration is 2809.

| |messageContent|This is a text string that is sent as the body of the message. Note that only JMS TextMessages can be sent using the putMessage() method. Specifically, the other types of JMS messages such as Stream, Bytes, Map and Object are not available.| |connectionFactory|This is the JNDI name of the JMS queue connection factory.| |queueName|This is the JNDI name of the JMS queue to be accessed| |Q Lookup|This is a boolean value (true/false). If set to true, the queue named in the queueName parameter is looked up in JNDI. If set to false, an attempt is made to create a queue with the given name.| |properties|This is a IBPM Map data type that contains a set of name/value properties. These properties will be added to the transmitted TextMessage.| |filter||

See also:

JMS – Triggering a UCA

IBM BPM can listen on JMS queues that are owned by WAS. When a message arrives on this queue, IBM BPM will trigger a UCA as a result. This provides an elegant way for a IBPM based solution to be triggered by the arrival of an asynchronous message.

The primary queue that is being watched by IBM BPM for events is retrievable at the JNDI location called:

jms/eventqueue

The format of the message that arrives on the queue is prescribed by IBPM architecture and mustlook as follow:

\<eventmsg\>
 \<!-- The process app acronym and event name are required.
 The snapshot and UCA name are optional --\>
 \<event
 processApp="***acronym***"
 snapshot="[snapshot\_name]"
 ucaname="[UCA\_name]"\>***event\_name***\</event\>
 \<!--Optional: The name of the queue for the event to run in--\>
 \<queue\>[queue name]\</queue\>
 \<!--Any parameters the UCA may require-
 \<parameters\>
 \<parameter\>
 \<key\>param1\</key\>
 \<value\>![CDATA[value1]]\</value\>
 \</parameter\>
 \</parameters\>
 \</eventmsg\>

The processApp attribute on the event element provides the name of the acronym or "short name" of the Process Application. This is required as all artifacts are scoped by the Process App or Toolkit in which they live. Since the message we are going to put in a queue is destined for a UCA which is associated with a Process Application, we need to name the Process Application in which it lives.

When we define a UCA, we give that UCA an attribute called the "Event Message".

This is a poor choice of name for this attribute. What this attribute does is correlate an external event such as a message arriving on a JMS queue with the type of UCA to invoke. A Process Application may have multiple UCAs associated with it yet IBPM listens on just one queue. This means that when a message is deposited in the queue, there has to be data in the message to allow IBPM to know which UCA to deliver the message to. The Event Message attribute is the key. By default, IBPM assigns this a uniquely generated value but it is common to change this to a human readable value that describes its nature with more meaning.

The "event_name" element in the JMS XML message contains the name of the Event Message that is used for matching.

Since an incoming event may have data associated with it and this data matches data with the UCA, the Parameters section may have multiple parameter values that are mapped to the message.

See also:

JMS Client Tools

|| |Note: 2012-01-26 - Bizarrely, both of these tools are no longer available from the IBM AlphaWorks web site. Checking with the IBM DeveloperWorks staff, it was found that the reason they are no longer present is that the original IBM authors chose not to continue to support/distribute these tools. Although the tools may have been “dated”, they worked beautifully. It is lamentable that they are no longer present. This section will be left in case they should be resurrected.|

IBM has a sample JMS client application called the "IBM Client Application Tool for JMS" (what else would it be called). This can be found here:

http://www.alphaworks.ibm.com/tech/jmstool

Another useful tool is called Service Integration Bus Explorer. It can be found here:

http://www.alphaworks.ibm.com/tech/sibexplorer

For sending messages, I prefer the JMSTool. Here is a quick-start to get it working with IBPM.

First download the tool in a file called JMS_Client.zip as well as the SWT libraries as documented at the JMSTool download page. Ensure that the SWT libraries that you download match the platform on which the tool is to be run. For example, download the 64bit libraries if you are going to be running on a 64bit OS. Also make sure that there are no spaces in the paths for either JMS Client, SWT or the WAS App Server.

Extract both the JMSTool.zip and the SWT libraries ZIP.

In the JMS_Client folder created, find the file called setenv.bat. Edit this file with a text editor.

Change the variables for WAS_HOME and SWTJARS and JNDI_PROV_URL

An example resulting file looks like:

rem ############################################################################ rem # rem # IBM Client Application for JMS environment script rem # Copyright (C) IBM Corp. 2006 rem # rem # This script is run automatically when starting the IBM Client Application rem # for JMS using the 'JMSClient.bat'script. The environment variables in this rem # script should be customized to your system before running the rem # 'JMSClient.bat'script. Rem # rem ############################################################################ rem ############################################################################ rem # This value should be the location of your WebSphere installation rem # (E.g. C:\Program Files\IBM\WebSphere\AppServer) rem ############################################################################ set WAS_HOME=C:\IBM\Lombardi7\AppServer set WMQ_JAR_PATH=%WAS_HOME%\lib\WMQ\java\lib rem ############################################################################ rem # If you are using the SWT GUI, you need to make sure that the SWT libraries rem # are available on the classpath. The value of the following environment rem # should be set to the location of the SWT libraries (I.e. the location of rem # your 'swt.jar' file and SWT DLL's) rem ############################################################################ set SWTJARS=C:\Projects\WLE\JMS\swt-3.6.1-win32-win32-x86_64 rem ############################################################################ rem # The JMS connection factory for the default messaging JMS provider has a rem # dependency on JNDI, performing a lookup for certain WebSphere resources rem # when attempting to connect to a messaging engine within a Service rem # Integration Bus. For this reason, a value must be specified for the rem # JNDI_PROV_URL environment variable that points at a valid Naming Service rem # within the target WebSphere Application Server cell if you are attempting rem # to connect to a Service Integration Bus. Rem ############################################################################ set JNDI_PROV_URL=corbaloc:iiop:localhost:12812

To launch the tool, run:

JMSClient.bat -ui SWT

Click the Browse JNDI button to bring up a list of connection factories:

Pick the entry called javax.jms.QueueConnectionFactory

Click Connect to to connect to the WAS Server. The main page of the tool is shown:

Click on the Message Producer tab to start the task of sending (producing) a message:

Click the Browse JNDI button beside the Destination input. This will produce a list of known queues as found by listing the JNDI registry. Pick eventqueue from the list:

In the JMS payload section, enter the data that you wish to place on the queue. The content should be XML as previously described. Finally press the Send Once button to send a single instance of the message to the queue.

Once the message has been sent, IBPM will retrieve the message and process it.

In addition to the above tools, another tool "SI Bus Performance Tool" can be found here:

https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=13a714e5-6379-4325-aeee-c0c88ec3d15b

This tool is especially useful for seeing if there are readers of SI Bus queues.

See also:

Hermes JMS

Hermes JMS is another good tool for working with JMS messages. It doesn't appear to have been modified since 2009 but it still seems to work. Although not advertised as working with WebSphere SI Bus, it does seem to work reasonably well.

The home page for Hermes is:

http://www.hermesjms.com/confluence/display/HJMS/Home

As of 2014-04, the latest version is 1.14

Download the hermes-install-xxx.jar

Run java -jar hermes-install-xxx.jar

Once run, we see an initial welcome screen.

Next is a description of what HermesJMS is all about.

Now the license instructions.

Next the folder into which the tool will be deployed.

Now a prompt about what is going to be installed.

Now a final confirmation.

And a progress step.

In my experience running on Windows 7, this last window did not terminate and I was forced to quit. However, the tool was installed and seemed to run correctly. It appeared that the problem was caused by the tool trying to create a desktop icon which failed.

It is absolutely vital that Hermes run using the IBM JVM and not any other JVM. The easiest way to do this is to edit the "hermes.bat" and insert:

set JAVA_HOME=C:\IBM\WebSphere\AppServer\java

near the top of the file. Make sure that the path matches what you expect.

We also need to add the JAR files from WAS to allow connection. Open Options > Configuration and switch to the Providers tab. Right click in the background and select "Add Group". Set the "Classpath group name" to be "WAS85". Right click "Library" and select "Add JAR(s)". Add the following JAR files from the "<WAS Root>/runtimes/" folder:


In the target WAS server, we need to change RMI/IIOP security settings. Open the WAS Admin console and go to Security > Global Security

Open the "CSIv2 inbound communications":

Change the "Transport" property to "SSL – supported"

Repeat the above for the "CSIv2 outbound communications" setting.

If you fail to do the above, an error similar to the following will be received

'javax.naming.NamingException: Error getting WsnNameService properties [Root exception is org.omg.CORBA.TRANSIENT: initial and forwarded IOR inaccessible vmcid: IBM minor code: E07 completed: No]

http://www-01.ibm.com/support/docview.wss?uid=swg21614221

Next we need to create a new context. The parameters that need set are:


Also had to fiddle with IIOB settings when I received:

'javax.naming.NamingException: Error getting WsnNameService properties [Root exception is org.omg.CORBA.TRANSIENT: initial and forwarded IOR inaccessible vmcid: IBM minor code: E07 completed: No]

http://www-01.ibm.com/support/docview.wss?uid=swg21614221

Double clicking on the new context will show a list of JNDI entries. In the jms folder, we will find connection factories, queues and topics.

When the WAS server hosting SI Bus is configured, a port is defined for SI Bus client connections. The WAS logical name for this is "SIB_ENDPOINT_ADDRESS". When we configure a WAS queue connection factory, we must also define the endpoint providers property to allow the client to connect to the correct provider. For example:

In the WAS Bus > Security settings, change the permitted transports to work over all types:

Experience seems to be showing that after adding a QCF or CF we need to restart the server for it to show up in JNDI.

In a QCF definition, we also need to provide a "prover endpoint"

localhost:7281:BootstrapBasicMessaging

WebSphere Default Messaging

The JMS implementation supplied by WAS is called Default Messaging or sometimes System Integration Bus (SI Bus).

See also:


Database Integration

A commonly needed ability is the integration of data held in databases with a process. A database may hold prices, stock availability or other important information that may be desired to be used by a process. IBPM provides "connectors" to allow access to the database content held in tables.

Integration with supplied DB connectors

The functions to access databases are found in the System Data toolkit as implementation services. These services are themselves implemented in Java presumably using the Java JDBC bindings.

The overall story looks as follows:

A BPD that wishes to work with data from a Database will invoke a user written Service through an activity. This is business as usual. The called Service will wish to access the database. In order to do this it will leverage one of the IBM supplied pre-implemented Services found in the System Data Toolkit. When that is invoked, that supplied service calls native Java code supplied in a JAR packaged with the System Data Toolkit. That Java code utilizes JDBC to interact with the target database.

The list of available functions includes:

|| |Name of Service|Description| |SQL Blob to File|Read a Blob from the database and write the data to a file.| |SQL Blob to File (SQL Statement)|| |SQL Call Multiple Stored Procedures|Execute multiple store procedures.| |SQL Call Multiple Stored Procedures (SQL Statement)|| |SQL Call Stored Procedure|Call a single stored procedure| |SQL Call Stored Procedure (SQL Statement)|| |SQL CLOB to File|Read a CLOB from the database and write the data to a file.| |SQL CLOB to File (SQL Statement)|| |SQL Execute Multiple Statements|Execute multiple SQL statements.| |SQL Execute Multiple Statements (SQL Result)|| |SQL Execute Script (SQL Result)|| |SQL Execute Statement|| |SQL Execute Statement (SQL Result)|| |SQL File to BLOB|Read binary data from a file and write it to the database.| |SQL File to BLOB (SQL Statement)|| |SQL File to CLOB|Read character data from a file and write it to the database.| |SQL File to CLOB (SQL Statement)|| |SQL Get Database Type|Determine the type of database.|

The parameters of these service calls supplied in the System Data Toolkit are typically the following:

|| |Parameter Name|Description| |sql|This is a String representation of the SQL statement/query to be executed against the database.| |parameters|These are a list of parameters to be replaced in the SQL expression. Each element of the list should be of type SQLParameter. These parameters are replaced in the SQL statement where '?' is found.| |maxRows|This is the maximum number of rows to be returned.| |returnType|This is a String representation of the return type. Typically this will be "XMLElement" but a complex data type can also be used.| |dataSourceName|This is the WAS defined JDBC name as a String.|

If the returnType is defined as XMLElement then a typical return from a query will be an XMLElement object with the following structure:

<resultSet recordCount="2" columnCount="3"> <record> <column name="C1">A</column> <column name="C2">B</column> <column name="C3">C</column> </record> <record> <column name="C1">D</column> <column name="C2">E</column> <column name="C3">F</column> </record> </resultSet>

Using JavaScript, we can then access the fields of the retrieved data as follows:

tw.local.result.record[0].column[0].getText() == "A"

If we wish, we can convert this XMLElement into a JavaScript object as follows:

var result = new Array(); var childRecord = tw.local.results.firstChild; while(childRecord != null){ var column = childRecord.firstChild; var row = new Object(); while(column != null) { var name = column.getAttribute("name"); var myText = column.getText(); row[name] = myText; column = column.nextSibling; } result.push(row); childRecord = childRecord.nextSibling; }

As an alternative to asking for the result to be an XMLElement, a Business Object data type name can be supplied. When the SQL is executed, a List object is returned where each element in the list is an instance of the Business Object data type. The columns in the returned data are matched to the names of the fields in the complex data type. Where there is a match, the returned column value is used as the value of the corresponding field.

A third solution, and currently my own favorite is to define the response type for the results to be "Record" and supply a "List of Record" as the receiver variable. This has all the advantages of defining a custom BO but does not need the overhead of a BO definition simply to exist to hold the SQL data returned.

See also:

The SQLParameter structure used in some of these calls has the following attributes:

|| |Name|Description| |value|The value associated with the SQL parameter.| |type|The type of the parameter. This is a DB data type such as VARCHAR, DECIMAL, DATE.| |mode|The mode of the parameter such as 'IN or 'OUT''.|

When working with Database integration, a recommended practice for building this is the following pattern:

See also:

Using LiveConnect and JDBC

JavaScript executing within a service runs on the Process Server and can use embedded Java in the form of LiveConnect. This means that JDBC programming can be explicitly included in the story:

try { var context = new Packages.javax.naming.InitialContext(); var datasource = context.lookup("jdbc/TEST"); } catch(myExp) { log.error("Exp: " + myExp.message); }

Example – Selecting rows from a table

In this example we will select and retrieve rows from a table. We will assume a table called EMPLOYEE that looks as follows:

We create a nested service activity and bind it to SQL Execute Statement. The data mapping for this activity looks like:

The SQL statement is:

"SELECT LASTNAME,FIRSTNAME FROM KOLBAN.EMPLOYEE WHERE EMPLOYEEID=?"

Note that there is a parameter for the EMPLOYEEID column in the WHERE clause. This is taken from a variable called parm1 which is defined as a list of SQLParameters.

The one and only element of that list variable is coded to be:

value = '123456' type = 'VARCHAR' mode = 'IN'

The result type is an XMLElement and a variable of that type called result is declared to hold the results. After execution, the LASTNAME and FIRSTNAME can be extracted through:

// LASTNAME tw.local.result.record[0].column[0].getText(); // FIRSTNAME tw.local.result.record[0].column[1].getText();

The order of the columns is as defined by the order of the returned items defined in the SELECT statement.

Security with Database Interaction

IBPM interacts with the database using the credentials defined in the security attributes defined in the WAS DataSource definition. It is vital that the tables being accessed have sufficient permissions granted for this userid. If not, an odd error message will be presented:

DSRA9110E: Statement is closed.

JDBC definitions needed to access databases

In order for a process or service to access a database, a JDBC definition must be made in the underlying WAS server that describes how to access the database. This is fully described at:

Service to Insert a row

Following the recommendation of building wrapping services to perform function, here is an example of a service to insert a row into a DB. Imagine that the DB is called TESTDB and has a table called TAB1 which has columns A, B and C. We have a BO called BO1 that has fields A, B and C. The input to this service is an instance of BO1:

The setup variables step populates a String variable called sql which will contain the SQL code to execute and also it will populate the parms variable which is a list of SQL parameters.

tw.local.parms = new tw.object.listOf.SQLParameter(); tw.local.parms[0] = new tw.object.SQLParameter(); tw.local.parms[0].value = tw.local.bo1.A; tw.local.parms[0].type = "VARCHAR"; tw.local.parms[0].mode = "IN"; tw.local.parms[1] = new tw.object.SQLParameter(); tw.local.parms[1].value = tw.local.bo1.B; tw.local.parms[1].type = "VARCHAR"; tw.local.parms[1].mode = "IN"; tw.local.parms[2] = new tw.object.SQLParameter(); tw.local.parms[2].value = tw.local.bo1.C; tw.local.parms[2].type = "VARCHAR"; tw.local.parms[2].mode = "IN"; tw.local.sql = "INSERT INTO KOLBAN.TAB1 (A, B, C) VALUES (?, ?, ?)";

The Execute SQL parameters look as follows:

Service to Delete a row

Again with the notion that we want to create re-usable services to perform work, here is one to delete a row. Imagine that the DB is called TESTDB and has a table called TAB1 which has columns A, B and C. We have a BO called BO1 that has fields A, B and C. The input to this service is an instance of BO1 and we want to delete the row where the row matches field A.

The setup variables looks as follows:

tw.local.parms = new tw.object.listOf.SQLParameter(); tw.local.parms[0] = new tw.object.SQLParameter(); tw.local.parms[0].value = tw.local.bo1.A; tw.local.parms[0].type = "VARCHAR"; tw.local.parms[0].mode = "IN"; tw.local.sql = "DELETE FROM KOLBAN.TAB1 WHERE A=?";

The Execute SQL parameters look as follows:

Service to Update a row

Similar to the previous examples, this example illustrates a service to update a row in a table:

tw.local.parms = new tw.object.listOf.SQLParameter(); tw.local.parms[0] = new tw.object.SQLParameter(); tw.local.parms[0].value = tw.local.bo1.B; tw.local.parms[0].type = "VARCHAR"; tw.local.parms[0].mode = "IN"; tw.local.parms[1] = new tw.object.SQLParameter(); tw.local.parms[1].value = tw.local.bo1.C; tw.local.parms[1].type = "VARCHAR"; tw.local.parms[1].mode = "IN"; tw.local.parms[2] = new tw.object.SQLParameter(); tw.local.parms[2].value = tw.local.bo1.A; tw.local.parms[2].type = "VARCHAR"; tw.local.parms[2].mode = "IN"; tw.local.sql = "UPDATE KOLBAN.TAB1 SET B=?, C=? WHERE A=?";

Calling stored procedures

Let us first think about an initial sample stored procedure. Imagine a table that looks as follows:

OURTABLE

COL_A

COL_B

This is a simple table with two columns. Imagine that we have written a stored procedure that inserts a new row into the table. Duplicates are allowed. When a row is inserted, the number of COL_A rows that have the same COL_A value as the inserted row are returned.

A procedure signature for this might be:

ourProcedure(in colAValue varchar(10), in colBValue varchar(10), out numDuplicates integer)

The code for this might be:

CREATE PROCEDURE OURPROCEDURE ( IN COLAVALUE VARCHAR(10), IN COLBVALUE VARCHAR(10), OUT NUMDUPLICATES INTEGER ) P1: BEGIN INSERT INTO NULLID.OURTABLE VALUES (COLAVALUE, COLBVALUE); SELECT COUNT(*) INTO NUMDUPLICATES FROM NULLID.OURTABLE WHERE COL_A = COLAVALUE; END P1

In IBPM, we can then invoke the System Data toolkit provided Integration Service called "SQL Call Stored Procedure".

The passed in SQL was:

tw.local.sql = "CALL DB2ADMIN.OURPROCEDURE(?, ?, ?)";

and the parameters that were built were:

tw.local.sqlParms = new tw.object.listOf.SQLParameter(); tw.local.sqlParms[0] = new tw.object.SQLParameter(); tw.local.sqlParms[0].type="VARCHAR"; tw.local.sqlParms[0].mode="IN"; tw.local.sqlParms[0].value="ABC"; tw.local.sqlParms[1] = new tw.object.SQLParameter(); tw.local.sqlParms[1].type="VARCHAR"; tw.local.sqlParms[1].mode="IN"; tw.local.sqlParms[1].value="ABC"; tw.local.sqlParms[2] = new tw.object.SQLParameter(); tw.local.sqlParms[2].type="NUMERIC"; tw.local.sqlParms[2].mode="OUT";

For MS SQL Server, it appears that the SQL syntax may have to be:

{ call schema.procname(?) }

(Notice the addition of the curly braces.

See also:

NoSQL Database Integration

Cloudant / CouchDB

The CouchDB stores structures data that is supplied in JSON encoded format. The usual CRUD operations are available including insertion, deletion, update and search/retrieval. Unlike a SQL database where we have tables which contain rows and columns, the tables are replaced by named repositories of "documents" where a document is an instance of a JSON described data structure. The structure of the document does not need to be predefined to CouchDB and a document can be hierarchical. From a BPM perspective, this is music to our ears. What it means is that we can alter the structure of business objects that are to be stored and not worry about having to maintain an equivalent set of DDL/column definitions. In addition, since a couchDB document is richly structured and a BPM business object is also richly structured, the two map together perfectly to produce an ideal storage and retrieval mechanism for BPM business objects.

The interface to CouchDB is through REST APIs. Since the latest versions of BPM have first class support for REST through the Open API specification, we are in an excellent place. What is required is that we map the couchDB REST access to equivalent Open API specification, import that into BPM and we are all ready to use couchDB. We can virtually ignore the implementation details of the database and focus on our BPM activities and efforts.

The default port number for a CouchDB instance is 5984.

All documents require two fields called:

To create a document, we use the REST "POST" command. For example:

POST https://<host>/<db>/<docId>

with a payload of:

{ _id: "<docId>", restOfData ... }

To retrieve a specific document we use the REST "GET" command. For example:

GET https://<host>/<db>/<docId>

To retrieve all documents we use the REST "GET" command. For example:

GET https://<host>/<db>/_all_docs

There are some important query parameter options that we can use with this request:

To update a document we the REST "PUT" command. For example:

PUT https://<host>/<db>/<docId>

with a payload of:

{ _id: "<docId>", restOfData ... }

To delete a document we use the REST "DELETE" command. For example:

DELETE https://<host>/<db>/<docId>

To search for documents we use the REST "POST" command. For example:

POST https://<host>/<db>/_find

Where the body of the request contains a JSON encoded description of the set of documents we are looking for. The encoding is known as a selector and is described in the CouchDB Selector descriptions.

The high level is:

{ "selector": { <expressions> } }

And <expressions> can be built from:

<name>: <value>

This matches documents where the name field has the equal value field.

We can combine these with:

<name1>: <value1> <name2>: <value2>

This matches documents where the name1 field has the equal value1 field and the name2 fields has the equal value2 field.

If we want to test for values that are not equal, we use a value of the format:

{ <operator>: <value> }

where the operator can be one of:


It is also common to want to express combination expressions using constructs such as "and", "or" and others. The combination operators available are:


The general syntax is:

<operator> : [ { <expression> }, { <expression> } … ]

BPM Mapping of CouchDB

Now we have had a look at the low level CouchDB REST requests, let us turn our attention to using these within BPM. First we want to get a copy of an OpenAPI specification of interactions with the database. Once located, we will import that as an external service.

The operations on the CouchDB service are currently defined as:


Because the generic specification isn't compatible with BPM Business Objects, we must drop down to JavaScript in order to send the low level requests.

Here is an example:

var request = new BPMRESTRequest(); request.externalServiceName = "cloudant"; request.httpMethod = "PUT"; request.path="/{db}/{docId}"; request.parameters = { db: "testdb", docId: "DOC1", doc: { "_id": "DOC1", "name": "Neil" } }; var result = tw.system.invokeREST(request); log.info("Result: " + JSON.stringify(result));

In the object that comes back, we will find:


To perform a retrieve of a document, we can use:

var request = new BPMRESTRequest(); request.externalServiceName = "cloudant"; request.httpMethod = "GET"; request.path="/{db}/{docId}"; request.parameters = { db: "testdb", docId: "DOC1", }; var result = tw.system.invokeREST(request); log.info("Result: " + JSON.stringify(result));

To search for matching documents we can use:

var request = new BPMRESTRequest(); request.externalServiceName = "cloudant"; request.httpMethod = "POST"; request.path="/{db}/_find"; request.httpHeaders = { "Content-Type": "application/json" }; request.parameters = { "db": "testdb", "selector": { "selector": { "name": "Neil" } } }; var result = tw.system.invokeREST(request); log.info("Result: " + JSON.stringify(result));

See also:

Mapping IBM BPM Data types to DB data types

Database data types sometimes need to be mapped to/from IBM BPM data types. This section provides some notes on performing those tasks.

Dates/times

Consider a table which has a column of type TIMESTAMP. We may wish to perform a SQL select comparing against such a value. In IBM BPM we have the Date/TWDate entry. What if we wanted to compare against this?

For example:

SELECT * FROM MYTABLE WHERE ORDERDATE == tw.local.myDate

How can we achieve that?

SQL provides some conversion functions


These functions can take string parameters and convert the string to a comparable SQL query.

myDate.format("yyyy-MM-dd")

eg.

"SELECT \* FROM MYTABLE WHERE ORDERDATE == DATE('" + tw.local.myDate.format("yyyy-MM-dd") + "')"

For a SQL TIMESTAMP, we can use:

sql = "…" + tw.local.myDate.format("yyyyMMddHHmmss") + "…";

Java Integration

Java integration is the idea that we can invoke the services of custom Java classes as part of the execution or a process. By dropping down into Java, we have a powerful environment at our disposal. This is achieved within IBM BPM using the Java Service implementation. This component can be found in an Integration Service.

When added into the diagram, a Java Service implementation looks as follows:

Note that this looks visually indistinguishable from a Web Service implementation. The Java Service is added from the palette from the component with the icon that looks like:

Once added, the properties sheet provides the options. The following image shows a properties page already completed.

The Select button allows the user to select the Java Class file that is to be called. This class must be part of a JAR file that has been defined as an "external file" to the Process Application (see: Adding managed files). Once the class has been chosen, methods in the class that may be called can be selected from the Method pull-down.

The Translate JavaBeans check box is used to describe how data being returned from the method call is to be handled. If checked, the data is serialized into an XML document and an XMLElement object is returned. This is useful if the returned Java Object is a JavaBean.

If the Translate JavaBeans box is not checked, the return data type MUST be one of the following types:

  • java.lang.String
  • java.lang.Long
  • java.lang.Integer
  • java.lang.Short
  • java.lang.Byte
  • java.lang.Double
  • java.lang.Boolean
  • java.lang.Character
  • java.util.Calendar
  • java.util.ArrayList
  • java.util.HashMap
  • org.jdom.Document
  • org.jdom.Element
  • com.lombardisoftware.core.XMLNodeList
  • com.lombardisoftware.core.TWObject

In addition to calling Java classes natively, JavaScript "LiveConnect" technologies could be used to call Java from within the context of a JavaScript environment however the use of LiveConnect is discouraged for performance reasons.

If a business object is passed, it appears to receive an instance of a class that implements the interface called teamworks.TWObject.

The TWObject class can be found in the pscInt.jar file found in the <Install>/BPM/Lombardi/libfolder. Experience also shows that one must also add "svrcoreclnt.jar" otherwise an error about a missing "TeamWorksException" class is listed.

The interface for TWObject looks as follows:

public abstract interface TWObject
{
   public abstract String getTWClassName() throws TeamWorksException;
   public abstract Set<String> getPropertyNames();
   public abstract Object getPropertyValue(String paramString);
   public abstract void setPropertyValue(String paramString, Object paramObject);
   public abstract void setPropertyValue(Map<String, Object> paramMap);
   public abstract void removeProperty(String paramString);
}

Think of the TWObject class as a wrapper/holder for a Business Object. The names of all the properties contained in the object can be retrieved with the getPropertyNames() method. The value of one of these properties can then be obtained via the getPropertyValue() method.

If a list object is passed in as a parameter, the object is an instance of teamworks.TWList. The definition of this is:

public abstract interface TWList
{
   public abstract String getTWClassName() throws TeamWorksException;
   public abstract int getArraySize();
   public abstract void addArrayData(Object paramObject);
   public abstract void addArrayData(int paramInt, Object paramObject);
   public abstract Object getArrayData(int paramInt);
   public abstract void removeIndex(int paramInt);
   public abstract boolean isEmptyArray();
   public abstract List getUnmodifiableArray();
}

An interesting item to note is that IBPM has an internal class type called com.lombardisoftware.core.TWObject. This class implements both the TWList and TWObject interfaces. When a Java class is called, it is often this object type that is passed in both when a list and a complex object are used as actual parameters. The implication of this is that if one is dynamically processing input, we can't use the java "instanceof TWList" or "instanceof TWObject" to determine whether we have a list or a complex data type as both of these interfaces are implemented by a common class. Looking closer at the IBPM implementation, there is a getType() method which returns either com.lombardisoftware.core.TYPE_ARRAY or com.lombardisoftware.core.TYPE_PROPERTIES.

Any writing to System.out.println() will appear in the SystemOut.log file. This can be used for debugging and other tasks.

When a Java Class wishes to return a Business Object, it must create an instance of one. The teamworks.TWObjectFactory class can be used. It has a static method called createObject() which returns an instance of a TWObject. This method takes a parameter which is the type name of the Business Object. The type name is the BO's simple name. There is also a corresponding method to create a TWList object that will return a list of Business Objects. Again, the type of the Business Objects contained within the list must be specified.

public TWObjectFactory {
   VersioningContext getThreadVersioningContext();
   void setThreadVersioningContext(VersioningContext);
   TWObject createObject();
   TWObject createObject(String);
   TWObject createPrototype();
   TWList createList();
   TWList createList(String);
   TWList createList(Collection);
   TWObject createIndexedMapObject();
}

An interesting permutation of use of this function is the creation of BPM Record type objects. If we create a TWObject without naming its type, we can add properties to it and map the Java Object to a BPM Record object. For example:

TWObject reportObj = TWObjectFactory.createObject();
reportObj.setPropertyValue("X", 123);
reportObj.setPropertyValue("Y", "Hello");
reportObj.setPropertyValue("Z", Boolean.TRUE);
return reportObj;

When a Java class is exposed as an IBPM callable service, Process Designer knows only a little about it. Calling a Java method will simply show the following about the expected in the tooling:

This doesn't say anything about the nature of the parameters. What we would ideally like the result to be is:

A class called a "BeanInfo" class can be created which provides the meta data that can be used by PD to provide more info. The BeanInfo class must be created in the same package as the primary class and have the same name as the primary class appended with "BeanInfo". For example, if the primary class to be called by IBPM is "com.kolban.MyClass" then the BeanInfo class should be called "com.kolban.MyClassBeanInfo".

Next we will describe the creation of a suitable BeanInfo class.

First we create a new class that extends java.beans.SimpleBeanInfo. Next we override the function called getMethodDescriptors. This method returns an array of MethodDescriptor objects. The array will contain an object for each exposed method.

Imagine that we had a method in our class called "transform" that took as input a HashMap and a String. The following would present these as parameters we choose to call "Map" and "Template":

package com.kolban.bpm.velocity;
import java.beans.MethodDescriptor;
import java.beans.ParameterDescriptor;
import java.beans.SimpleBeanInfo;
import java.lang.reflect.Method;
import java.util.HashMap;

public class WLEVelocityBeanInfo extends SimpleBeanInfo {
   private Class<WLEVelocity> beanClass = WLEVelocity.class;
   @Override
   public MethodDescriptor[] getMethodDescriptors() {
      System.out.println("getMethodDescriptors called");
      try
      {
         Method method = beanClass.getMethod("transform", HashMap.class, String.class);
         if (method == null)
         {
            System.out.println("Unable to find method.");
            return null;
         }
         ParameterDescriptor parm1 = new ParameterDescriptor();
         parm1.setShortDescription("Map");
         parm1.setDisplayName("Map");
         ParameterDescriptor parm2 = new ParameterDescriptor();
         parm2.setShortDescription("Template");
         parm2.setDisplayName("Template");
         MethodDescriptor methodDescriptor = new MethodDescriptor(method, new ParameterDescriptor[]{parm1, parm2});
         return new MethodDescriptor[]{methodDescriptor};
       }
       catch(Exception e)
       {
          e.printStackTrace();
       }
       return super.getMethodDescriptors();
    }
}

Experimentation seems to show that adding or changing a BeanInfo class may require a restart of Process Designer before the changes become apparent.

It is absolutely essential that if a BeanInfo class is provided that it be in lock-step with the Java Bean class that it describes. Specifically, if one deletes a method from the Java class, delete it from the BeanInfo. If one changes the signature of a Java method, reflect those changes in the BeanInfo. There may well be others. If we fail to do this, Process Designer will show "NullPointer" exceptions when you try and use the Java code.

If we wish to declare that our Java code has encountered a problem, we can throw an exception. The Exception should be an instance of TeamWorksException. This has a number of constructors available to us. We can catch the exception at the Java Integration layer.

When working with Java Integration, I have found it useful to de-compile the classes provided by the product to examine how some of the supplied classes operate. Personally, I use the "Java De-compiler" called JD found here:

http://jd.benow.ca/

The tool has both a stand-alone GUI as well as excellent integration with the Eclipse platform. Truly a top-notch effort here.

See also:

Java source level Debugging

When running Java code as part of the Process Application, we may wish to perform source level debugging against it. Fortunately, this is pretty simple to do. The WAS server hosting IBPM can have debugging enabled for it. Open a WAS Admin Console and navigate to Servers > Server Types > WebSphere application servers > <serverName> > Debugging Service.

Within that area, there will be a check-box (initially unchecked) called "Enable service at server startup". Check that check box and restart the server. Take note of the "JVM debug port" value which defaults to 7777.

Restart the server for the change to take effect. Once the server has been bounced, it will start listening on port 7777 for incoming debugger attachment requests.

I am assuming that Eclipse is used for Java development. After saving a project which contains the source of the Java code, we can create a Debug Profile in Eclipse. From the debug menu, select "Debug Configurations...."

This will open the debug configurations menu. Select/create a new entry under Remote Java Application.

Next click the Debug button. Switch to the Eclipse Debug perspective and set a breakpoint in your source code of your project.

Now when an integration service is execute which invokes the code, a breakpoint will be reached and you can step through the code:

eMail

IBPM has the ability to integrate with an external eMail provider. This can be used to originate an email during the execution of a process or receive an email for processing.

Sending an email

The System Data toolkit provides an integration service called "Send E-mail via SMTP". This can be invoked as a service through a Nested Service activity. The parameters to this service look as follows:

Parameter
smtpHost
to
from
subject
messageText
contentType
replyTo
cc
bcc
importance
attachmentFileNames

The service is implemented as the Java class called teamworks.Mail. This is contained within the "integration.jar" file. Examining the code for teamworks.Mail, it seems that:

Receiving an email

Similar to sending an email, the System Data Toolkit provides a supplied service called "Read E-mail via POP" and a second service called "Read E-mail via IMAP". These services retrieve one or more emails from an email in-box and return them for processing. If there are no emails to be retrieved then the call returns immediately with no results. One common way of using this feature is to create a service which is scheduled to start every period of time. When the service is started, it polls the email in-box and if no emails are present, it ends and awaits its next start. If an email is found, then a UCA is used to start a BPD instance to process the email. The start of the BPD is asynchronous and the email service can end before the BPDs have been processed.

The parameters for the service looks as follow:

Their description is:

Parameter
server
username
password
returnAttachment
scanForVariables
deleteAfterReading

The returned data is an XML data structure. When an email is retrieved, the format of the XML is as follows:

\<resultSet recordCount="7" columnCount="-1"\>
 \<record\>
 \<column name="MSG\_SUBJECT"\>test\</column\>
 \<column name="MSG\_TO"\>kolban@test.com\</column\>
 \<column name="MSG\_FROM"\>kolban@test.com\</column\>
 \<column name="MSG\_DATE"\>Mon Oct 25 10:50:22 CDT 2010\</column\>
 \<column name="MSG\_CC" /\>
 \<column name="MSG\_REPLY"\>kolban@test.com\</column\>
 \<column name="MSG\_NARRATIVE"\>Hello\</column\>
 \<column name="MSG\_ATTACHMENTS" /\>
 \</record\>
 …

The columns of data that come back include:


To handle the result, here are some example XML processing fragments.

Here is an example of getting data from the emails:

var countOfEmails = Number(tw.local.results.getAttribute("recordCount"));
 log.info(tw.local.results);
 for (var i=0; i\<countOfEmails; i++) {
 var subject = tw.local.results.xpath("/resultSet/record[" + (i+1) + "]/column[@name='MSG\_SUBJECT']").item(0).getText();
 var from = tw.local.results.xpath("/resultSet/record[" + (i+1) + "]/column[@name='MSG\_FROM']").item(0).getText();
 var body = tw.local.results.xpath("/resultSet/record[" + (i+1) + "]/column[@name='MSG\_NARRATIVE']").item(0).getText();
 log.info(subject);
 log.info(from);
 log.info(body);
 }

If there are attachments within the email we can declare that we are interested in processing these. By default, attachments will be ignored. To enable attachment processing, pass a value of 'true' as the 'returnAttachment' parameter. This will then cause a files to be written into the OS temp folder for each attachment included. The list of file names, one for each attachment, are returned in the 'MSG_ATTACHMENTS' property. If there are multiple attachments, they are comma separated.

When writing the emails to the temp folder, the physical files are given unique names such that if a file by the original name already exists, it is augmented with an index to make it unique.

|| |Note: Opinion – This feels like an extremely hokey solution. If an email arrives with a virus or other malware as an attachment, it will be written to the file system and exist as a plan file … a land-mine waiting to be trodden upon.|

See also:

Send an email and receive a response

On occasion, a process solution may wish to send an email and then wait for a response to be received before carrying on. This would typically be seen when a process needs to interact with an external party who has no access to the task lists being maintained by BPM. For example, if a process is handling a request from a customer and needs some additional information or confirmation before continuing, email may be the most appropriate way to achieve that.

We have already seen that we can send an email very easily but how then do we block waiting for a response email to arrive?

The solution to this puzzle from a BPM perspective is to utilize a Message Intermediate Event which will block the process until an event is published. That event will be the arrival of a response email. What we will do is set up a scheduler which runs every period of time (say 15 minutes). When it runs, a service is invoked which polls the eMail system and retrieves each of the emails in a particular inbox. For each email received, the subject, sender and content may be examined. From this data, a correlation value will need to be found. An event will then be published using that correlation value.

When the original email is sent, imagine that a unique "id" is added to the subject line. It may look like:

Subject: [yT1hP] – Request for information

It could alternatively be added into the body.

This "tag" should also be found in a response email. By matching the request tag to the response tag, we will have our correlation value.

Here is an example of a service which examines incoming emails and publishes events:

First the set of all the emails is retrieved. These emails are returned in an XML document which isn't that convenient to work with. We first convert each email into a Business Object instance defined as:

At the time of initial parsing, the sender, subject and body portions are populated.

Next we start looping through each of the emails returned. We examine each email looking for a correlation value (in this example called "requestId"). In this example, the following code was used:

// Get the Request ID from the subject .... it will be found in '... [xxxx] …'
 // Build a pattern which is:
 // o Any number of characters followed by a '[' followed by any number of characters terminated by a ']' followed by any number of characters
 tw.local.currentEmailResponse = tw.local.responses[tw.local.responseIndex];
 tw.local.currentEmailResponse.success = false;
 tw.local.readyForPublish = false;
 var pattern = /.\*\\[(.\*)\\].\*/;
 var reqId = pattern.exec(tw.local.currentEmailResponse.subject);
 if (reqId == null) {
 log.info("No match!");
 } else {
 tw.local.currentEmailResponse.requestId = reqId[1];
 if (tw.local.currentEmailResponse.subject.match(/success/i) != null) {
 tw.local.currentEmailResponse.success = true;
 }
 tw.local.readyForPublish = true;
 }
 log.info("[Examine Emails]: RequestId: " + tw.local.currentEmailResponse.requestId + ", success = " + tw.local.currentEmailResponse.success );
 // End of file

The core code in this is the use of a JavaScript regular expression to look for the tag in the Subject line.

This piece of wonderful JavaScript achieves that task:

var pattern = /.\*\\[(.\*)\\].\*/;
 var reqId = pattern.exec(tw.local.currentEmailResponse.subject);

It says "Match any string of characters up till a '[' and then find any string of characters up till a corresponding ']' and remember the result". Now search the Subject line using that pattern and retrieve the matched data between the '[' and ']' characters and call that the requested Id.

How we ask the email responder to reply is also a choice that we can make. We can ask them to simply reply to the email and add comments to the Subject or simply pass the body back with the event publish so that it can be examined by the blocked process when it wakes up.

Sending an email which includes response buttons

Another permutation that is of interest to us is the notion that we might want to send an email to someone from within a process, and within the body of that email there are immediate response buttons. Here is a mock-up of what an email may look like when it is received by a user:

The body of the email will contain sufficient description for the reader to understand how to handle and all they need do is click either "Approve" or "Reject" to indicate the outcome. How then can we model and implement such a story?

There are likely going to be many ways to achieve this effect and here is one.

From a high level process perspective, our modeled process will have the following design:

A process (shown at the bottom) will send an email to a user. It will then wait for a response. The user will then see the sent email in their email in box and will either approve or reject. The act of clicking the Approval or Rejection button will result in the wake up of the BPM process which will now know the choice made by the user and the process may continue.

Now we need to start breaking this design down into its constituent components.

The first is sending the email. That has been covered in depth elsewhere in this book however there is now going to be a relationship between what is contained in the email and the wake up of the process. When an email is received by a user, the body of the email can be encoded as either plain text (simple) or as HTML. It needs to be noted that the message viewing area of an email client should not be confused with that of a simple browser. Either the viewing area constrains what can be shown or else the email client "re-writes" the body to permit only a small subset of the possible HTML that could be shown in a real browser. This is to prevent obvious security exposures. Imagine that I sent you a malicious email who's body contained HTML that was undesirable. It could contain inappropriate images, or JavaScript or any number of ugly items. Commonly, before one looks at the body of an email, one has very little knowledge of precisely what is contained within it. As such, email clients typically constrain what may be done.

However, the key to our story is that one specific useful function is allowed … namely, the hypertext link. An email body can contain explicit links to other web pages. When the email user clicks such a link, a new browser is opened to that page. Since this requires an explicit act from the email user, opening a new link is not considered a security exposure. Rather, it is considered a useful and necessary function. Imagine that you receive an email which says "Have a look at this new offering from our partner …" … it would be great to embed a link to the corresponding web page that would take the reader to the desired page with the minimum of fuss.

Putting these pieces together, we can envisage a design that might look as follows:

This takes a little explanation. Let us follow the story from a bottom up perspective.

From within a process, we have a BPM activity that is blocking waiting for an event. In BPM these events are unfortunately called "UCAs". The BPM process will not wake up until and unless a UCA event occurs. Within a BPM process, we can trigger the arrival of a UCA event from within another process or service, but that doesn't help us here. However, we can also trigger a UCA event through a BPM REST service called "sendMessage". This is a REST service that is exposed from BPM that, when called with the correct parameters, will cause BPM to fire the corresponding UCA event. If we start to factor these items out, we see that if BPM receives a "sendMessage" REST request, the blocking BPM process will wake up. Unfortunately, we can't make that call directly from an email client as the format of the request isn't similar to that of a HTTP GET caused by a hyper link traversal. Instead, we introduce a simple Java EE application that listens for an incoming REST request (the HTTP GET from the button click) and, upon receipt of that request, forwards out a BPM REST "sendMessage" event to the target BPM server.

Although at first glance, this may seem to add more complexity, the reality is that it additionally solves a number of other problems such as the security exposures of making BPM available directly to the Internet.

If we look at all the pieces, the one that we don't yet have is the Java EE application. Fortunately, it is extremely easy to build such a thing. Java EE provides an implementation of the specification known as JAX-RS which, via simple annotation, exposes REST Web Service with ease.

Here is an example of a simple handler for an HTTP GET request from an email message body:

package com.kolban.bpm.events;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.QueryParam;
 @Path("/approve")
 public class ApproveEvent {
 @GET
 public String approve(@QueryParam("instance") String instance, @QueryParam("outcome") String
 outcome) {
 System.out.println("Approval called: instance=" + instance + ", outcome=" + outcome);
 SendEvent.sendMessage("EBC", "c943b980-e320-4d16-804f-a117199ba402", "\<parameter\>\<key\>instance\</key\>\<value\>" + instance + "\</value\>\</parameter\>\<parameter\>\<key\>outcome\</key\>\<value\>" + outcome + "\</value\>\</parameter\>");
 return "Approval registered: Your outcome was: " + outcome;
 }
 }

The way to read this code fragment is that when an incoming GET request arrives at the relative URL of "/approve", extract the query parameters called "instance" and "outcome". So, for example, a REST request to:

http://<host:port>/approve?instance=123&outcome=approve

will trigger this code fragment. The body of the code calls a method called "sendMessage" which sends a REST request to BPM to trigger the UCA. The body of THAT method looks as follows:

package com.kolban.bpm.events;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import com.ibm.ws.util.Base64;
 public class SendEvent {
 public static void sendMessage(String processApp, String eventName, String message) {
 try {
 URL url = new URL("http://localhost:9081/rest/bpm/wle/v1/process");
 HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
 String wasUserName = "kolban";
 String wasPassword = "password";
 String authorization = "Basic " + Base64.encode((new String(wasUserName + ":" +
 wasPassword).getBytes()));
 httpUrlConnection.addRequestProperty("Authorization", authorization);
 httpUrlConnection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
 httpUrlConnection.addRequestProperty("X-HTTP-Method-Override", "POST");
 httpUrlConnection.setRequestMethod("POST");
 httpUrlConnection.setDoOutput(true);
 String payload = "\<eventmsg\>\<event processApp='" + processApp + "'\>" + eventName +
 "\</event\>\<parameters\>" + message + "\</parameters\>\</eventmsg\>";
 String data = "action=sendMessage&message=" + payload;
 OutputStreamWriter wr = new OutputStreamWriter(httpUrlConnection.getOutputStream());
 wr.write(data);
 wr.flush();
 InputStreamReader inReader = new InputStreamReader(httpUrlConnection.getInputStream());
 BufferedReader br = new BufferedReader(inReader);
 String line = br.readLine();
 while (line != null) {
 System.out.println("Line is: " + line);
 line = br.readLine();
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }

The construction of the sending of the email could look as follows:

It is unlikely that you will want to simply have hyper-links in place of buttons. Fortunately, some CSS styling can take care of that for you. A great resource on the web for building buttons is as follows:

Bulletproof Email Buttons

From there, you can enter your desired button look and feel plus the URL to the WAS REST service and the HTML that is to be inserted into your email is generated for you.

All that remains is to build a BPD and test it out:

There are many tweaks that can be made to this story, some of the more obvious ones include:


Installing hMailServer as a test EMail provider

When working with emails, it is useful to have a sandbox email server against which to test sending emails. A good and free email server package for this purpose is called hMailServer which is available for download:

http://www.hmailserver.com/

Installation of the package is very simple as shown in the following walk through.

When the installer is first launched, we get the standard installer welcome message.

Next, read and review the license agreement and if you concur, accept and continue.

The EMail server will be installed on your local Windows file system. Here you get the opportunity to select the location where the product will be installed.

The EMail server has two components. One being the EMail server and the other being its basic Administration tools. Both should be installed.

Even though we plan to use the EMail server for unit testing, the EMail server wants to know where to save its emails. Select the in-built Microsoft SQL database engine to minimize further configuration. The footprint is small and no explicit configuration is required.

Finally, we are asked what we would like the EMail server to be known as in our start menu.

We are shown the final summary screen and then installation progresses.

Once installed, the hMailServer administration console can be launched.

When testing with hMailServer, it is a good idea to switch off the auto-ban feature. Auto-ban will disable a host from being able to make mail connections because too many failed attempts have been recorded. To switch off auto-ban, un-check the enabled check box in the Auto-ban settings:

If a ban has already happened, you will be able to undo the ban from the IP Ranges menu entry where an entry will be found from the host making the connection requests.

Security may also be a problem. BPM does not provide support for SMTP authentication. We can disable the authentication requirements in the IP Ranges section:

javax.mail can be debugged by adding the "mail.debug=true" flag in the System Properties, or so it is claimed on the web however I have not been able to find a recipe to switch this on.

The hMailServer and various other mail servers want to give userids the format "xxx@yyy" for login and access. Unfortunately, this seems to break the parsing of a userid. A work-around is to code as "xxx%40yyy". The "%40" is the hex escape code for the "@" symbol.

WPS and SCA

Prior to the release of IBPM 7.5, IBM had two separate BPM products. One of those was called WebSphere Process Server (WPS). WPS was a heavy utilizer of the SCA technologies. There may be occasions where an IBPM 7.5 environment may wish to interact with a legacy WPS environment.

Asynchronous Invocation of an SCA Module

Consider a IBPM process that wishes to invoke an instance of an old WPS or WESB hosted module. That module might take some time to respond. Conceivably, it could take days if a Human Task in WPS is utilized. As such, we can't afford to keep a connection from IBPM to SCA open for that length of time. One possible solution to our problem is leverage two one-way invocations. One from IBPM to SCA to start the module and later, when the SCA module has completed its work, one further invocation back from SCA to IBPM to allow the waiting IBPM hosted process to continue.

In this story, we will assume that the SCA Module exposes an interface called SCA_I1 which is a one-way interface that has an input variable called in1 of data type BO1 (a Business Object).

The interface looks like:

Once the module has been deployed, we now find that we have an exposed Web Service that when invoked, will start an instance of that module. Looking at the binding of the SCA Export, we find that we have the WSDL URL. However, WPS is pulling a trick on us here. If we added a "?wsdl" to that URL an HTTP redirect would occur which would break IBPM. So what we do is manually add a "?wsdl" to the URL in a browser and get the real URL to the WSDL.

becomes (when adding a "?wsdl" on the end)

After running through the IBPM Web Service integration steps and importing the data types, we end up with a new data type called BO1 which has the properties we desire:

and we also find that if we can see the settings/operation in the Implementation Web Service tab:

If we now wire a test service as follows:

and invoke it, we find that the SCA module has indeed been called:

Where the implementation of LogCall looks like:

public void op1(DataObject in1) {
 System.out.println("Op1 called: " + in1);
 }

We find the following in the output log:

[3/7/11 16:57:13:326 CST] 0000007c SystemOut O Op1 called: BusinessObject: BO1@41c441c4 (f1=f1 val, f2=f2 val, f3=f3 val)

Hooray. This is half the challenge. We have now called SCA from IBPM. Now we need to wait for a response and have SCA call us back. Here we will assume that IBPM exposes a Web Service interface. This one will be invoked by the SCA module when the response is ready to be sent. Exposing a IBPM service is described elsewhere.

Once we have configured IBPM to expose a Web Service, we can import that Web Service definition into the SCA module. In the IBPM Web Service definition, we find the WSDL URI for the exposed service. We need to copy that URI into the copy buffer.

In WID, select the Imports and from there navigate to Business Integration > WSDL and XSD

Select the source of the WSDL to be imported as a remote artifact existing on the net.

Paste the URL that was selected from the IBPM Web Services component. At this point you can select Finish and the artifacts will be created.

At the culmination, a new Web Services interface definition will be found in your project. Any data types associated with that interface will also be created.

An SCA Import component can then be used to call the IBPM web service.

Remember that correlation must be utilized in this solution. There must be some data in the returned response that allows IBPM to know which instance of the BPD being blocked by an Intermediate Message Event to wake up. A suitable BPD looks as follows:

The Intermediate Message Event is triggered by a UCA which is itself invoked by the SCA originated Web Service call. The Intermediate Message Event's configuration names a UCA supplied parameter that must match a correlation value in the originating BPD.

Integration with Node-RED

For inbound integration with BPM, we can expose Web Services which use SOAP/HTTP. We can then use a SOAP caller such as Node-RED to send in-bound messages. For outbound, we can also send SOAP requests from BPM and have a SOAP listener running within Node-RED to catch the incoming requests.

For some situations, we may want BPM to receive UCA events. In this instance, we have choices. We can use an in-bound SOAP request or else we can use an in-bound REST request that triggers a UCA.

To send an event into BPM, we will use the soap request node.

This node will send a SOAP request to BPM and, as such, we must also define an exposed Web Service definition in BPM. In that definition we will find the operation name and the WSDL URL for the service which are both parameters to the Node-RED node:

We must also flag the Web Service definition as using SOAP 1.1.

When invoking BPM, we may find that we get an error related to the use of self signed certificates in regards to SSL. This is quickly resolved by setting the environment variable called NODE_TLS_REJECT_UNAUTHORIZED to "0".

No Comments
Back to top