chap-workflow.xml revision 7d19a158b53f47b175ba1e6aad07c79365847ae6
<?xml version="1.0" encoding="UTF-8"?>
<!--
! CCPL HEADER START
!
! This work is licensed under the Creative Commons
! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
! To view a copy of this license, visit
! http://creativecommons.org/licenses/by-nc-nd/3.0/
! or send a letter to Creative Commons, 444 Castro Street,
! Suite 900, Mountain View, California, 94041, USA.
!
! You can also obtain a copy of the license at
! legal/CC-BY-NC-ND.txt.
! See the License for the specific language governing permissions
! and limitations under the License.
!
! If applicable, add the following below this CCPL HEADER, with the fields
! enclosed by brackets "[]" replaced with your own identifying information:
! Portions Copyright [yyyy] [name of copyright owner]
!
! CCPL HEADER END
!
! Copyright 2011-2012 ForgeRock AS
!
-->
<chapter xml:id='chap-workflow'
xmlns='http://docbook.org/ns/docbook'
version='5.0' xml:lang='en'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd'
xmlns:xlink='http://www.w3.org/1999/xlink'
xmlns:xinclude='http://www.w3.org/2001/XInclude'>
<title>Business Processes &amp; Workflow</title>
<para>Key to any identity management solution is the ability to provide
workflow driven provisioning activities, whether for self-service actions
such as request for entitlements, roles or resources, running sunrise or
sunset processes, handling approvals with escalations, or performing
maintenance.</para>
<para>OpenIDM provides an embedded workflow and business process engine
based on Activiti and the Business Process Model and Notation (BPMN) 2.0
standard.</para>
<note>
<para>The embedded workflow and business process engine is currently
provided as part of a separate, experimental download,
<filename>openidm-<?eval ${docTargetVersion}?>-workflow-experimental.zip</filename>,
supported separately from the standard OpenIDM release.</para>
<!-- TODO: Confirm this approach for delivery -->
<para>You install
<filename>openidm-<?eval ${docTargetVersion}?>-workflow-experimental.zip</filename>
in the same way as standard OpenIDM. See the <link
xlink:href="install-guide#chap-install"
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Installation
Guide</citetitle></link> for instructions. After starting OpenIDM, run
the <command>scr list</command> command at the console, and check that
bundle is active.</para>
<screen>-&gt; scr list
...
[ 6] [active ] org.forgerock.openidm.external.activiti
...</screen>
<para>Contact ForgeRock at <link xlink:href="mailto:info@forgerock.com"
>info@forgerock.com</link> for details on support for the experimental
embedded workflow and business process engine.</para>
</note>
<para>More information about Activiti and the Activiti project can be found
at <link xlink:href="http://www.activiti.org" xlink:show="new"
>http://www.activiti.org</link>.</para>
<section xml:id="about-bmpm-2-activiti">
<title>About BPMN 2.0 &amp; Activity Tools</title>
<para>Business Process Model and Notation 2.0 is the result of consensus
among Business Process Management (BPM) system vendors. The <link
xlink:href="http://omg.org/" xlink:show="new">Object Management Group</link>
(OMG) has developed and maintained the <link xlink:show="new"
xlink:href="http://www.omg.org/spec/BPMN/">BPMN</link> standard since
2004.</para>
<para>The first version of the BPMN specification focused only on graphical
notation, and quickly became popular with the business analyst audience.
BPMN 1.x defines how constructs such as human tasks, executable scripts, and
automated decisions are visualized in a vendor-neutral, standard way. The
second version of BPMN extends that focus to include execution semantics,
and a common exchange format. Thus, BPMN 2.0 process definition models
can be exchanged not only between different graphical editors, but also
can be executed as is on any BPMN 2.0-compliant engine, such as the engine
embedded in OpenIDM.</para>
<para>Using BMPN 2.0, you can add artifacts describing workflow and business
process behavior to OpenIDM for provisioning and other purposes. For example,
you can craft the actual artifacts defining business processes and workflow
in a text editor such as <command>vi</command>, or using a special Eclipse
plugin. The Eclipse plugin provides visual design capabilities, simplifying
packaging and deployment of the artifact to OpenIDM. See the <link
xlink:href="http://docs.codehaus.org/display/ACT/Activiti+BPMN+2.0+Eclipse+Plugin"
xlink:show="new">Activiti BPMN 2.0 Eclipse Plugin</link> documentation for
instructions on installing Activiti Eclipse BPMN 2.0 Designer.</para>
<para>Also read the Activiti <citetitle>User Guide</citetitle> section
covering <link xlink:href="http://www.activiti.org/userguide/#bpmnConstructs"
xlink:show="new"><citetitle>BPMN 2.0 Constructs</citetitle></link>, which
describes in detail the graphical notations and XML representations for
events, flows, gateways, tasks, and process constructs.</para>
</section>
<section xml:id="openidm-activity-limitations">
<title>Known Issues &amp; Limitations</title>
<itemizedlist>
<para>The following are known issues and limitation for the experimental
embedded workflow and business process engine.</para>
<listitem>
<para>OpenIDM does not include a form generator, making it difficult to
include embedded forms. (OPENIDM-468)</para>
</listitem>
<listitem>
<!-- TODO: Issue ID? -->
<para>Error handling needs improvement. (OPENIDM-???)</para>
</listitem>
<listitem>
<para>This version depends on Activiti 5.8, which does not have the fix
for <link xlink:href="http://jira.codehaus.org/browse/ACT-583"
xlink:show="new">ACT-583</link>: Processes are not found in the bar file,
if they are below root.</para>
<para>To work around this issue, create a directory inside the .bar, and
put the BPMN XML artifact in the directory so that it is properly picked up
by the Process Engine.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="invoking-activiti-workflows">
<title>Invoking Activiti Workflows</title>
<para>You can invoke workflows and business processes from any trigger point
OpenIDM offers, including reacting to situations discovered during
reconciliation. Invocation relies on the <literal>openidm.action()</literal>
function.</para>
<programlisting language="javascript">
/*
* Calling 'myWorkflow' workflow
*/
var map = {
"_action" : "myWorkflow",
"_workflowParams" : {
"foo" : "bar"
}
};
openidm.action("workflow/activiti", map);</programlisting>
</section>
<section xml:id="sample-activiti-workflows">
<title>Example Activiti Workflows With OpenIDM</title>
<para>This section describes two example workflows, one using email
notification, the other involving a sunset process triggered during
reconciliation.</para>
<!-- TODO: Rather than include the samples only in the doc, deliver the
samples with OpenIDM an describe the highlights in the documentation. -->
<section xml:id="example-activiti-email-notification-flow">
<title>Example Email Notification Workflow</title>
<para>This example uses the Activiti Eclipse BPMN 2.0 Designer to set up an
email notification business process. The example relies on an SMTP server
listening on <literal>localhost</literal>, port 25.</para>
<variablelist>
<para>The example sets up a workflow that can accept parameters used to
specify the sender and recipient of the mail.</para>
<varlistentry>
<term><literal>${fromSender}</literal></term>
<listitem>
<para>Used to specify the sender</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>${toEmail}</literal></term>
<listitem>
<para>Used to specify the recipient</para>
</listitem>
</varlistentry>
</variablelist>
<para>Once you have defined the workflow, drag and drop components to create
the workflow. This simple example uses only a
<literal>StartEvent</literal>, <literal>MailTask</literal>, and
<literal>EndEvent</literal>.</para>
<mediaobject>
<alt>Email notification process</alt>
<imageobject>
<imagedata fileref="images/bpmn-email-notification.png" format="PNG" />
</imageobject>
<textobject>
<para>The email notification workflow has a start event, mail task, and
end event.</para>
</textobject>
</mediaobject>
<para>After creating the workflow, adjust the generated XML source code to
use the variables inside the <literal>&lt;serviceTask&gt;</literal>
tag shown in the following listing.</para>
<programlisting language="xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;definitions
xmlns=&quot;http://www.omg.org/spec/BPMN/20100524/MODEL&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:activiti=&quot;http://activiti.org/bpmn&quot;
xmlns:bpmndi=&quot;http://www.omg.org/spec/BPMN/20100524/DI&quot;
xmlns:omgdc=&quot;http://www.omg.org/spec/DD/20100524/DC&quot;
xmlns:omgdi=&quot;http://www.omg.org/spec/DD/20100524/DI&quot;
typeLanguage=&quot;http://www.w3.org/2001/XMLSchema&quot;
expressionLanguage=&quot;http://www.w3.org/1999/XPath&quot;
targetNamespace=&quot;http://www.activiti.org/test&quot;&gt;
&lt;process id=&quot;EmailNotification&quot; name=&quot;emailNotification&quot;&gt;
&lt;documentation&gt;Simple Email Notification Task&lt;/documentation&gt;
&lt;startEvent id=&quot;startevent1&quot; name=&quot;Start&quot;&gt;&lt;/startEvent&gt;
&lt;sequenceFlow id=&quot;flow1&quot; name=&quot;&quot; sourceRef=&quot;startevent1&quot;
targetRef=&quot;mailtask1&quot;&gt;&lt;/sequenceFlow&gt;
&lt;endEvent id=&quot;endevent1&quot; name=&quot;End&quot;&gt;&lt;/endEvent&gt;
&lt;sequenceFlow id=&quot;flow2&quot; name=&quot;&quot; sourceRef=&quot;mailtask1&quot;
targetRef=&quot;endevent1&quot;&gt;&lt;/sequenceFlow&gt;
&lt;serviceTask id=&quot;mailtask1&quot; name=&quot;Email Notification&quot;
activiti:type=&quot;mail&quot;&gt;
&lt;extensionElements&gt;
&lt;activiti:field name=&quot;to&quot; expression=&quot;${toEmail}&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;from&quot; expression=&quot;no-reply@forgerock.com&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;subject&quot; expression=&quot;Simple Email Notification&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;html&quot;&gt;
&lt;activiti:expression&gt;&lt;![CDATA[Here is a simple Email Notification
from ${fromSender}.]]&gt;&lt;/activiti:expression&gt;
&lt;/activiti:field&gt;
&lt;/extensionElements&gt;
&lt;/serviceTask&gt;
&lt;/process&gt;
&lt;bpmndi:BPMNDiagram id=&quot;BPMNDiagram_EmailNotification&quot;&gt;
&lt;bpmndi:BPMNPlane bpmnElement=&quot;EmailNotification&quot;
id=&quot;BPMNPlane_EmailNotification&quot;&gt;
&lt;bpmndi:BPMNShape bpmnElement=&quot;startevent1&quot; id=&quot;BPMNShape_startevent1&quot;&gt;
&lt;omgdc:Bounds height=&quot;35&quot; width=&quot;35&quot; x=&quot;170&quot; y=&quot;250&quot;&gt;&lt;/omgdc:Bounds&gt;
&lt;/bpmndi:BPMNShape&gt;
&lt;bpmndi:BPMNShape bpmnElement=&quot;endevent1&quot; id=&quot;BPMNShape_endevent1&quot;&gt;
&lt;omgdc:Bounds height=&quot;35&quot; width=&quot;35&quot; x=&quot;410&quot; y=&quot;250&quot;&gt;&lt;/omgdc:Bounds&gt;
&lt;/bpmndi:BPMNShape&gt;
&lt;bpmndi:BPMNShape bpmnElement=&quot;mailtask1&quot; id=&quot;BPMNShape_mailtask1&quot;&gt;
&lt;omgdc:Bounds height=&quot;55&quot; width=&quot;105&quot; x=&quot;250&quot; y=&quot;240&quot;&gt;&lt;/omgdc:Bounds&gt;
&lt;/bpmndi:BPMNShape&gt;
&lt;bpmndi:BPMNEdge bpmnElement=&quot;flow1&quot; id=&quot;BPMNEdge_flow1&quot;&gt;
&lt;omgdi:waypoint x=&quot;205&quot; y=&quot;267&quot;&gt;&lt;/omgdi:waypoint&gt;
&lt;omgdi:waypoint x=&quot;250&quot; y=&quot;267&quot;&gt;&lt;/omgdi:waypoint&gt;
&lt;/bpmndi:BPMNEdge&gt;
&lt;bpmndi:BPMNEdge bpmnElement=&quot;flow2&quot; id=&quot;BPMNEdge_flow2&quot;&gt;
&lt;omgdi:waypoint x=&quot;355&quot; y=&quot;267&quot;&gt;&lt;/omgdi:waypoint&gt;
&lt;omgdi:waypoint x=&quot;410&quot; y=&quot;267&quot;&gt;&lt;/omgdi:waypoint&gt;
&lt;/bpmndi:BPMNEdge&gt;
&lt;/bpmndi:BPMNPlane&gt;
&lt;/bpmndi:BPMNDiagram&gt;
&lt;/definitions&gt;</programlisting>
<para>In Eclipse, select the project, then right click and select
Create deployment artifacts to generate the components and package them
in a .bar file for deployment in the <filename>workflow</filename>
directory where you installed OpenIDM. OpenIDM can pick up the new
workflow dynamically.</para>
<!-- TODO: Add specifics here... -->
<para>Before you deploy the .bar, work around the issue mentioned above
by adding the XML artifact in the right place in the .bar.</para>
<para>After you deploy the .bar, create a script in
<filename>script/invokeEmailNotification.js</filename> where you installed
OpenIDM. The script invokes the workflow.</para>
<programlisting language="javascript">
/*
* Calling 'EmailNotification' workflow
*/
var map = {
"_action" : "emailNotification",
"_workflowParams" : {
"fromSender" : "noreply@openidm",
"toEmail" : "john.doe@corp.com"
}
};
openidm.action("workflow/activiti", map);</programlisting>
<para>Also, create a schedule configuration object in
<filename>conf/schedule-EmailNotification.json</filename> where you
installed OpenIDM. The following schedule invokes the workflow once per
minute.</para>
<programlisting language="javascript">
{
"enabled" : true,
"type" : "cron",
"schedule" : "0 0/1 * * * ?",
"invokeService" : "script",
"invokeContext" : {
"script" : {
"type" : "text/javascript",
"file" : "script/invokeEmailNotification.js"
},
}
}</programlisting>
<para>TODO: Show and explain the result.</para>
</section>
<section xml:id="example-activiti-recon-sunset-flow">
<title>Example Sunset Workflow Triggered By Reconciliation</title>
<para>OpenIDM can allow deferred deprovisioning and disabling, such as
needed when an external consultant's contract expires or is up for
renewal. This example shows how OpenIDM can be used to handle such
scenarios.</para>
<!-- TODO: Finish the example, and then rewrite this section. Perhaps
all of this should be in one of the samples, rather than describing how
it all works in great detail here? -->
<para>To understand the scenario, consider the target resource and the
authoritative source.</para>
<para>Set up a CSV file connector to connect to an authoritative source
file containing the following.</para> <!-- TODO: Howto steps or link to howto -->
<!-- TODO: need a script or steps to generate dates for the example, because
the dates here will not be useful if you read the documentation in 2013, right? -->
<programlisting language="csv" width="112">
firstName,uid,"lastName","email","employeeNumber",password,"sunrise","sunset","active"
"Darth","DDOE","Doe","doe@forgerock.org","123456","Z29vZA==","2011-11-30T10:17:00","2011-12-23T16:12:00","TRUE"</programlisting>
<para>The authoritative source contains the field, <literal>sunset</literal>,
containing a time and date string referring to a time in the future.</para>
<para>Set up a simple XML connector to replicate fields in the authoritative
CSV source on a 1-to-1 basis.</para> <!-- TODO: Howto steps or link to howto -->
<para>Define a Sunset Workflow using Activiti Eclipse BPMN 2.0
Designer.</para>
<mediaobject>
<alt>Sunset workflow process</alt>
<imageobject>
<imagedata fileref="images/bpmn-sunset-flow.png" format="PNG" />
</imageobject>
<textobject>
<para>The sunset workflow starts by saving the invoke context, prepares
the call to OpenIDM, reads the user, runs an email notification, disables
the account, and ends.</para>
</textobject>
</mediaobject>
<para>Use the following XML artifact.</para>
<programlisting language="xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;definitions id=&quot;definitions&quot;
xmlns=&quot;http://www.omg.org/spec/BPMN/20100524/MODEL&quot;
xmlns:activiti=&quot;http://activiti.org/bpmn&quot;
targetNamespace=&quot;Examples&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation=&quot;http://www.omg.org/spec/BPMN/20100524/MODEL
http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd&quot;&gt;
&lt;process id=&quot;sunset&quot; name=&quot;sunset usecase&quot;&gt;
&lt;startEvent id=&quot;start&quot; /&gt;
&lt;sequenceFlow sourceRef=&quot;start&quot; targetRef=&quot;saveInvokeContext&quot;/&gt;
&lt;scriptTask id=&quot;saveInvokeContext&quot; scriptFormat=&quot;groovy&quot;
activiti:returnVariable=&quot;invokecontext&quot;&gt;
&lt;script&gt;
&lt;![CDATA[
invokecontext =
org.forgerock.openidm.context.InvokeContext.getContext()
]]&gt;
&lt;/script&gt;
&lt;/scriptTask&gt;
&lt;sequenceFlow sourceRef=&quot;saveInvokeContext&quot;
targetRef=&quot;timerintermediatecatchevent1&quot;/&gt;
&lt;intermediateCatchEvent id=&quot;timerintermediatecatchevent1&quot;
name=&quot;TimerCatchEvent&quot;&gt;
&lt;extensionElements&gt;
&lt;activiti:field name=&quot;t&quot; expression=&quot;${time}&quot;
&gt;&lt;/activiti:field&gt;
&lt;/extensionElements&gt;
&lt;timerEventDefinition&gt;
&lt;timeDate &gt;${time}&lt;/timeDate&gt;
&lt;/timerEventDefinition&gt;
&lt;/intermediateCatchEvent&gt;
&lt;sequenceFlow sourceRef=&quot;timerintermediatecatchevent1&quot;
targetRef=&quot;idmCall&quot;/&gt;
&lt;serviceTask id=&quot;idmCall&quot; activiti:delegateExpression=&quot;${openidm}&quot;
activiti:async=&quot;true&quot;&gt;
&lt;extensionElements&gt;
&lt;activiti:field name=&quot;userName&quot; expression=&quot;${userName}&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;system&quot; expression=&quot;${system}&quot;
&gt;&lt;/activiti:field&gt;
&lt;/extensionElements&gt;
&lt;/serviceTask&gt;
&lt;sequenceFlow sourceRef=&quot;idmCall&quot; targetRef=&quot;readUserAccount&quot;/&gt;
&lt;scriptTask id=&quot;readUserAccount&quot; scriptFormat=&quot;groovy&quot;
activiti:returnVariable=&quot;user&quot; &gt;
&lt;script&gt;
&lt;![CDATA[
out:println '********Sunset date for user ' + userName
org.forgerock.openidm.context.InvokeContext.getContext().
putApprover(invokecontext.getApprover())
org.forgerock.openidm.context.InvokeContext.getContext().
putRequester(invokecontext.getRequester())
org.forgerock.openidm.context.InvokeContext.getContext().
pushActivityId(invokecontext.popActivityId())
user = openidm.read(system + userName);
]]&gt;
&lt;/script&gt;
&lt;/scriptTask&gt;
&lt;sequenceFlow sourceRef=&quot;readUserAccount&quot; targetRef=&quot;sendMail&quot;/&gt;
&lt;serviceTask id=&quot;sendMail&quot; name=&quot;Email Notification&quot;
activiti:type=&quot;mail&quot;&gt;
&lt;extensionElements&gt;
&lt;activiti:field name=&quot;to&quot; expression=&quot;${toEmail}&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;from&quot; expression=&quot;no-reply@forgerock.com&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;subject&quot; expression=&quot;Disabling User&quot;
&gt;&lt;/activiti:field&gt;
&lt;activiti:field name=&quot;text&quot;&gt;
&lt;activiti:expression&gt;&lt;![CDATA[
The following user has been disabled:
${user}]]&gt;
&lt;/activiti:expression&gt;
&lt;/activiti:field&gt;
&lt;/extensionElements&gt;
&lt;/serviceTask&gt;
&lt;sequenceFlow sourceRef=&quot;sendMail&quot; targetRef=&quot;disableAccount&quot;/&gt;
&lt;scriptTask id=&quot;disableAccount&quot; scriptFormat=&quot;groovy&quot;&gt;
&lt;script&gt;
&lt;![CDATA[
out:println '********Disabling user account ' + userName
user['__ENABLE__'] = false
openidm.update(system + userName, null, user)
]]&gt;
&lt;/script&gt;
&lt;/scriptTask&gt;
&lt;sequenceFlow sourceRef=&quot;disableAccount&quot; targetRef=&quot;end&quot;/&gt;
&lt;endEvent id=&quot;end&quot;/&gt;
&lt;/process&gt;
&lt;/definitions&gt;</programlisting>
<para>Set up a deferred action during reconciliation by reacting to a
<literal>FOUND</literal> situation. When called, the script invoked must
return the proper action to the found situation, which in the case of this
example is <literal>LINK</literal>.</para>
<para>Add the following script to
<filename>script/triggerSunset.js</filename> where you installed OpenIDM,
invoked when reconciliation encounters a <literal>FOUND</literal>
situation.</para>
<programlisting language="javascript">
/*
* Calling 'sunrise' workflow
*/
var map = {
"_action" : "sunrise",
"_workflowParams" : {
"userName" : target.__UID__,
"system" : "system/xmlfile/account/",
"time" : source.sunset,
"toEmail" : "manager@corp.org",
"fromSender" : "noreply@openidm"
}
};
openidm.action("workflow/activiti", map);
"LINK"</programlisting>
<para>Add the following schedule to
<filename>conf/scheduler-reconcile_HR_XML.json</filename> where you
installed OpenIDM to invoke the script.</para>
<programlisting language="javascript">
{
"enabled" : true,
"type" : "cron",
"schedule" : "0 08 16 * * ?",
"invokeService" : "discovery-engine",
"invokeContext" : {
"action" : "reconcile",
"mapping" : {
"name" : "CSV_XML",
"source" : "system/CSV/persons",
"target" : "system/xmlfile/account",
"correlationQuery" : {
"type" : "text/javascript",
"source" : "var myarray = [source.uid];var map = {
'query' : {
'Equals': {'field' : 'name','values' : myarray}
}
};map;"
},
"properties" : [
{
"source" : "firstname",
"target" : "firstname"
},
{
"source" : "uid",
"target" : "name"
},
{
"source" : "lastname",
"target" : "lastname"
},
{
"source" : "email",
"target" : "email"
}
],
"policies" : [
{
"situation" : "CONFIRMED",
"action" : "IGNORE"
},
<emphasis role="bold">{
"situation" : "FOUND",
"action" : {
"type" : "text/javascript",
"file" : "script/triggerSunset.js"
}
},</emphasis>
{
"situation" : "ABSENT",
"action" : "IGNORE"
},
{
"situation" : "AMBIGUOUS",
"action" : "IGNORE"
},
{
"situation" : "MISSING",
"action" : "IGNORE"
},
{
"situation" : "UNQUALIFIED",
"action" : "IGNORE"
},
{
"situation" : "UNASSIGNED",
"action" : "IGNORE"
}
]
}
}
}</programlisting>
<para>TODO: Show results</para>
</section>
</section>
</chapter>