ch02-GettingStarted.adoc 32.5 KB
Newer Older
J
Joram Barrez 已提交
1 2 3

== Getting Started

4
=== What is Flowable?
J
Joram Barrez 已提交
5

T
Tijs Rademakers 已提交
6
Flowable is a light-weight business process engine written in Java. The Flowable process engine allows you to deploy BPMN 2.0 process definitions (an industry XML standard for defining processes), 
P
PHH 已提交
7 8
creating process instances of those process definitions, running queries, accessing active or historical process instances and related data, plus much more. This section will gradually introduce various concepts and 
APIs to do that through examples that you can follow on your own development machine.
J
Joram Barrez 已提交
9

P
PHH 已提交
10
Flowable is extremely flexible when it comes to adding it to your application/services/architecture. You can _embed_ the engine in your application or service by including the Flowable library, 
D
David B Malkovsky 已提交
11
which is available as a JAR. Since it's a JAR, you can add it easily to any Java environment: Java SE; servlet containers, such as Tomcat or Jetty, Spring; Java EE servers, such as JBoss or WebSphere, and so on.
P
PHH 已提交
12
Alternatively, you can use the Flowable REST API to communicate over HTTP. There are also several Flowable Applications (Flowable Modeler, Flowable Admin, Flowable IDM and Flowable Task) that offer out-of-the-box example UIs for working with processes and tasks.
J
Joram Barrez 已提交
13

T
Tijs Rademakers 已提交
14 15
Common to all the ways of setting up Flowable is the core engine, which can be seen as a collection of services that expose APIs to manage and execute business processes. 
The various tutorials below start by introducing how to set up and use this core engine. The sections afterwards build upon the knowledge acquired in the previous sections.
J
Joram Barrez 已提交
16

P
PHH 已提交
17 18 19
* The <<getting.started.command.line, first section>> shows how to run Flowable in the simplest way possible: a regular Java main using only Java SE. Many core concepts and APIs will be explained here.
* The <<getting.started.rest, section on the Flowable REST API>> shows how to run and use the same API through REST.
* The <<getting.started.flowable.app, section on the Flowable App>>, will guide you through the basics of using the out-of-the-box example Flowable user interfaces.
J
Joram Barrez 已提交
20

21
=== Flowable and Activiti
J
Joram Barrez 已提交
22

P
PHH 已提交
23
Flowable is a fork of Activiti (registered trademark of Alfresco). In all the following sections you’ll notice that the package names, configuration files, and so on, use _flowable_.
J
Joram Barrez 已提交
24

25 26
[[getting.started.command.line]]
=== Building a command-line application
J
Joram Barrez 已提交
27

28
==== Creating a process engine
J
Joram Barrez 已提交
29

P
PHH 已提交
30 31
In this first tutorial we're going to build a simple example that shows how to create a Flowable process engine, introduces some core concepts and shows how to work with the API. 
The screenshots show Eclipse, but any IDE works. We'll use Maven to fetch the Flowable dependencies and manage the build, but likewise, any alternative also works (Gradle, Ivy, and so on).
J
Joram Barrez 已提交
32

33
The example we'll build is a simple _holiday request_ process:
J
Joram Barrez 已提交
34

35 36
* the _employee_ asks for a number of holidays
* the _manager_ either approves or rejects the request
P
PHH 已提交
37
* we'll mimic registering the request in some external system and sending out an email to the employee with the result
J
Joram Barrez 已提交
38

P
PHH 已提交
39
First, we create a new Maven project through _File -> New -> Other -> Maven Project_
J
Joram Barrez 已提交
40

41
image::images/getting.started.new.maven.png[align="center"]
J
Joram Barrez 已提交
42

43
In the next screen, we check '_create a simple project (skip archetype selection)_'
J
Joram Barrez 已提交
44

45
image::images/getting.started.new.maven2.png[align="center"]
J
Joram Barrez 已提交
46

47
And fill in some _'Group Id'_ and _'Artifact id'_:
J
Joram Barrez 已提交
48

49
image::images/getting.started.new.maven3.png[align="center"]
J
Joram Barrez 已提交
50

51
We now have an empty Maven project, to which we'll add two dependencies:
J
Joram Barrez 已提交
52

P
PHH 已提交
53
* The Flowable process engine, which will allow us to create a ProcessEngine object and access the Flowable APIs.
T
Tijs Rademakers 已提交
54
* An in-memory database, H2 in this case, as the Flowable engine needs a database to store execution and historical data while running process instances. 
P
PHH 已提交
55
Note that the H2 dependency includes both the database _and_ the driver. If you use another database (for example, PostgresQL, MySQL, and so on), you'll need to add the specific database driver dependency.
J
Joram Barrez 已提交
56

57
Add the following to your _pom.xml_ file:
J
Joram Barrez 已提交
58

59 60 61 62 63 64
[source,xml,linenums]
----
<dependencies>
  <dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-engine</artifactId>
T
Tijs Rademakers 已提交
65
    <version>6.3.0</version>
66 67 68 69
  </dependency>
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
T
Tijs Rademakers 已提交
70
    <version>1.3.176</version>
71 72 73 74
  </dependency>
</dependencies>
----

P
PHH 已提交
75
If the dependent JARs are not automatically retrieved for some reason, you can right-click the project and select '_Maven -> Update Project'_ to force a manual refresh (but this should not be needed, normally). 
T
Tijs Rademakers 已提交
76
In the project, under '_Maven Dependencies_', you should now see the _flowable-engine_ and various other (transitive) dependencies.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

Create a new Java class and add a regular Java main method:

[source,java,linenums]
----
package org.flowable;

public class HolidayRequest {

  public static void main(String[] args) {

  }

}
----

T
Tijs Rademakers 已提交
93 94
The first thing we need to do is to instantiate a *ProcessEngine* instance. This is a thread-safe object that you typically have to instantiate only once in an application. 
A _ProcessEngine_ is created from a *ProcessEngineConfiguration* instance, which allows you to configure and tweak the settings for the process engine. Often, the _ProcessEngineConfiguration_ is created 
D
David B Malkovsky 已提交
95 96
using a configuration XML file, but (as we do here) you can also create it programmatically. The minimum configuration a _ProcessEngineConfiguration_ needs
is a JDBC connection to a database:
97 98 99 100 101

[source,java,linenums]
----
package org.flowable;

102 103 104
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

public class HolidayRequest {

  public static void main(String[] args) {
    ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
      .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1")
      .setJdbcUsername("sa")
      .setJdbcPassword("")
      .setJdbcDriver("org.h2.Driver")
      .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

    ProcessEngine processEngine = cfg.buildProcessEngine();
  }

}
----

P
PHH 已提交
122
In the code above, on line 10, a _standalone_ configuration object is created. The _'standalone'_ here refers to the fact that the engine is created and used completely by itself (and not, for example, 
T
Tijs Rademakers 已提交
123
in a Spring environment, where you'd use the _SpringProcessEngineConfiguration_ class instead). On lines 11 to 14, the JDBC connection parameters to an in-memory H2 database instance are passed. 
P
PHH 已提交
124
Important: note that such a database does not survive a JVM restart. If you want your data to be persistent, you'll need to switch to a persistent database and switch the connection parameters accordingly. 
T
Tijs Rademakers 已提交
125 126
On line 15 we're setting a flag to _true_ to make sure that the database schema is created if it doesn't already exist in the database pointed to by the JDBC parameters. 
Alternatively, Flowable ships with a set of SQL files that can be used to create the database schema with all the tables manually.
127 128 129

The *ProcessEngine* object is then created using this configuration (line 17).

P
PHH 已提交
130
You can now run this. The easiest way in Eclipse is to right-click on the class file and select _Run As -> Java Application_:
131 132 133 134 135 136 137

image::images/getting.started.run.main.png[align="center"]

The application runs without problems, however, no useful information is shown in the console except a message stating that the logging has not been configured properly:

image::images/getting.started.console.logging.png[align="center"]

P
PHH 已提交
138
Flowable uses link:$$http://www.slf4j.org/$$[SLF4J] as its logging framework internally. For this example, we'll use the log4j logger over SLF4j, so add the following dependencies to the pom.xml file:
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

[source,xml,linenums]
----
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.21</version>
</dependency>
----

Log4j needs a properties file for configuration. Add a _log4j.properties_ file to the _src/main/resources_ folder with the following content:

----
log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
----

Rerun the application. You should now see informative logging about the engine booting up and the database schema being created in the database:

image::images/getting.started.console.logging2.png[align="center"]

We've now got a process engine booted up and ready to go. Time to feed it a process!

==== Deploying a process definition

T
Tijs Rademakers 已提交
172
The process we'll build is a very simple holiday request process. The Flowable engine expects processes to be defined in the BPMN 2.0 format, which is an XML standard that is widely accepted in the industry. 
P
PHH 已提交
173 174
In Flowable terminology, we speak about this as a *process definition*. From a _process definition_, many *process instances* can be started. Think of the _process definition_ as the blueprint for many executions 
of the process. In this particular case, the _process definition_ defines the different steps involved in requesting holidays, while one _process instance_ matches the request for a holiday by one particular employee.
175

P
PHH 已提交
176 177
BPMN 2.0 is stored as XML, but it has a visualization part too: it defines in a standard way how each different step type (a human task, an automatic service call, and so on) is represented and how to connect 
these different steps to each other. Through this, the BPMN 2.0 standard allows technical and business people to communicate about business processes in a way that both parties understand.
178 179 180 181 182 183 184

The process definition we'll use is the following:

image::images/getting.started.bpmn.process.png[align="center"]

The process should be quite self-explanatory, but for clarity's sake let's describe the different bits:

T
Tijs Rademakers 已提交
185
* We assume the process is started by providing some information, such as the employee name, the amount of holiday requested and a description. Of course, this could be modeled as a separate first step in the process. 
P
PHH 已提交
186 187
However, by having it as 'input data' for the process, a process instance is only actually created when a real request has been made. In the alternative case, a user could change his mind and cancel before submitting, yet the process instance would now be there. 
In some scenarios this could be valuable information (for example, how many times is a request started, but not finished), depending on the business goal.
188 189 190
* The circle on the left is called a *start event*. It's the starting point of a process instance.
* The first rectangle is a *user task*. This is a step in the process that a human user has to perform. In this case, the manager needs to approve or reject the request.
* Depending on what the manager decides, the *exclusive gateway* (the diamond shape with the cross) will route the process instance to either the approval or the rejection path.
P
PHH 已提交
191 192 193
* If approved, we have to register the request in some external system, which is followed by a user task again for the original employee that notifies them of the decision. 
This could, of course, be replaced by an email.
* If rejected, an email is sent to the employee informing them of this.
194

P
PHH 已提交
195
Typically, such a _process definition_ is modeled with a visual modeling tool, such as the Flowable Designer (Eclipse) or the Flowable Modeler (web application).
196

P
PHH 已提交
197
Here, however, we're going to write the XML directly to familiarize ourselves with BPMN 2.0 and its concepts.
198

P
PHH 已提交
199
The BPMN 2.0 XML corresponding to the diagram above is shown below. Note that this is only the 'process part'. If you'd used a graphical modeling tool, the underlying XML file also contains the 'visualization' part that describes the graphical information, such as the coordinates of the various elements of the process definition (all graphical information is contained in the _BPMNDiagram_ tag in the XML, which is a child element of the _definitions_ tag).
200

P
PHH 已提交
201
Save the following XML in a file named _holiday-request.bpmn20.xml_ in the _src/main/resources_ folder.
202 203 204 205 206 207 208 209 210 211

[source,xml,linenums]
----
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
  xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
  xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
212
  xmlns:flowable="http://flowable.org/bpmn"
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
  typeLanguage="http://www.w3.org/2001/XMLSchema"
  expressionLanguage="http://www.w3.org/1999/XPath"
  targetNamespace="http://www.flowable.org/processdef">

  <process id="holidayRequest" name="Holiday Request" isExecutable="true">

    <startEvent id="startEvent"/>
    <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

    <userTask id="approveTask" name="Approve or reject request"/>
    <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

    <exclusiveGateway id="decision"/>
    <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[
          ${approved}
        ]]>
      </conditionExpression>
    </sequenceFlow>
    <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[
          ${!approved}
        ]]>
      </conditionExpression>
    </sequenceFlow>

T
Tijs Rademakers 已提交
241 242
    <serviceTask id="externalSystemCall" name="Enter holidays in external system" 
        flowable:class="org.flowable.CallExternalSystemDelegate"/>
243 244 245 246 247
    <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

    <userTask id="holidayApprovedTask" name="Holiday approved"/>
    <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

T
Tijs Rademakers 已提交
248 249
    <serviceTask id="sendRejectionMail" name="Send out rejection email" 
        flowable:class="org.flowable.SendRejectionMail"/>
250 251 252 253 254 255 256 257 258 259 260
    <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

    <endEvent id="approveEnd"/>

    <endEvent id="rejectEnd"/>

  </process>

</definitions>
----

P
PHH 已提交
261
Lines 2 to 11 look a bit daunting, but it's the same as you'll see in almost every process definition. It's kind of _boilerplate_ stuff that's needed to be fully compatible with the BPMN 2.0 standard specification.
262

P
PHH 已提交
263
Every step (in BPMN 2.0 terminology, *'activity'*) has an _id_ attribute that gives it a unique identifier in the XML file. All _activities_ can have an optional name too, which increases the readability of the visual diagram, of course.
264

P
PHH 已提交
265
The _activities_ are connected by a *sequence flow*, which is a directed arrow in the visual diagram. When executing a process instance, the execution will flow from the _start event_ to the next _activity_, following the _sequence flow_.
266

P
PHH 已提交
267
The _sequence flows_ leaving the _exclusive gateway_ (the diamond shape with the X) are clearly special: both have a _condition_ defined in the form of an _expression_ (see line 25 and 32). When the process instance execution reaches this _gateway_, the _conditions_ are evaluated and the first that resolves to _true_ is taken. This is what the _exclusive_ stands for here: only one is selected. Other types of gateways are, of course, possible if different routing behavior is needed.
268 269 270 271 272

The condition written here as an _expression_ is of the form _${approved}_, which is a shorthand for _${approved == true}_. The variable 'approved' is called a *process variable*. A _process variable_ is a persistent bit of data that is stored together with the process instance and can be used during the lifetime of the process instance. In this case, it does mean that we will have to set this _process variable_ at a certain point (when the manager user task is submitted or, in Flowable terminology, _completed_) in the process instance, as it's not data that is available when the process instance starts.

Now we have the process BPMN 2.0 XML file, we next need to *'deploy'* it to the engine. _Deploying_ a process definition means that:

P
PHH 已提交
273 274
* the process engine will store the XML file in the database, so it can be retrieved whenever needed
* the process definition is parsed to an internal, executable object model, so that _process instances_ can be started from it.
275

P
PHH 已提交
276
To _deploy_ a process definition to the Flowable engine, the _RepositoryService_ is used, which can be retrieved from the _ProcessEngine_ object. Using the _RepositoryService_, a new _Deployment_ is created by passing the location of the XML file and calling the _deploy()_ method to actually execute it:
277 278 279 280 281 282 283 284 285

[source,java,linenums]
----
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("holiday-request.bpmn20.xml")
  .deploy();
----

P
PHH 已提交
286
We can now verify that the process definition is known to the engine (and learn a bit about the API) by querying it through the API. This is done by creating a new _ProcessDefinitionQuery_ object through the _RepositoryService_.
287 288 289 290 291 292 293 294 295 296 297

[source,java,linenums]
----
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
  .deploymentId(deployment.getId())
  .singleResult();
System.out.println("Found process definition : " + processDefinition.getName());
----

==== Starting a process instance

D
David B Malkovsky 已提交
298
We now have the process definition _deployed_ to the process engine, so _process instances_ can be started using this _process definition_ as a 'blueprint'.
299

P
PHH 已提交
300
To start the process instance, we need to provide some initial _process variables_. Typically, you'll get these through a form that is presented to the user or through a REST API when a process is triggered by something automatic. In this example, we'll keep it simple and use the java.util.Scanner class to simply input some data on the command line:
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

[source,java,linenums]
----
Scanner scanner= new Scanner(System.in);

System.out.println("Who are you?");
String employee = scanner.nextLine();

System.out.println("How many holidays do you want to request?");
Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

System.out.println("Why do you need them?");
String description = scanner.nextLine();
----

P
PHH 已提交
316
Next, we can start a _process instance_ through the _RuntimeService_. The collected data is passed as a _java.util.Map_ instance, where the key is the identifier that will be used to retrieve the variables later on. The process instance is started using a _key_. This _key_ matches the _id_ attribute that is set in the BPMN 2.0 XML file, in this case _holidayRequest_.
317

P
PHH 已提交
318
(NOTE: there are many ways you'll learn later on to start a process instance, beyond using a key)
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

[source,xml]
----
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
----

[source,java,linenums]
----
RuntimeService runtimeService = processEngine.getRuntimeService();

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", employee);
variables.put("nrOfHolidays", nrOfHolidays);
variables.put("description", description);
ProcessInstance processInstance =
  runtimeService.startProcessInstanceByKey("holidayRequest", variables);
----

P
PHH 已提交
337
When the process instance is started, an *execution* is created and put in the start event. From there, this _execution_ follows the sequence flow to the user task for the manager approval and executes the user task behavior. This behavior will create a task in the database that can be found using queries later on. A user task is a _wait state_ and the engine will stop executing anything further, returning the API call.
338 339 340

==== Sidetrack: transactionality

P
PHH 已提交
341
In Flowable, database transactions play a crucial role to guarantee data consistency and solve concurrency problems. When you make a Flowable API call, by default, everything is synchronous and part of the same transaction. Meaning, when the method call returns, a transaction will be started and committed.
342 343 344

When a process instance is started, there will be *one database transaction* from the start of the process instance to the next _wait state_. In this example, this is the first user task. When the engine reaches this user task, the state is persisted to the database and the transaction is committed and the API call returns.

P
PHH 已提交
345
In Flowable, when continuing a process instance, there will always be one database transaction going from the previous _wait state_ to the next _wait state_. Once persisted, the data can be in the database for a long time, even years if it has to be, until an API call is executed that takes the process instance further. Note that no computing or memory resources are consumed when the process instance is in such a wait state, waiting for the next API call.
J
Joram Barrez 已提交
346

347
In the example here, when the first user task is completed, one database transaction will be used to go from the user task through the exclusive gateway (the automatic logic) until the second user task. Or straight to the end with the other path.
J
Joram Barrez 已提交
348 349


350
==== Querying and completing tasks
J
Joram Barrez 已提交
351

P
PHH 已提交
352
In a more realistic application, there will be a user interface where the employees and the managers can log in and see their task lists. With these, they can inspect the process instance data that is stored as _process variables_ and decide what they want to do with the task. In this example, we will mimic task lists by executing the API calls that normally would be behind a service call that drives a UI.
J
Joram Barrez 已提交
353

354
We haven't yet configured the assignment for the user tasks. We want the first task to go the the 'managers' group and the second user task to be assigned to the original requester of the holiday. To do this, add the _candidateGroups_ attribute to the first task:
J
Joram Barrez 已提交
355

356 357
[source,xml]
----
358
<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
359 360 361 362 363 364
----

and the _assignee_ attribute to the second task as shown below. Note that we're not using a static value like the 'managers' value above, but a dynamic assignment based on a process variable that we've passed when the process instance was started:

[source,xml]
----
365
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
366
----
J
Joram Barrez 已提交
367

P
PHH 已提交
368
To get the actual task list, we create a _TaskQuery_ through the _TaskService_ and we configure the query to only return the tasks for the 'managers' group:
J
Joram Barrez 已提交
369

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
[source,java,linenums]
----
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
System.out.println("You have " + tasks.size() + " tasks:");
for (int i=0; i<tasks.size(); i++) {
  System.out.println((i+1) + ") " + tasks.get(i).getName());
}
----

Using the task identifier, we can now get the specific process instance variables and show on the screen the actual request:

[source,java,linenums]
----
System.out.println("Which task would you like to complete?");
int taskIndex = Integer.valueOf(scanner.nextLine());
Task task = tasks.get(taskIndex - 1);
Map<String, Object> processVariables = taskService.getVariables(task.getId());
T
Tijs Rademakers 已提交
388 389
System.out.println(processVariables.get("employee") + " wants " + 
    processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");
390
----
J
Joram Barrez 已提交
391

392
Which, if you run this, should look something like this:
J
Joram Barrez 已提交
393

394
image::images/getting.started.console.logging3.png[align="center"]
J
Joram Barrez 已提交
395

396 397
The manager can now *complete the task*. In reality, this often means that a form is submitted by the user. The data from the form is then passed as _process
 variables_. Here, we'll mimic this by passing a map with the 'approved' variable (the name is important, as it's used later on in the conditions of the sequence flow!) when the task is completed:
J
Joram Barrez 已提交
398

399
[source,java,linenums]
J
Joram Barrez 已提交
400
----
401 402 403 404
boolean approved = scanner.nextLine().toLowerCase().equals("y");
variables = new HashMap<String, Object>();
variables.put("approved", approved);
taskService.complete(task.getId(), variables);
J
Joram Barrez 已提交
405 406
----

407 408 409 410 411
The task is now completed and one of the two paths leaving the exclusive gateway is selected based on the 'approved' process variable.

[[getting.started.delegate]]
==== Writing a JavaDelegate

P
PHH 已提交
412
There is a last piece of the puzzle still missing: we haven't implemented the automatic logic that will get executed when the request is approved. In the BPMN 2.0 XML this is a *service task* and it looked above like:
413 414 415

[source,xml]
----
T
Tijs Rademakers 已提交
416 417
<serviceTask id="externalSystemCall" name="Enter holidays in external system" 
    flowable:class="org.flowable.CallExternalSystemDelegate"/>
418 419
----

P
PHH 已提交
420
In reality, this logic could be anything, ranging from calling a service with HTTP REST, to executing some legacy code calls to a system the organization has
D
David B Malkovsky 已提交
421
been using for decades. We won't implement the actual logic here but simply log the _processing_.
422

423
Create a new class (_File -> New -> Class_ in Eclipse), fill in _org.flowable_ as package name and _CallExternalSystemDelegate_ as class name. Make that class implement the _org.flowable.engine.delegate.JavaDelegate_ interface and implement the _execute_ method:
424 425 426 427 428

[source,java,linenums]
----
package org.flowable;

429 430
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
431 432 433

public class CallExternalSystemDelegate implements JavaDelegate {

T
Tijs Rademakers 已提交
434 435 436 437
    public void execute(DelegateExecution execution) {
        System.out.println("Calling the external system for employee "
            + execution.getVariable("employee"));
    }
438 439 440 441

}
----

P
PHH 已提交
442
When the _execution_ arrives at the _service task_, the class that is referenced in the BPMN 2.0 XML is instantiated and called.
443 444 445 446 447 448 449 450

When running the example now, the logging message is shown, demonstrating the custom logic is indeed executed:

image::images/getting.started.console.logging4.png[align="center"]


==== Working with historical data

P
PHH 已提交
451
One of the many reasons for choosing to use a process engine like Flowable is because it automatically stores *audit data* or *historical data* for all the process instances. This data allows the creation of rich reports that give insights into how the organization works, where the bottlenecks are, etc.
452 453 454 455

For example, suppose we want to show the duration of the process instance that we've been executing so far. To do this, we get the _HistoryService_  from the _ProcessEngine_ and create a query for _historical activities_. In the snippet below you can see we add some additional filtering:

* only the activities for one particular process instance
P
PHH 已提交
456
* only the activities that have finished
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

The results are also sorted by end time, meaning that we'll get them in execution order.

[source,java,linenums]
----
HistoryService historyService = processEngine.getHistoryService();
List<HistoricActivityInstance> activities =
  historyService.createHistoricActivityInstanceQuery()
   .processInstanceId(processInstance.getId())
   .finished()
   .orderByHistoricActivityInstanceEndTime().asc()
   .list();

for (HistoricActivityInstance activity : activities) {
  System.out.println(activity.getActivityId() + " took "
    + activity.getDurationInMillis() + " milliseconds");
}
----

Running the example again, we now see something like this in the console:

----
startEvent took 1 milliseconds
approveTask took 2638 milliseconds
decision took 3 milliseconds
externalSystemCall took 1 milliseconds
----

==== Conclusion

P
PHH 已提交
487
This tutorial introduced various Flowable and BPMN 2.0 concepts and terminology, while also demonstrating how to use the Flowable API programmatically.
488 489

Of course, this is just the start of the journey. The following sections will dive more deeply into the many options and features that the Flowable engine supports. Other sections go into the various ways the Flowable engine can be set up and used, and describe in detail all the BPMN 2.0 constructs that are possible.
J
Joram Barrez 已提交
490

491 492
[[getting.started.rest]]
=== Getting started with the Flowable REST API
J
Joram Barrez 已提交
493

494
This section shows the same example as the <<getting.started.command.line, previous section>>: deploying a process definition, starting a process instance, getting a task list and completing a task. If you haven't read that section, it might be good to skim through it to get an idea of what is done there.
J
Joram Barrez 已提交
495

P
PHH 已提交
496
This time, the Flowable REST API is used rather than the Java API. You'll soon notice that the REST API closely matches the Java API, and knowing one automatically means that you can find your way around the other.
J
Joram Barrez 已提交
497

P
PHH 已提交
498
To get a full, detailed overview of the Flowable REST API, check out the <<restApiChapter, REST API chapter>>.
J
Joram Barrez 已提交
499

500 501
==== Setting up the REST application

P
PHH 已提交
502
When you download the .zip file from the flowable.org website, the REST application can be found in the _wars_ folder. You'll need a servlet container, such as link:$$http://tomcat.apache.org//$$[Tomcat], link:$$http://www.eclipse.org/jetty//$$[Jetty], and so on, to run the WAR file.
503 504 505 506 507 508 509 510

When using Tomcat the steps are as follows:

* Download and unzip the latest and greatest Tomcat zip file (choose the 'Core' distribution from the Tomcat website).
* Copy the flowable-rest.war file from the _wars_ folder of the unzipped Flowable distribution to the _webapps_ folder of the unzipped Tomcat folder.
* On the command line, go to the _bin_ folder of the Tomcat folder.
* Execute '_./catalina run_' to boot up the Tomcat server.

P
PHH 已提交
511
During the server boot up, you'll notice some Flowable logging messages passing by. At the end, a message like '_INFO [main] org.apache.catalina.startup.Catalina.start Server startup in xyz ms_' indicates that the server is ready to receive requests. Note that by default an in-memory H2 database instance is used, which means that data won't survive a server restart.
512

513
In the following sections, we'll use cURL to demonstrate the various REST calls. All REST calls are by default protected with _basic authentication_. The user 'rest-admin' with password 'test' is used in all calls.
514 515 516 517

After bootup, verify the application is running correctly by executing

----
518
curl --user rest-admin:test http://localhost:8080/flowable-rest/service/management/engine
519 520 521 522 523 524 525 526 527
----

If you get back a proper json response, the REST API is up and running.

==== Deploying a process definition

The first step is to deploy a process definition. With the REST API, this is done by uploading a .bpmn20.xml file (or .zip file for multiple process definitions) as 'multipart/formdata':

----
528
curl --user rest-admin:test -F "file=@holiday-request.bpmn20.xml" http://localhost:8080/flowable-rest/service/repository/deployments
529 530 531 532 533
----

To verify that the process definition is deployed correctly, the list of process definitions can be requested:

----
534
curl --user rest-admin:test http://localhost:8080/flowable-rest/service/repository/process-definitions
535 536 537 538 539 540 541
----

which returns a list of all process definitions currently deployed to the engine.


==== Start a process instance

P
PHH 已提交
542
Starting a process instance through the REST API is similar to doing the same through the Java API: a _key_ is provided to identify the process definition to use along with a map of initial process variables:
543 544

----
545
curl --user rest-admin:test -H "Content-Type: application/json" -X POST -d '{ "processDefinitionKey":"holidayRequest", "variables": [ { "name":"employee", "value": "John Doe" }, { "name":"nrOfHolidays", "value": 7 }]}' http://localhost:8080/flowable-rest/service/runtime/process-instances
546 547 548 549 550 551 552 553 554 555
----

which returns something like

----
{"id":"43","url":"http://localhost:8080/flowable-rest/service/runtime/process-instances/43","businessKey":null,"suspended":false,"ended":false,"processDefinitionId":"holidayRequest:1:42","processDefinitionUrl":"http://localhost:8080/flowable-rest/service/repository/process-definitions/holidayRequest:1:42","activityId":null,"variables":[],"tenantId":"","completed":false}
----

==== Task list and completing a task

P
PHH 已提交
556
When the process instance is started, the first task is assigned to the 'managers' group. To get all tasks for this group, a task query can be done through the REST API:
557 558

----
559
curl --user rest-admin:test -H "Content-Type: application/json" -X POST -d '{ "candidateGroup" : "managers" }' http://localhost:8080/flowable-rest/service/query/tasks
560 561 562 563 564 565 566
----

which returns a list of all tasks for the 'managers' group

Such a task can now be completed using:

----
567
curl --user rest-admin:test -H "Content-Type: application/json" -X POST -d '{ "action" : "complete", "variables" : [ { "name" : "approved", "value" : true} ]  }' http://localhost:8080/flowable-rest/service/runtime/tasks/25
568 569 570 571 572 573 574
----

However, you most likely will get an error like:

----
{"message":"Internal server error","exception":"couldn't instantiate class org.flowable.CallExternalSystemDelegate"}
----
J
Joram Barrez 已提交
575

P
PHH 已提交
576
This means that the engine couldn't find the _CallExternalSystemDelegate_ class that is referenced in the service task. To solve this, the class needs to be put on the classpath of the application (which will require a restart). Create the class as described in <<getting.started.delegate, this section>>, package it up as a JAR and put it in the _WEB-INF/lib_ folder of the flowable-rest folder under the _webapps_ folder of Tomcat.