SwitchYard BPM Component

Business Process Management functionality has been added to SwitchYard via the BPM Component.  The BPM Component leverages jBPM 5 (currently version 5.1.0.Final) under the hood to accomplish this.

 

To illustrate this new functionality, a new Quickstart Demo has been created.  The business process is a "Help Desk" workflow, and is available in the quickstarts git repository under demos/helpdesk/.  Let's start by looking at a graphical representation of the process:

 

helpdesk.png

 

Before we explain the process itself, a little information about the IDE.  It is Eclipse, with the jBPM 5.1.0.Final plugin installed.  This plugin allows you to graphically author BPMN2 processes via drag-and-drop and properties editors - much easier than hand-authoring the XML, although you can do that too, and toggle back-and-forth.

 

On the left we see various workflow components that can be added to the process. In our example, the SwitchYard "Service" task is of primary interest.  What this allows one to do is declare a SwitchYard Service invocation from within the process.  Once you add a SwitchYard Service node to your process, you can click on it and edit its configuration using the Eclipse Properties View.  Two properties are of primary interest:

  1. ServiceName: The name of the SwitchYard Service to invoke.
  2. ServiceOperationName: The name of the operation exposed by that Service to invoke.

That's all you have to do to integrate SwitchYard Services into your process!

 

In our Help Desk example, you have five SwitchYard Service nodes: Open Ticket, Approve Ticket, Close Ticket, Reject Ticket and Request Details.  In each case, the ServiceName is the same: TicketManagementService.  However, each of their ServiceOperationNames is different: openTicket, approveTicket, closeTicket, rejectTicket and requestDetails.  There is nothing saying each node couldn't be calling completely separate services, however in our example we just use different operations on the same service for simplicity.  Next let's look at the TicketManagementService:

 

public interface TicketManagementService {
    public TicketAck openTicket(Ticket ticket);
    public void approveTicket(Ticket ticket);
    public void closeTicket(Ticket ticket);
    public void requestDetails(Ticket ticket);
    public void rejectTicket(Ticket ticket);
}

 

So in the case of the "Open Ticket" process node, you would configure:

 

PropertyValue
ServiceNameTicketManagementService
ServiceOperationNameopenTicket

 

The implementation of TicketMangagementService in our example is TicketManagementServiceBean, and SwitchYard uses our Bean Component to execute the implementation using CDI.  Please refer to the Bean Component documentation for information on how this works. The method bodies of TicketManagementServiceBean don't do much, as it isn't important for the example.

 

Now that you know how to invoke SwitchYardServices from within a BPM process, how do we interact with a BPM process using SwitchYard?  The low-level answer is that we use a SwitchYard ExchangeHandler to do the work (specifically, the DroolsBpmExchangeHandler), however you don't need to have knowledge of that code to do what you need to do.

 

To interact with the process, you need to create an interface to your process.  In our case, it is HelpDeskService, which looks like this:

 

public interface HelpDeskService {
    public TicketAck openTicket(Ticket ticket);
}

 

You can see that there is nothing tied to a BPM implementation in this interface. And there shouldn't be.  But how do we "wire in" this interface with our process?  Well, you have a couple choices here.  You could either write your own XML section inside a META-INF/switchyard.xml file, or you can have that XML auto-generated for you from an annotation.  In our example, we do just that:

 

import org.switchyard.component.bpm.Process;
import org.switchyard.component.bpm.task.jbpm.CommandBasedWSHumanTaskHandler;
@Process(
    value = HelpDeskService.class,
    taskHandlers = { CommandBasedWSHumanTaskHandler.class }
)
public final class HelpDeskServiceProcess {}

 

The first thing you might notice is that there isn't any code inside HelpDeskServiceProcess. What gives???  Well, you really don't have to have any code here, because what the process does is defined in the bpmn file itself, not here.  This class is just a way for us to auto-configure switchyard for you, and expose what you want exposed (via the processInterface HelpDeskService).  This is what the final META-INF/switchyard.xml file looks like (note that the SOAP binding XML was merged in via our provided Maven plugin):

 

<?xml version="1.0" encoding="UTF-8"?>
<switchyard xmlns="urn:switchyard-config:switchyard:1.0" name="helpdesk">
    <composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" name="helpdesk" targetNamespace="urn:switchyard-quickstart-demo:helpdesk:0.1.0">
        <service name="HelpDeskService" promote="HelpDeskService">
            <binding.soap xmlns="urn:switchyard-component-soap:config:1.0">
                <wsdl>META-INF/HelpDeskService.wsdl</wsdl>
                <serverPort>18001</serverPort>
                <contextMapper xmlns="urn:switchyard-config:switchyard:1.0" includeNamespaces="urn:switchyard-component-bpm:process:1.0"/>
            </binding.soap>
        </service>
        <component name="HelpDeskService">
            <implementation.bpm xmlns="urn:switchyard-component-bpm:config:1.0" processDefinition="META-INF/HelpDeskService.bpmn" processDefinitionType="BPMN2" processId="HelpDeskService">
                <taskHandler class="org.switchyard.component.bpm.task.SwitchYardServiceTaskHandler" name="SwitchYard Service"/>
                <taskHandler class="org.switchyard.component.bpm.task.jbpm.CommandBasedWSHumanTaskHandler" name="Human Task"/>
            </implementation.bpm>
            <service name="HelpDeskService">
                <interface.java interface="org.switchyard.quickstarts.demos.helpdesk.HelpDeskService"/>
            </service>
        </component>
        <component name="TicketManagementService">
            <implementation.bean xmlns="urn:switchyard-component-bean:config:1.0" class="org.switchyard.quickstarts.demos.helpdesk.TicketManagementServiceBean"/>
            <service name="TicketManagementService">
                <interface.java interface="org.switchyard.quickstarts.demos.helpdesk.TicketManagementService"/>
            </service>
        </component>
    </composite>
    <transforms>
        <transform.java xmlns="urn:switchyard-config:transform:1.0" class="org.switchyard.quickstarts.demos.helpdesk.Transformers" from="{urn:switchyard-quickstart-demo:helpdesk:1.0}openTicket" to="java:org.switchyard.quickstarts.demos.helpdesk.Ticket"/>
        <transform.java xmlns="urn:switchyard-config:transform:1.0" class="org.switchyard.quickstarts.demos.helpdesk.Transformers" from="java:org.switchyard.quickstarts.demos.helpdesk.TicketAck" to="{urn:switchyard-quickstart-demo:helpdesk:1.0}openTicketResponse"/>
        <transform.java xmlns="urn:switchyard-config:transform:1.0" class="org.switchyard.quickstarts.demos.helpdesk.Transformers" from="java:org.switchyard.quickstarts.demos.helpdesk.TicketAck" to="java:org.switchyard.quickstarts.demos.helpdesk.Ticket"/>
    </transforms>
</switchyard>

 

Most everything in this META-INF/switchyard.xml file was auto-generated. Of particular interest to us is the HelpDesk section above.  Note the interface.java of HelpDeskService, and the processDefinition of META-INF/HelpDeskService.bpmn.  In fact, all the attributes of implementation.bpm were defaulted to sensible values, based on the name of the Service interface: HelpDeskService.  However, if you want to customize those values, you can override the defaults by setting other values of the Process annotation properties:

 

// (shortened for brevity)
public @interface Process {
    public Class<?> value();
    public String definition();
    public ProcessResourceType definitionType() default BPMN2;
    public String id();
    public String[] resources();
    public Class<? extends TaskHandler>[] taskHandlers();
}

 

definition is the location of the process definition, definitionType can be anything the jBPM5 engine supports (since we use that under the covers) and defaults to BPMN2, the id allows you to specify the id of the process, resources allows you to specify any number of other resources (like Drools rules files, etc.) the process might require, and taskHandlers allows you to plug in your own code to handle specific pieces of the process.

 

A note about TaskHandlers: TaskHandlers allow you to extend the functionality of your process via domain-specific work.  In our example, the SwitchYardServiceTaskHandler is automatically added, and is the mechanism by which we can handle the SwitchYard Service nodes within the process.  The CommandBasedWSHumanTaskHandler is there to leverage jBPM's WS-Human Task capability.  You can write your own TaskHandlers (most likely as inner classes of something similar to our example's HelpDeskServiceProcess), however this is beyond the scope of our example here, and will be described in a future, separate article.

 

Next is process execution.  In our case, the only integration point with the process is the single method defined by HelpDeskService: openTicket.  Our test code interacts with the SOAP Component's gateway by sending it an SOAP XML request:

 

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:bpm="urn:switchyard-component-bpm:process:1.0"
    xmlns:helpdesk="urn:switchyard-quickstart-demo:helpdesk:1.0">
    <!-- The default processActionType is "startProcess", but here's how it would look:
    <soap:Header>
        <bpm:processActionType>startProcess</bpm:processActionType>
    </soap:Header>
    -->
    <soap:Body>
        <helpdesk:openTicket>
            <ticket>
                <id>TCKT-17761852</id>
            </ticket>
        </helpdesk:openTicket>
    </soap:Body>
</soap:Envelope>

 

This will, in turn, kick off our process, and if the process stores a process instance variable with the name {urn:switchyard-component-bpm:process:1.0}messageContent (the name of the variable is configurable), this content will be returned in the SOAP XML response.  In our case:

 

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:bpm="urn:switchyard-component-bpm:process:1.0"
    xmlns:helpdesk="urn:switchyard-quickstart-demo:helpdesk:1.0">
    <SOAP-ENV:Header>
        <bpm:processInstanceId>1</bpm:processInstanceId>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
        <helpdesk:openTicketResponse>
            <ticketAck>
                <id>TCKT-17761852</id>
                <received>true</received>
            </ticketAck>
        </helpdesk:openTicketResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

A note about data tranformation: At this point you might wonder how the ticket and ticketAck XML gets transformed between Ticket and TicketAck java objects. The answer lies within SwitchYard's Transformation capability, and is described in that documentation.

 

Once the ticket has been successfully opened, and the acknowledgement returned, the rest of the process will execute as defined in the BPMN process definition.  Remember that further interaction with the BPM process can be done (beyond just starting it), but is not shown in this example.  Looking ahead, one might want to define an event in the process, perhaps representing an external interaction.  This event might come in as another SOAP request.  As long as there exists a <bpm:processInstanceId> SOAP header which correlates back to the correct process instance, once can signal an event with data in that request, or possibly even abort the process instance all together.  A future article will showcase process instance signaling.

 

Comments and criticism welcome!