Chapter 3. Rule Flow

Table of Contents

Creating a RuleFlow process
Using the graphical RuleFlow editor
Defining processes using XML
Defining processes using the Process API
Using a process in your application
Detailed explanation of the different node types
Data
Constraints
Actions
Events
Exceptions
Timers
Assigning rules to a ruleflow group
A simple ruleflow

Figure 3.1. Ruleflow

Ruleflow

A RuleFlow is a process that describes the order in which a series of steps need to be executed, using a flow chart. A process consists of a collection of nodes that are linked to each other using connections. Each of the nodes represents one step in the overall process while the connections specify how to transition from one node to the other. A large selection of predefined node types have been defined. This chapter describes how to define such processes and use them in your application.

Creating a RuleFlow process

Processes can be created by using one of the following three methods:

  1. Using the graphical RuleFlow editor in the Drools plug-in for Eclipse
  2. As an XML file, according to the XML process format as defined in the 'drools-processes' XML Schema Definition.
  3. By directly creating a process using the Process API.

Using the graphical RuleFlow editor

The graphical RuleFlow editor is a editor that allows you to create a process by dragging and dropping different nodes on a canvas and editing the properties of these nodes. The graphical RuleFlow editor is part of the Drools plug-in for Eclipse. Once you have set up a Drools project (check the IDE chapter if you do not know how to do this), you can start adding processes. When in a project, launch the 'New' wizard (use "Ctrl+N" or by right-clicking the directory you would like to put your ruleflow in and selecting "New ... -> Other ...". Choose the section on "Drools" and then pick "RuleFlow file". This will create a new .rf file.

Figure 3.2. Creating a new RuleFlow file

Creating a new RuleFlow file

Next you will see the graphical ruleflow editor. Now would be a good time to switch to the "Drools Perspective" (if you haven't done so already) - this will tweak the UI so it is optimal for rules. Then ensure that you can see the "Properties" view down the bottom of the Eclipse window, as it will be necessary to fill in the different properties of the elements in your process. If you cannot see the properties view, open it using the menu Window -> Show View -> Other ..., and under the General folder select the properties view.

Figure 3.3. New RuleFlow process

New RuleFlow process

The RuleFlow editor consists of a palette, a canvas and an outline view. To add new elements to the canvas, select the element you would like to create in the palette and then add them to the canvas by clicking on the preferred location. For example, click on the RuleFlowGroup icon in the Component Pallette of the GUI - you can then draw a few rule flow groups. Clicking on an element in your ruleflow allows you to set the properties of that element.You can link the nodes together (as long as it is allowed by the different types of nodes) by using "Connection Creation" from the component palette.

You can keep adding nodes and connections to your process until it represents the business logic that you want to specify. You'll probably need to check the process for any missing information (by pressing the green "check" icon in the IDE menu bar) before trying to use it in your application.

Defining processes using XML

It is also possible to specify processes using the underlying XML directly. The syntax of these XML processes is defined using an XML Schema Definition. For example, the following XML fragment shows a simple process that contains a sequence of a start node, an action node that prints "Hello World" to the console, and an end node.

<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://drools.org/drools-5.0/process"
         xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
         xs:schemaLocation="http://drools.org/drools-5.0/process drools-processes-5.0.xsd"
         type="RuleFlow" name="ruleflow" id="com.sample.ruleflow" package-name="com.sample" >

  <header>
  </header>

  <nodes>
    <start id="1" name="Start" x="16" y="16" />
    <actionNode id="2" name="Hello" x="128" y="16" >
      <action type="expression" dialect="mvel" >System.out.println("Hello World");</action>
    </actionNode>
    <end id="3" name="End" x="240" y="16" />
  </nodes>

  <connections>
    <connection from="1" to="2" />
    <connection from="2" to="3" />
  </connections>

</process>

The process XML file should consist of exactly one <process> element. This element contains parameters related to the process (the type, name, id and package name of the process), and consists of three main subsections: a <header> (where process-level information like variables, globals, imports and swimlanes can be defined), a <nodes> section that defines each of the nodes in the process (there is a specific element for each of the different node types that defines the various parameters and possibly sub-elements for that node type), and a <connections> section that contains the connections between all the nodes in the process.

Defining processes using the Process API

While it is recommended to define processes using the graphical editor or the underlying XML (to shield yourself from internal APIs), it is also possible to define a process using the Process API directly. The most important process elements are defined in the org.drools.workflow.core and the org.drools.workflow.core.node packages. A "fluent API" is provided that allows you to easily construct processes in a readable manner using factories. At the end, you can validate the process that you were constructing manually. Some examples about how to build processes using this fluent API are added below.

Example 1

This is a simple example of a basic process with a ruleset node only:

      RuleFlowProcessFactory factory =
          RuleFlowProcessFactory.createProcess("org.drools.HelloWorldRuleSet");
      factory
          // Header
          .name("HelloWorldRuleSet")
          .version("1.0")
          .packageName("org.drools")
          // Nodes
          .startNode(1).name("Start").done()
          .ruleSetNode(2)
              .name("RuleSet")
              .ruleFlowGroup("someGroup").done()
          .endNode(3).name("End").done()
          // Connections
          .connection(1, 2)
          .connection(2, 3);
      RuleFlowProcess process = factory.validate().getProcess();
        

You can see that we start by calling the static createProcess() method from the RuleFlowProcessFactory class. This method creates a new process with the given id and returns the RuleFlowProcessFactory that can be used to create the process. A typical process consists of three parts: a header part that contains global elements like the name of the process, imports, variables, etc. The nodes section contains all the different nodes that are part of the process and finally the connections section links these nodes to each other to create a flow chart.

So in this example, the header contains the name and the version of the process and the package name. After that you can start adding nodes to the current process. If you have auto-completion you can see that you different methods to create each of the supported node types at your disposal.

When you start adding nodes to the process, in this example by calling the startNode(), ruleSetNode() and endNode() methods, you can see that these methods return a specific NodeFactory, that allows you to set the properties of that node. Once you have finished configuring that specific node, the done() method returns you to the current RuleFlowProcessFactory so you can add more nodes if necessary.

When you finised adding nodes you must connect them by creating connections between them. This can be done by calling the connection method, which will link the earlier created nodes.

Finally, you can validate the generated process by calling the validate() method and retrieve the create RuleFlowProcess object.

Example 2

This example is using Split and Join nodes:

      RuleFlowProcessFactory factory =
          RuleFlowProcessFactory.createProcess("org.drools.HelloWorldJoinSplit");
      factory
          // Header
          .name("HelloWorldJoinSplit")
          .version("1.0")
          .packageName("org.drools")
          // Nodes
          .startNode(1).name("Start").done()
          .splitNode(2).name("Split").type(Split.TYPE_AND).done()
          .actionNode(3).name("Action 1").action("mvel", "System.out.println(\"Inside Action 1\")").done()
          .actionNode(4).name("Action 2").action("mvel", "System.out.println(\"Inside Action 2\")").done()
          .joinNode(5).type(Join.TYPE_AND).done()
          .endNode(6).name("End").done()
          // Connections
          .connection(1, 2)
          .connection(2, 3)
          .connection(2, 4)
          .connection(3, 5)
          .connection(4, 5)
          .connection(5, 6);
      RuleFlowProcess process = factory.validate().getProcess();
        

This shows a simple split / join example. As you can see, split nodes can have multiple outgoing connections and joins multipe incomming connections. To understand the behaviour of the different types of split and join nodes, take a look at the documentation for each of these nodes.

Example 3

Now we show a more complex example with a ForEach node, where we have nested nodes:

      RuleFlowProcessFactory factory =
          RuleFlowProcessFactory.createProcess("org.drools.HelloWorldForeach");
      factory
          // Header
          .name("HelloWorldForeach")
          .version("1.0")
          .packageName("org.drools")
          // Nodes
          .startNode(1).name("Start").done()
          .forEachNode(2)
              // Properties
              .linkIncomingConnections(3)
              .linkOutgoingConnections(4)
              .collectionExpression("persons")
              .variable("child", new ObjectDataType("org.drools.Person"))
              // Nodes
              .actionNode(3)
                  .action("mvel", "System.out.println(\"inside action1\")").done()
              .actionNode(4)
                  .action("mvel", "System.out.println(\"inside action2\")").done()
              // Connections
              .connection(3, 4)
              .done()
          .endNode(5).name("End").done()
          // Connections
          .connection(1, 2)
          .connection(2, 5);
      RuleFlowProcess process = factory.validate().getProcess();
        

Here you can see how we can include a ForEach node with nested action nodes. Note the linkIncommingConnections() and linkOutgoingConnections() methods that are called to link the foreach node with the internal action node. These methods are used to specify what the first and last nodes are inside the ForEach composite node.