/
Chapter-ExceptionMgmt.xml
870 lines (727 loc) · 42.2 KB
/
Chapter-ExceptionMgmt.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
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
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="ch.exception-mgmt"
version="5.0"
xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd http://www.w3.org/1999/xlink http://www.docbook.org/xml/5.0/xsd/xlink.xsd"
xml:base="../" xmlns="http://docbook.org/ns/docbook" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:ns="http://docbook.org/ns/docbook">
<title id="ch.exception-mgmt.title">Exception Management</title>
<section>
<title>Overview</title>
<para>This chapter will describe how to deal with unexpected behavior in your business processes
using both BPMN2 and technical mechanisms.</para>
<para>The first section (Introduction) will define and explain the types of exceptions that can happen or
be used in a business process (Business Exceptions and Technical Exceptions).</para>
<para>The next section will explain Technical Exceptions: we'll go through an example that uses both BPMN2 and
<code>WorkItemHandler</code> implementations in order to isolate and handle exceptions caused
by a technical component. We will also explain how to modify the example to suit other use
cases.</para>
</section>
<section>
<title>Introduction</title>
<para>What happens to a business process when something unexpected happens during the process? Most
of the time, when you create and design a new process definition, you'll begin by describing the
<emphasis>normative</emphasis> or desirable behaviour. However, a process definition that only
describes all of the normal tasks and their execution order is incomplete.</para>
<para>The next step is to think about what might go <emphasis>wrong</emphasis> when the business
process is run. What would happen if any of the human or technical actors in the process do
<emphasis>not</emphasis> respond in unexpexected ways? Will any of the technical systems that the
process interacts with return unexpected results -- or not return any results at all?</para>
<para>Deviations from the normative or "happy" flow of a business process are called
<emphasis>exceptions</emphasis>. In some cases, exceptions might not be that unusual, such as
trying to debit an empty bank account. However, some processes might contain many complex
situations involving exceptions, all of which must be handled correctly.</para>
<note>
<para>The rest of chapter assumes that you know how to create custom <code><task></code>
nodes and how to implement and register <code>WorkItemHandler</code> implementations. More
information about these topics can be found in the <link linkend='ch.domain-specific-processes'
endterm="ch.domain-specific-processes.title"/> chapter.</para>
</note>
</section>
<section>
<title>Business Exceptions</title>
<para>Business Exceptions are exceptions that are designed and managed in the BPMN2 specification of
a business process. In other words, Business Exceptions are exceptions which happen at the process
or workflow level, and are not related to the technical components.</para>
<para>Many of the elements in BPMN2 related to Business Exceptions are related to
<emphasis>Compensation</emphasis> and <emphasis>Business Transactions</emphasis>. Compensation, in
particular, is complexer than many other parts of the BPMN2 specfication.</para>
<para>Full support for <emphasis>compensation</emphasis> and <emphasis>business
transactions</emphasis> is expected with the release of jBPM 6.1 or 6.2. Once that has been
implemented, this section will contain more information about using those BPMN2 features with
jBPM.</para>
<section>
<title>Business Exceptions elements in BPMN2</title>
<para>The following attempts to briefly describe Compensation and Business Transaction related
elements in BPMN2. For more complete information about these elements and their uses, see
the BPMN2 specification, Bruce Silver's book <filename>BPMN Method and Style</filename> or any of
the other available books about the use of BPMN2.</para>
<table frame='all'>
<title>BPMN2 Exception Handling Elements</title>
<tgroup cols="2" align='left' rowsep='1' colsep='1'>
<thead>
<row>
<entry>BPMN2 Element types</entry>
<entry>Description</entry>
</row>
</thead><tbody valign='top'>
<row>
<entry>Errors and Error Events</entry>
<entry><para>Error Events can be used to signal when a process has encountered an unexpected
situation: signalling an error is often called <emphasis>throwing</emphasis> an error.</para>
<para>Boundary Error Events in a different part of the process can then be used to
<emphasis>catch</emphasis> the error and initiate a sequence of activities to handle the
exception.</para>
<para>Errors themselves can be extended with extra information that is passed from the
throwing to catching event. This is done with the use of an Item Definition.</para>
</entry>
</row><row>
<entry>Business Transactions</entry>
<entry><para>A Business Transaction in BPMN2 is a subprocess which can be used with
<emphasis>compensation</emphasis>. Grouping activities in a Business Transaction lets the
process designer easily add exception handling to specific activities in the
subprocess.</para>
<para>Using a Business Transaction guarantees that all activities in the
transaction will have completed successfully if the Business Transaction completes
successfully.</para>
<para>When a Business Transaction is interrupted or otherwise not completed successfully,
there is a guarantee that all activities in the Business Transaction that have been initiated
will be compensated if compensating activities are defined for those activities.
</para></entry>
</row><row>
<entry>Compensation</entry>
<entry><para>Exception handling activities <emphasis>associated</emphasis> with the normal
activies in a Business Transaction are triggered by <emphasis>Compensation
Events</emphasis>.</para>
<para>Compensation Events may only be used within Business Transactions.</para>
<para>There are 3 types of compensation events: Intermediate (a.k.a. Boundary) (catch) events,
Start (catch) events, and Intermediate or End (throw) events.</para>
<para>Compensation Boundary (catch) events are attached to activites (e.g. tasks) that could
cause an exception. They may only be attached to activites inside a Business Transaction.
If a Business Transaction fails, possibly because of the failure of one of the activities
inside it, then the activities associated with Boundary (catch) events will be triggered.
Only <emphasis>one</emphasis> activity or node may be associated with a Compensation Boundary
Event!</para>
<para>Start (catch) events are used when defining an
<emphasis>Compensation Event SubProcess</emphasis> inside a Business Transaction. Compensation
Event SubProcesses are often used when a subprocess is needed to compensate for the Business
Transaction as a whole (as opposed to defining compensating activities per node in the
Business Transaction. This subprocess is triggered when a Business Transaction fails, just
like activities attached to Compensation Boundary (catch) events.</para>
<para>Compensation Intermediate and End events are used within Business Transactions
in order to throw Compensation Events. Often, logic in the Business Transaction subprocesses
will determine whether or not the Business Transaction has succeeded or failed. If the
subprocess has failed, then the process will proceed to an Intermediate or End Compensation
Event in order to trigger compensation for the Business Transaction subprocess.</para>
</entry>
</row><row>
<entry>Cancel Events</entry>
<entry>
<para>Cancel Events trigger <emphasis>cancellation</emphasis> of a Business
Transaction and can thus only be used with a Business Transaction.</para>
<para>When a Cancel Event is thrown, this indicates that the Business Transaction should be
cancelled. Entities involved in the Business Transaction are then informed (via a
<emphasis>TransactionProtocol Cancel Message</emphasis>) that the Business Transaction has
been cancelled.</para>
<para><emphasis>Cancellation</emphasis> of a Business Transaction implicitly triggers
<emphasis>compensation</emphasis> of the Business Transaction.</para>
<para>See the sources mentioned above for the differences between Error Events (abortion
of a process), Cancel Events (cancellation) and Compensate Events (compensation).</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
<!-- // mriet: replace the above section with the following section once compensation is implemented
<para>BPMN2 contains a number of constructs to model exceptions in business processes. There are
several advantages to doing exception handling at the business process level (as opposed to
handling it with code):
<itemizedlist>
<listitem><emphasis>Transparency</emphasis><itemizedlist>
<listitem>Being able to quickly see what happens in exceptional situations means that
the results and performance of a process is more easily monitored and measured.</listitem>
<listitem>It also increases how easily a process can be implemented as well as how
maintainable a process definition is.</listitem></itemizedlist>
</listitem>
<listitem><emphasis>Business Logic Isolation</emphasis><itemizedlist>
<listitem>Again, the idea behind using a business process is to isolate the business logic
from the technical code. This simplifies the complexity of the system and increases how
quickly you can create new business processes and change existing ones.</listitem>
<listitem>Implementing exception handling at a technical level often takes more time because
it's often complexer and specific to a system.</listitem></itemizedlist>
</listitem>
</itemizedlist>
</para>
-->
</section>
</section>
<section>
<title>Technical Exceptions</title>
<para>Technical exceptions happen when a technical component of a business process acts in an
unexpected way. When using Java based systems, this often results in a literal Java Exception being
thrown by the system.</para>
<para>Technical components used in a process can fail in a way that can not be described using
BPMN2. In this case, it's important to handle these exceptions in expected ways.</para>
<para>The following types of code might throw exceptions:
<itemizedlist>
<listitem>Any code that is present in the process definition itself</listitem>
<listitem>Any code that is executed during a process and is not part of jBPM</listitem>
<listitem>Any code that interacts with a technical component outside of the process engine</listitem>
</itemizedlist>
However, those are somewhat abstract defintions. We can narrow down the places at which an
exception might be thrown. Technical exceptions can occur at the following points:
<orderedlist>
<listitem>Code present in <code><scriptTask></code> nodes or in the
jbpm-specific <code><onEntry></code> and <code><onExit></code> elements</listitem>
<listitem>Code executed in <code>WorkItemHandlers</code> associated with
<code><task></code> and task-type nodes</listitem>
</orderedlist>
It is <emphasis>much easier</emphasis> to ensure correct exception handling for
<code><task></code> and other task-type nodes that use <code>WorkItemHandler</code>
implementations, than for code executed directly in a <code><scriptTask></code>.</para>
<para>Exceptions thrown by <code><scriptTask></code> can cause the process
to fail in an unrecoverable fashion. While there are certain things that you can do to contain the
damage, a process that has failed in this way can not be restarted or otherwise recovered. This
also applies for other nodes in a process definition that contain script code in the node
definition, such as the <code><onEntry></code> and <code><onExit></code>
elements.</para>
<para>When jBPM engine does throw an exception generated by the code in a <code><scriptTask></code>
the exception thrown is a special Java exception called the <code>WorkflowRuntimeException</code> that
contains information about the process.</para>
<warning>
<para>Again, exceptions generated by a <code><scriptTask></code> node (and other nodes
containing script code) will leave the
process <emphasis>unrecoverable</emphasis>. In fact, often, the code that starts the process
itself will end up throwing the exception generated by the business process, without returning
a reference to the process instance.<literallayout>
</literallayout>For this reason, it's important to limit the scope of the code in these nodes to operations
dealing with process variables. Using a <code><scriptTask></code> to interact with a different technical component, such as a
database or web service has <emphasis>significant risks</emphasis> because any exceptions thrown
will corrupt or abort the process.<literallayout>
</literallayout><code><task></code> nodes, <code><serviceTask></code> nodes and the rest of
the <code>task</code>-type nodes are explictly meant for interacting with other systems -- not
<code><scriptTask></code> nodes! Use <code><task></code>-type nodes to interact with
other technical components.</para>
</warning>
<section>
<title>Handling exceptions in <code>WorkItemHandler</code> instances</title>
<para><code>WorkItemHandler</code> classes are used when your process interacts with other technical
systems. For an introduction to them and how to use them in processes, please see the <link
linkend='ch.domain-specific-processes' endterm="ch.domain-specific-processes.title"/> chapter.</para>
<para>While you can build exception handling into your own <code>WorkItemhandler</code>
implementations, there are also two <quote>handler decorator</quote> classes that you can use to
<emphasis>wrap</emphasis> a <code>WorkItemhandler</code> implementation.</para>
<para>These two wrapper classes include logic that is executed when an exception is thrown during the
execution (or abortion) of a work item.</para>
<table frame='all'>
<title>Exception Handling <code>WorkItemHandler</code> wrapper classes</title>
<tgroup cols="2" align='left' rowsep='2' colsep='1'>
<thead valign='top'>
<row>
<entry>Decorator classes in the <code>org.jbpm.bpmn2.handler</code> package</entry>
<entry>Description</entry>
</row>
</thead><tbody valign='top'>
<row>
<entry><code>SignallingTaskHandlerDecorator</code></entry>
<entry>This class wraps an existing <code>WorkItemHandler</code> implementation. When the
<code>.executeWorkItem(...)</code> or <code>.abortWorkItem(...)</code> methods of the original
<code>WorkItemHandler</code> instance throw an exception, the
<code>SignallingTaskHandlerDecorator</code> will catch the exception and signal the process instance
using a configurable event type. The exception thrown will be passed as part of the event. This
functionality can be used to signal an <emphasis>Event SubProcess</emphasis> defined in the process
definition.</entry>
</row><row>
<entry><code>LoggingTaskHandlerDecorator</code></entry>
<entry>This class reacts to all exceptions thrown by the <code>.executeWorkItem(...)</code>
or <code>.abortWorkItem(...)</code> <code>WorkItemHandler</code> methods by logging the errors. It
also saves any exceptions thrown so to an internal list so that they can be retrieved later for
inspection or further logging. Lastly, the content and format of the message logged upon an
exception are configurable.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>While the two classes described above should cover most cases involving exception handling, a
Java developer with some experience with jBPM should be able to create a
<code>WorkItemHandler</code> that executes custom code upon an exception.</para>
<para>If you do decide to write a custom <code>WorkItemHandler</code> that includes exception
handling logic, keep the following checklist in mind:
<orderedlist>
<listitem>Are you catching all possible exceptions that you want to (and no more, or
less)?</listitem>
<listitem>Are you making sure to either complete or abort the work item after an exception has
been caught? If not, are there mechanisms to retry the process later? Or are incomplete process
instances acceptable?</listitem>
<listitem>What other actions should be taken when an exception is caught? Do you want to simply
log the exception, or is it also important to interact with other technical systems? Do you want
to trigger a (BPMN2) subprocess that will handle the exception?</listitem>
</orderedlist>
</para>
<important>
<para>When you use the <code>WorkItemManager</code> to signal that the work item has been completed
or aborted, make sure to do that <emphasis>after you've sent any signals</emphasis> to the process
instance. Depending on how you've defined your process, calling <code>WorkItemManager.completeWorkItem(...)</code> or
<code>WorkItemManager.abortWorkItem(...)</code> will trigger the completion of the process instance.
This is because the these methods trigger the jBPM process engine to continue the process flow.</para>
</important>
<para>In the next section, we'll describe an example that uses the
<code>SignallingTaskHandlerDecorator</code> to signal an <emphasis>event subprocess</emphasis> when
a work item handler throws an exception.</para>
</section>
</section>
<section>
<title>Technical Exception Examples</title>
<section>
<title>Example: service task handlers </title>
<para>We'll go through one example in this section, and then look quickly at how you can change
it to get the behavior you want. The example involves an
<code><error></code> event that's caught by an <emphasis>(Error) Event SubProcess</emphasis>.
</para>
<para>When an <emphasis>Error Event</emphasis> is thrown, the containing process will be interrupted.
This means that after the process flow attached to the error event has executed, the following
will happen:
<orderedlist>
<listitem>process execution will stop, and no other parts of the process will execute</listitem>
<listitem>the process instance will end up in an aborted state (instead of completed)</listitem>
</orderedlist>
</para>
<para>The example we'll go through contains an <code><error></code>, but at the end of the
secion, we'll show how you can change the process to use a <code><signal></code>
instead.</para>
<tip>
<para>The code and BPMN2 process definition shown in the next section are available in the
<filename>jbpm-examples</filename> module. See the
<code>org.jbpm.examples.exceptions.ExceptionHandlingErrorExample</code> class for the java
code. The BPMN2 process definition is available in the
<filename>exceptions/ExceptionHandlingWithError.bpmn2</filename> file in the
<filename>src/main/resources</filename> directory of the <filename>jbpm-examples</filename>
module.</para>
</tip>
<section>
<title>BPMN2 configuration</title>
<para>Let's look at the BPMN2 process definition first. Besides the definition of the process, the
BPMN2 elements defined before the actual process definition are also important. Here's an image of
the BPMN2 process that we'll be using in the example:</para>
<figure>
<mediaobject>
<imageobject>
<imagedata align="center" width="100%" fileref="images/Chapter-ExceptionMgmt/exception-flow.png" format="PNG" role="" />
</imageobject>
</mediaobject>
</figure>
<para>The BPMN2 process fragment below is part of the process shown above, and contains some notes
on the different BPMN2 elements.</para>
<note>If you're viewing this on a web browser, you may need to widen your browser window in order to see
the "callout" or note numbers on the righthand side of the code.</note>
<programlistingco>
<areaspec>
<areaset id="err.bpmn2.item.string">
<area id="err.bpmn2.item.string.1" coords='1' />
<area id="err.bpmn2.item.string.2" coords='23' />
</areaset>
<areaset id="err.bpmn2.service.throw">
<area id="err.bpmn2.service.throw.1" coords='2' />
<area id="err.bpmn2.service.throw.2" coords='6' />
</areaset>
<areaset id="err.bpmn2.error">
<area id="err.bpmn2.error.1" coords='10' />
<area id="err.bpmn2.error.2" coords='39' />
</areaset>
<areaset id="err.bpmn2.service.catch">
<area id="err.bpmn2.service.catch.1" coords='12' />
<area id="err.bpmn2.service.catch.2" coords='13' />
<area id="err.bpmn2.service.catch.3" coords='17' />
<area id="err.bpmn2.service.catch.4" coords='24' />
<area id="err.bpmn2.service.catch.5" coords='37' />
</areaset>
</areaspec>
<programlisting><![CDATA[ <itemDefinition id="_stringItem" structureRef="java.lang.String"/>
<message id="_message" itemRef="_stringItem"/>
<interface id="_serviceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
<operation id="_serviceOperation" name="throwException">
<inMessageRef>_message</inMessageRef>
</operation>
</interface>
<error id="_exception" errorCode="code" structureRef="_exceptionItem"/>
<itemDefinition id="_exceptionItem" structureRef="org.kie.api.runtime.process.WorkItem"/>
<message id="_exceptionMessage" itemRef="_exceptionItem"/>
<interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
<operation id="_handlingServiceOperation" name="handleException">
<inMessageRef>_exceptionMessage</inMessageRef>
</operation>
</interface>
<process id="ProcessWithExceptionHandlingError" name="Service Process" isExecutable="true" processType="Private">
<!-- properties -->
<property id="serviceInputItem" itemSubjectRef="_stringItem"/>
<property id="exceptionInputItem" itemSubjectRef="_exceptionItem"/>
<!-- main process -->
<startEvent id="_1" name="Start" />
<serviceTask id="_2" name="Throw Exception" implementation="Other" operationRef="_serviceOperation">
<!-- rest of the serviceTask element and process definition... -->
<subProcess id="_X" name="Exception Handler" triggeredByEvent="true" >
<startEvent id="_X-1" name="subStart">
<dataOutput id="_X-1_Output" name="event"/>
<dataOutputAssociation>
<sourceRef>_X-1_Output</sourceRef>
<targetRef>exceptionInputItem</targetRef>
</dataOutputAssociation>
<errorEventDefinition id="_X-1_ED_1" errorRef="_exception" />
</startEvent>
<!-- rest of the subprocess definition... -->
</subProcess>
</process>]]>
</programlisting>
<calloutlist>
<callout arearefs="err.bpmn2.item.string">
<para>This <code><itemDefinition></code> element defines a data structure that we then
use in the <code>serviceInputItem</code> property in the process. </para>
</callout>
<callout arearefs="err.bpmn2.service.throw">
<para>This <code><message></code> element (1rst reference) defines a
<emphasis>message</emphasis> that has a String as its content (as defined by the
<code><itemDefintion></code> element on line above). The
<code><interface></code> element below it refers to it (2nd reference) in order to
define what type of content the service (defined by the <code><interface></code>)
expects.</para>
</callout>
<callout arearefs="err.bpmn2.error">
<para>This <code><error></code> element (1rst reference) defines an error for use later
in the process: an <emphasis>Event SubProcess</emphasis> is defined that is triggered by this
<emphasis>error</emphasis> (2nd reference). The content of the error is defined by the
<code><itemDefintion></code> element defined below the <code><error></code>
element.</para>
</callout>
<callout arearefs="err.bpmn2.service.catch">
<para>This <code><itemDefintion></code> element (1rst reference) defines an item that
contains a <code>WorkItem</code> instance. The <code><message></code> element (2nd
reference) then defines a <emphasis>message</emphasis> that uses this <emphasis>item
definition</emphasis> to define its content. The <code><interface></code> element below
that refers to the <code><message></code> definition (3rd reference) in order to define
the type of content that the service expects.</para>
<para>In the process itself, a <code><property></code> element (4th reference) is defined
as having the content defined by the initial <code><itemDefintion></code>. This is
helpful because it means that the <emphasis>Event SubProcess</emphasis> can then store the
<emphasis>error</emphasis> it receives in that property (5th reference).</para>
</callout>
</calloutlist>
</programlistingco>
<caution>
<para>When you're using a <code><serviceTask></code> to call a Java class, make sure to double
check the class name in your BPMN2 definition! A small typo there can cost you time later when
you're trying to figure out what went wrong.</para>
</caution>
</section>
<section>
<title><code>SignallingTaskHandlerDecorator</code> and <code>WorkItemHandler</code> configuration</title>
<para>Now that BPMN2 process definition is (hopefully) a little clearer, we can look at how to set
up jBPM to take advantage of the above BPMN2.</para>
<para>In the (BPMN2) process definition above, we define two different <code><serviceTask></code>
activities. The <code>org.jbpm.bpmn2.handler.ServiceTaskHandler</code> class is the default task
handler class used for <code><serviceTask></code> tasks. If you don't specify a
<code>WorkItemHandler</code> implementation for a <code><serviceTask></code>, the
<code>ServiceTaskHandler</code> class will be used.</para>
<para>In the code below, you'll see that we actually wrap or decorate the
<code>ServiceTaskHandler</code> class with a <code>SignallingTaskHandlerDecorator</code> instance.
We do this in order to define the what happens when the <code>ServiceTaskHandler</code> throws an
exception.</para>
<para>In this case, the <code>ServiceTaskHandler</code> will throw an exception because it's
configured to call the <code>ExceptionService.throwException</code> method, which throws an exception.
(See the <code>_handlingServiceInterface</code> <code><interface></code> element in the BPMN2.)</para>
<para>In the code below, we also configure which (error) event is sent to the process instance by
the <code>SignallingTaskHandlerDecorator</code> instance. The <code>SignallingTaskHandlerDecorator</code>
does this when an exception is thrown in a <emphasis>task</emphasis>. In this case, since we've
defined an <code><error></code> with the <emphasis>error code</emphasis> <quote>code</quote>
in the BPMN2, we set the signal to <code>Error-code</code>.</para>
<important>
<para>When signalling the jBPM process engine with an event of some sort, you should keep in mind
the rules for signalling process events.
<itemizedlist>
<listitem>Error events can be signalled by sending an "Error-" + <the <code>errorCode</code>
attribute value> value to the session.</listitem>
<listitem>Signal events can be signalled by sending the name of the signal to the session.</listitem>
</itemizedlist>
</para>
</important>
<programlistingco>
<areaspec>
<area id="err.java.signalhandler.event" coords='25' />
<area id="err.java.signalhandler" coords='26' />
<area id="err.java.signalhandler.exception" coords='28' />
</areaspec>
<programlisting language="java"><![CDATA[
import java.util.HashMap;
import java.util.Map;
import org.jbpm.bpmn2.handler.ServiceTaskHandler;
import org.jbpm.bpmn2.handler.SignallingTaskHandlerDecorator;
import org.jbpm.examples.exceptions.service.ExceptionService;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;
public class ExceptionHandlingErrorExample {
public static final void main(String[] args) {
runExample();
}
public static ProcessInstance runExample() {
KieSession ksession = createKieSession();
String eventType = "Error-code";
SignallingTaskHandlerDecorator signallingTaskWrapper
= new SignallingTaskHandlerDecorator(ServiceTaskHandler.class, eventType);
signallingTaskWrapper.setWorkItemExceptionParameterName(ExceptionService.exceptionParameterName);
ksession.getWorkItemManager().registerWorkItemHandler("Service Task", signallingTaskWrapper);
Map<String, Object> params = new HashMap<String, Object>();
params.put("serviceInputItem", "Input to Original Service");
ProcessInstance processInstance = ksession.startProcess("ProcessWithExceptionHandlingError", params);
return processInstance;
}
private static KieSession createKieSession() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ExceptionHandlingWithError.bpmn2"), ResourceType.BPMN2);
KieBase kbase = kbuilder.newKnowledgeBase();
return kbase.newKieSession();
}]]>
</programlisting>
<calloutlist>
<callout arearefs="err.java.signalhandler.event">
<para>Here we define the name of the event that will be sent to the process instance if
the wrapped <code>WorkItemHandler</code> implementation throws an exception. The
<code>eventType</code> string is used when instantiating the
<code>SignallingTaskHandlerDecorator</code> class.</para>
</callout>
<callout arearefs="err.java.signalhandler">
<para>Then we construct an instance of the <code>SignallingTaskHandlerDecorator</code> class.
In this case, we simply give it the <emphasis>class name</emphasis> of the
<code>WorkItemHandler</code> implementation class to instantiate, but another constructor is
available that we can pass an <emphasis>instance</emphasis> of a <code>WorkItemHandler</code>
implementation to (necessary if the <code>WorkItemHandler</code> implementation does not have
a no-argument constructor).</para>
</callout>
<callout arearefs="err.java.signalhandler.exception">
<para>When an exception is thrown by the wrapped <code>WorkItemHandler</code>, the
<code>SignallingTaskHandlerDecorator</code> saves it as a parameter in the <code>WorkItem</code>
instance with a parameter name that we configure the <code>SignallingTaskHandlerDecorator</code>
to give it (see the code below for the <code>ExceptionService</code>).</para>
</callout>
</calloutlist>
</programlistingco>
</section>
<section>
<title><code>ExceptionService</code> setup and configuration</title>
<para>In the BPMN2 process definition above, a service interface is defined that references
the <code>ExceptionService</code> class:</para>
<programlisting language="xml"><![CDATA[<interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
<operation id="_handlingServiceOperation" name="handleException">]]>
</programlisting>
<para>In order to fill in the blanks a little bit, the code for the <code>ExceptionService</code>
class has been included below. In general, you can specify any Java class with the default or an
other no-argument constructor and have it executed during a <code><serviceTask></code></para>
<programlisting language="java"><![CDATA[
public class ExceptionService {
public static String exceptionParameterName = "my.exception.parameter.name";
public void handleException(WorkItem workItem) {
System.out.println( "Handling exception caused by work item '" + workItem.getName() + "' (id: " + workItem.getId() + ")");
Map<String, Object> params = workItem.getParameters();
Throwable throwable = (Throwable) params.get(exceptionParameterName);
throwable.printStackTrace();
}
public String throwException(String message) {
throw new RuntimeException("Service failed with input: " + message );
}
public static void setExceptionParameterName(String exceptionParam) {
exceptionParameterName = exceptionParam;
}
}]]>
</programlisting>
</section>
<section>
<title>Changing the example to use a <code><signal></code></title>
<para>In the example above, the thrown Error Event interrupts the process: no other flows or
activities are executed once the Error Event has been thrown.</para>
<para>However, when a <emphasis>Signal Event</emphasis> is processed, the process will continue
after the <emphasis>Signal Event SubProcess</emphasis> (or whatever other activities that the
Signal Event triggers) has been executed. Furthermore, this implies that the the process will
<emphasis>not</emphasis> end up in an aborted state, unlike a process that throws an Error
Event.</para>
<para>In the process above, we use the <code><error></code> element in order to be able
to use an Error Event:</para>
<programlisting language="xml"><![CDATA[ <error id="_exception" errorCode="code" structureRef="_exceptionItem"/>]]></programlisting>
<para>When we want to use a Signal Event instead, we remove that line and use a
<code><signal></code> element:</para>
<programlisting language="xml"><![CDATA[ <signal id="exception-signal" structureRef="_exceptionItem"/> ]]></programlisting>
<para>However, we must also change all references to the "<code>_exception</code>"
<code><error></code> so that they now refer to the "<code>exception-signal</code>"
<code><signal></code>.</para>
<para>That means that the <code><errorEventDefintion></code> element in the <code><startEvent></code>,</para>
<programlisting language="xml"><![CDATA[ <errorEventDefinition id="_X-1_ED_1" errorRef="_exception" /> ]]></programlisting>
<para>must be changed to a <code><signalEventDefintion></code> which would like like this:</para>
<programlisting language="xml"><![CDATA[ <signalEventDefinition id="_X-1_ED_1" signalRef="exception-signal"/> ]]></programlisting>
<para>In short, we have to make the following changes to the <code><startEvent></code> in
the Event SubProcess:
<orderedlist>
<listitem>It will now contain a <code><signalEventDefintion></code> instead of a
<code><errorEventDefintion></code> </listitem>
<listitem>The <code>errorRef</code> attribute in the <code><erroEventDefintion></code> is
now a <code>signalRef</code> attribute in the <code><signalEventDefintion></code>.</listitem>
<listitem>The <code>id</code> attribute in the <code>signalRef</code> is of course now the id of
the <code><signal></code>
element. Before it was id of <code><error></code> element.</listitem>
<listitem>Lastly, when we signal the process in the Java code, we do not signal
"<code>Error-code</code>" but simply "<code>exception-signal</code>", the <code>id</code> of
the <code><signal></code> element.</listitem>
</orderedlist>
</para>
</section>
</section>
<section>
<title>Example: logging exceptions thrown by bad <code><scriptTask></code> nodes</title>
<para>In this section, we'll briefly describe what's possible when dealing with
<code><scriptTask></code> nodes that throw exceptions, and then quickly go through an example
(also available in the <filename>jbpm-examples</filename> module) that illustrates this.</para>
<section>
<title>Introduction</title>
<para>If you're reading this, then you probably already have problem: you're either
expecting to run into this problem because there are scripts in your process definition that might
throw an exception, or you're already running a process instance with scripts that are causing a
problem.</para>
<para>Unfortunately, if you're running into this problem, then there is not much you can do. The only
thing that you <emphasis>can</emphasis> do is retrieve more information about exactly what's causing
the problem. Luckily, when a <code><scriptTask></code> node causes an exception,
it's wrapped in a <code>WorkflowRuntimeException</code>.</para>
<para>What type of information is available? The <code>WorkflowRuntimeException</code> instance
will contain the information outlined in the following table. All of the fields listed are
available via the normal <code>get*</code> methods.</para>
<table frame='all'>
<title>Information contained in <code>WorkflowRuntimeException</code> instances.</title>
<tgroup cols="3" align='left' rowsep='2' colsep='1'>
<thead valign='top'>
<row>
<entry>Field name</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead><tbody valign='top'>
<row>
<entry><code>processInstanceId</code></entry>
<entry><code>long</code></entry>
<entry>The id of the <code>ProcessInstance</code> instance in which the exception occurred. This
<code>ProcessInstance</code> may not exist anymore or be available in the database if using
persistence!</entry>
</row><row>
<entry><code>processId</code></entry>
<entry><code>String</code></entry>
<entry>The id of the process definition that was used to start the process (i.e.
"<code>ExceptionScriptTask</code>" in
<programlisting language="java"><![CDATA[ksession.startProcess("ExceptionScriptTask"); ]]></programlisting>)
</entry>
</row><row>
<entry><code>nodeId</code></entry>
<entry><code>long</code></entry>
<entry>The value of the (BPMN2) id attribute of the node that threw the exception.</entry>
</row><row>
<entry><code>nodeName</code></entry>
<entry><code>String</code></entry>
<entry>The value of the (BPMN2) name attribute of the node that threw the exception.</entry>
</row><row>
<entry><code>variables</code></entry>
<entry><code>Map<String, Object></code></entry>
<entry>The map containing the variables in the process instance (<emphasis>experimental</emphasis>).</entry>
</row><row>
<entry><code>message</code></entry>
<entry><code>String</code></entry>
<entry>The short message indicating what went wrong.</entry>
</row><row>
<entry><code>cause</code></entry>
<entry><code>Throwable</code></entry>
<entry>The original exception that was thrown.</entry>
</row>
</tbody>
</tgroup>
</table>
</section>
<section>
<title>Example: Exceptions thrown by a <code><scriptTask></code>.</title>
<para>The following code illustrates how to extract extra information from a process instance
that throws a <code>WorkflowRuntimeException</code> exception instance.</para>
<programlisting language="java"><![CDATA[
import org.jbpm.workflow.instance.WorkflowRuntimeException;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;
public class ScriptTaskExceptionExample {
public static final void main(String[] args) {
runExample();
}
public static void runExample() {
KieSession ksession = createKieSession();
Map<String, Object> params = new HashMap<String, Object>();
String varName = "var1";
params.put( varName , "valueOne" );
try {
ProcessInstance processInstance = ksession.startProcess("ExceptionScriptTask", params);
} catch( WorkflowRuntimeException wfre ) {
String msg = "An exception happened in "
+ "process instance [" + wfre.getProcessInstanceId()
+ "] of process [" + wfre.getProcessId()
+ "] in node [id: " + wfre.getNodeId()
+ ", name: " + wfre.getNodeName()
+ "] and variable " + varName + " had the value [" + wfre.getVariables().get(varName)
+ "]";
System.out.println(msg);
}
}
private static KieSession createKieSession() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ScriptTaskException.bpmn2"), ResourceType.BPMN2);
KieBase kbase = kbuilder.newKnowledgeBase();
return kbase.newKieSession();
}
}]]>
</programlisting>
</section>
</section>
</section>
<!-- marco -->
<!-- // mriet: finish off and uncomment the following sections once
// compensation/exceptions are fully implemented
<section>
<title>Business Exceptions</title>
<section>
<title>Where Should I Design for Business exceptions?</title>
<para>Where are business exceptions likely to occur? There is academic research on this, but some
possible examples are:
<itemizedlist>
<listitem>When an interaction with an external party or 3rd party system does not go as
planned</listitem>
<listitem>When you can not fully check the the input data in your process (like a client's
address information, for example)</listitem>
<listitem>In general, if there are parts of your process that are particularly dependent on
one of the following, a business exception will be a good idea:
<itemizedlist>
<listitem>Company policy or policy governing certain in-house procedures</listitem>
<listitem>Laws governing the business process (age requirements, for example)</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</para>
</section>
<section>
<title>Business Exception Examples</title>
<orderedlist>
<listitem>using an error event</listitem>
<listitem>Using a compensation event</listitem>
<listitem>Using a compensation event with a business transaction</listitem>
<listitem>Using a cancel event with a business transaction</listitem>
<listitem>Using a cancel event and a compensation event with a bus trx</listitem>
</orderedlist>
</section>
</section>
-->
</chapter>