chap-workflow.xml revision 601f63c54242cbfe25637728c498c5061af3c380
<?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
! 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
! 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>Integrating Business Processes & Workflows</title>
<indexterm>
<primary>Workflow</primary>
</indexterm>
<indexterm>
<primary>Business processes</primary>
</indexterm>
<para>Key to any identity management solution is the ability to provide
workflow-driven provisioning activities, whether for self-service actions
such as requests 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>
<para>More information about Activiti and the Activiti project can be found
<section xml:id="about-bmpm-2-activiti">
<title>BPMN 2.0 and the Activiti Tools</title>
<para>Business Process Model and Notation 2.0 is the result of consensus
among Business Process Management (BPM) system vendors. The <link
(OMG) has developed and maintained the <link xlink:show="new"
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 can also
be executed as is on any BPMN 2.0-compliant engine, such as the engine
embedded in OpenIDM.</para>
<para>Using BPMN 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: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
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="setting-up-activiti">
<title>Setting Up Activiti Integration With OpenIDM</title>
<itemizedlist>
<para>There are two modes of integrating Activiti with OpenIDM:</para>
<listitem><para>Local integration, where an embedded Activiti Process Engine
is started in the OpenIDM OSGi container.</para></listitem>
<listitem><para>Remote integration, where OpenIDM and Activiti run as
separate instances and the integration is done using a REST API.</para>
</listitem>
</itemizedlist>
<section xml:id="setting-up-local-integration">
<title>Setting Up Local Integration</title>
<para>The embedded workflow and business process engine is provided as part
of the standard OpenIDM build.</para>
<para>Install the OpenIDM build, as described in the
<link xlink:href="install-guide#chap-install"
Guide</citetitle></link>. Start OpenIDM, and run the
<command>scr list</command> command at the console to check that the
workflow bundle is active.</para>
<screen>-> scr list
...
[ 14] [active ] org.forgerock.openidm.workflow
...</screen>
<para>To verify the workflow integration you need at least one workflow
directory to test the workflow integration.</para>
<para>You can verify the workflow integration by using the REST API. The
following REST call lists the defined workflows:</para>
<screen>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--request GET
</screen>
<para>The sample workflow definition that you copied in the previous step is
named <literal>osgiProcess</literal>. The result of the preceding REST call
is therefore something like:</para>
<screen>
{"osgiProcess:1:3":{"name":"Osgi process","key":"osgiProcess"}}
</screen>
<para>The <literal>osgiProcess</literal> definition calls OpenIDM, queries
the available workflow definitions from Activiti, then prints the list of
workflow definitions to the OpenIDM logs. Invoke the <literal>osgiProcess
</literal> workflow with the following REST call to OpenIDM:</para>
<screen width="100"><?dbfo pgwide="1"?>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--request POST
--data '{"key":"osgiProcess"}'
</screen>
<para>The workflow prints the list of workflow definitions to the OpenIDM
console. With the default sample, you should see something like this on the
console:</para>
<screen width="100"><?dbfo pgwide="1"?>
script task using expression resolver: [osgiProcess:1:3:[name:Osgi process, key:osgiProcess]]
script task using resolver: [osgiProcess:1:3:[name:Osgi process, key:osgiProcess]]
</screen>
<section xml:id="openidm-activity-limitations">
<title>Known Issues & Limitations</title>
<itemizedlist>
<para>The following issues and limitations exist for the
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.</para>
</listitem>
</itemizedlist>
</section>
</section>
<section xml:id="setting-up-remote-integration">
<title>Setting Up Remote Integration</title>
<para>You can set up integration with a remote Activiti engine, as described
in the following steps.</para>
<procedure>
<step>
<para>Download and install OpenIDM, as described in the
<link xlink:href="install-guide#chap-install"
Guide</citetitle></link>.</para>
</step>
<step>
<para>Download and unzip the Activiti zip file
</step>
<step>
<para>Edit the Activiti configuration file to avoid a port conflict with
OpenIDM. OpenIDM runs on port 8080 by default. Edit the file so that
Activiti Explorer runs on port 9090 (by replacing each instance of 8080
with 9090). The following example uses <literal>sed</literal> on a UNIX
system to replace all instances of 8080 with 9090.
</para>
</screen>
<note><para>There is currently a bug in the Activiti demo which might mean
that all port replacements are not made. If you cannot access Activiti
Explorer (in the next step) after making this change, <emphasis>also</emphasis>
edit the following file to find and replace each instance of 8080 with 9090:
</para></note>
</step>
<step>
<para>Set up the default Activiti demo.</para>
$ ant demo.start
</screen>
</step>
<step>
<para>In a browser, check that Activiti Explorer is running
(on localhost:9090/activiti-explorer). Log in with the default userid
<literal>kermit</literal> and password <literal>kermit</literal>. If all
is well, log out.</para>
</step>
<step>
<para>Configure Tomcat to operate with OpenIDM.</para>
<orderedlist>
<listitem>
<para>Stop Tomcat.</para>
$ ant tomcat.stop
</screen>
</listitem>
<listitem>
<para>Copy the OpenIDM remote workflow WAR file to the Tomcat webapps
folder.</para>
</screen>
</listitem>
<listitem>
<para>Copy the OpenIDM workflow Activiti demo jar file to the Tomcat
Activiti Explorer library.</para>
</screen>
</listitem>
<listitem>
<para>Edit the Activiti Explorer configuration file to be able to use the
OpenIDM extensions.</para>
</screen>
<para>Replace the <literal>processEngineConfiguration</literal> with the
OpenIDM extended configuration. So remove this section:
</para>
<programlisting language="xml" width="90">
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="customFormTypes">
<list>
<ref bean="userFormType"/>
</list>
</property>
</bean>
</programlisting>
<para>and replace it with this section:</para>
<programlisting language="xml" width="90">
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="customFormTypes">
<list>
<ref bean="userFormType"/>
</list>
</property>
<property name="customSessionFactories">
<list>
<property name="url" value="http://localhost:8080/openidm/"/>
<property name="user" value="openidm-admin"/>
<property name="password" value="openidm-admin"/>
</bean>
</list>
</property>
<property name="resolverFactories">
<list>
<bean class="org.forgerock.openidm.workflow.activiti.impl.OpenIDMResolverFactory"></bean>
</list>
</property>
<property name="expressionManager">
<bean class="org.forgerock.openidm.workflow.activiti.impl.OpenIDMExpressionManager"> </bean>
</property>
</bean>
</programlisting>
</listitem>
<listitem>
<para>Restart Tomcat.</para>
$ ant tomcat.start
</screen>
</listitem>
<listitem>
<para>Check that Activiti Explorer is running on
localhost:9090/activiti-explorer, as you did in the previous section.
</para>
</listitem>
</orderedlist>
</step>
<step>
<para>Configure OpenIDM to use the remote Activiti engine instead of the
local, bundled Activiti engine.</para>
<orderedlist>
<listitem>
the OpenIDM configuration directory.</para>
</screen>
</listitem>
<listitem>
<para>Edit the workflow configuration file to specify the remote Activiti
engine.</para>
</screen>
<para>Ensure that the url, username, and password fields contain the values
that correspond to your remote Activiti engine.</para>
<programlisting language="javascript">
{
"enabled" : "true",
"location" : "remote",
"engine" : {
"username" : "kermit",
"password" : "kermit"
},
</programlisting>
</listitem>
</orderedlist>
</step>
<step>
<para>Start up OpenIDM.</para>
$ /startup.sh
</screen>
</step>
<step>
<para>Test the integration by sending the following CURL request to list
the available workflows:</para>
<screen>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--request GET
</screen>
</step>
<step>
<para>Log in to the Activiti Explorer of the remote Activiti engine (with
the default username (kermit) and password (kermit).
</para>
</step>
<step>
<orderedlist>
<listitem>
<para>In Activiti Explorer, click Manage.</para>
</listitem>
<listitem>
<para>From the Deployments menu, select Upload New.</para>
</listitem>
<listitem>
</listitem>
</orderedlist>
</step>
<step>
<para>Verify the integration by sending the following CURL request:</para>
<screen>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--request POST
--data '{"key":"osgiProcess"}'
</screen>
<para>The request should return a process ID, similar to the following:</para>
<screen>
{"id":"301","processInstanceId":"301","status":"ended",
"businessKey":null,"processDefinitionId":"osgiProcess:1:3"}
</screen>
<para>This request starts the osgiProcess and writes the list of installed
</para>
</step>
<step>
<para>Test the integration with Activiti Explorer.</para>
<orderedlist>
<listitem>
<para>Click Processes and select Osgi Process from the list of process
definitions.</para>
</listitem>
<listitem>
<para>Click Start Process.</para>
</listitem>
<listitem>
<para>Check the output in the Tomcat server log file (
The list of installed workflows is written to the log file.</para>
</listitem>
</orderedlist>
</step>
</procedure>
</section>
</section>
<section xml:id="configuring-activiti-engine">
<title>Configuring the Activiti Engine</title>
<para>Whether you use the embedded Activiti engine, or a remote Activiti
engine, you configure the OpenIDM Activiti module in a file named
absent from the configuration, the workflow module is unavailable for use.
file assumes an embedded Activiti engine, and has the following
configuration:</para>
<programlisting language="javascript">
{
"enabled" : "true",
"location" : "embedded",
},
</programlisting>
<para>You can disable the workflow module by setting the "enabled" property
in this file to "false".</para>
To configure an Activiti engine beyond the default configuration that is
provided, edit this file as required and copy it to the
configuration:</para>
<programlisting language="javascript">
{
"enabled" : "true",
"location" : "remote",
"engine" : {
"username" : "youractivitiuser",
"password" : "youractivitipassword"
},
"mail" : {
"host" : "yourserver.smtp.com",
"port" : 587,
"username" : "yourusername",
"password" : "yourpassword",
"starttls" : true
},
"history" : "audit"
}
</programlisting>
<itemizedlist><para>These fields have the following meaning:</para>
<listitem>
<para><literal>enabled</literal>. Indicates whether the Activiti module is
enabled for use. Possible values are <literal>true</literal> or
<literal>false</literal>. The default value is <literal>true</literal>.</para>
</listitem>
<listitem><para><literal>location</literal>. Indicates whether the Activiti
engine is embedded with OpenIDM, or remote. Possible values are
<literal>embedded</literal> or <literal>remote</literal>. If
<literal>remote</literal>, you must provide details for the
<literal>engine</literal> property, below.</para></listitem>
<listitem><para><literal>engine</literal>. Specifies the details of the
remote Activiti engine. The following fields must be defined:</para>
<itemizedlist>
<listitem><para><literal>url</literal>. The URL of the remote engine,
including the host name and port number.</para></listitem>
<listitem><para><literal>username</literal>. A user name for the remote
Activiti engine.</para></listitem>
<listitem><para><literal>password</literal>. The password for the user
specified above.</para></listitem>
</itemizedlist>
</listitem>
<listitem><para><literal>mail</literal>. Specifies the details of the
mail server that Activiti will use to send email notifications. By default,
Activiti uses the mail server <literal>localhost:25</literal>. To specify
a different mail server, enter the details of the mail server here.</para>
<itemizedlist>
<listitem><para><literal>host</literal>. The host of the mail server.
</para></listitem>
<listitem><para><literal>port</literal>. The port number of the mail
server.</para></listitem>
<listitem><para><literal>username</literal>. The user name of the account
that connects to the mail server.</para></listitem>
<listitem><para><literal>password</literal>. The password for the user
specified above.</para></listitem>
<listitem><para><literal>startTLS</literal>. Whether startTLS should be
used to secure the connection.</para></listitem>
</itemizedlist>
</listitem>
<listitem><para><literal>history</literal>. Determines the history level
that should be used for the Activiti engine. For more information, see
<link linkend="activiti-history-level">Configuring the Activiti History
Level</link>.</para></listitem>
</itemizedlist>
<section xml:id="activiti-history-level">
<title>Configuring the Activiti History Level</title>
<para>The Activiti history level determines how much historical information
is retained when workflows are executed. You can configure the history
level by setting the <literal>history</literal> property in the
<screen>"history" : "audit"</screen>
<itemizedlist>
<para>The following history levels can be configured:</para>
<listitem>
<para><literal>none</literal>. No history archiving is done. This level
results in the best performance for workflow execution, but no historical
information is available.</para>
</listitem>
<listitem>
<para><literal>activity</literal>. Archives all process instances and
activity instances. No details are archived.</para>
</listitem>
<listitem>
<para><literal>audit</literal>. This is the default level. All process
instances, activity instances and submitted form properties are archived
so that all user interaction through forms is traceable and can be
audited.</para>
</listitem>
<listitem>
<para><literal>full</literal>. This is the highest level of history
archiving and has the greatest performance impact. This history level
stores all information as in the audit level as well as any process
variable updates.</para>
</listitem>
</itemizedlist>
</section>
</section>
<section xml:id="defining-activiti-workflows">
<title>Defining Activiti Workflows</title>
<para>The following section outlines the process to follow when you create
an Activiti workflow for OpenIDM. Before you start creating workflows, you
must configure the Activiti engine, as described in <link
linkend="configuring-activiti-engine">Configuring the Activiti Engine</link>.
</para>
<orderedlist>
<listitem><para>Define your workflow in a text file, either using an editor,
such as Activiti Eclipse BPMN 2.0 Designer, or a simple text editor.</para>
</listitem>
<listitem><para>Package the workflow definition file as a
<literal>.bar</literal> file (Business Archive File). If you are using
Eclipse to define the workflow, a <literal>.bar</literal> file is created
when you select "Create deployment artifacts". Essentially, a
<literal>.bar</literal> file is the same as a <literal>.zip</literal> file,
but with the <literal>.bar</literal> extension.</para></listitem>
<listitem><para>Copy the <literal>.bar</literal> file to the
<listitem><para>Invoke the workflow using a script (in
For more information, see <link linkend="invoking-activiti-workflows">
Invoking Activiti Workflows</link>.</para></listitem>
<listitem><para>You can also schedule the workflow to be invoked repeatedly,
or at a future time. For more information, see the
<link xlink:href="integrators-guide#appendix-scheduling"
Reference</citetitle></link></para></listitem><!-- TO DO Add a new section
that specifically describes scheduling workflows -->
</orderedlist>
</section>
<section xml:id="invoking-activiti-workflows">
<title>Invoking Activiti Workflows</title>
<para>You can invoke workflows and business processes from any trigger point
within OpenIDM, including reacting to situations discovered during
reconciliation. Workflows can be invoked from script files, using the
interface.</para>
<para>The following sample script extract shows how to invoke a workflow from
a script file:</para>
<programlisting language="javascript">
/*
* Calling 'myWorkflow' workflow
*/
var params = {
"foo" : "bar"
};
{"key":"myWorkflow", "keyrequired" : params});
</programlisting>
<para>You can invoke the same workflow from the REST interface by sending
the following REST call to OpenIDM:</para>
<screen>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--request POST
--data '{"key":"myWorkflow", "foo":"bar"}'
</screen>
</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>
<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 xml:id="figure-bpmn-email-notification">
<alt>Email notification process</alt>
<imageobject>
</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><serviceTask></literal>
tag shown in the following listing.</para>
<programlisting language="xml">
<?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:activiti="http://activiti.org/bpmn"
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"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="EmailNotification" name="emailNotification">
<documentation>Simple Email Notification Task</documentation>
<startEvent id="startevent1" name="Start"></startEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1"
targetRef="mailtask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" name="" sourceRef="mailtask1"
targetRef="endevent1"></sequenceFlow>
<serviceTask id="mailtask1" name="Email Notification"
activiti:type="mail">
<extensionElements>
<activiti:field name="to" expression="${toEmail}"
></activiti:field>
<activiti:field name="from" expression="no-reply@forgerock.com"
></activiti:field>
<activiti:field name="subject" expression="Simple Email Notification"
></activiti:field>
<activiti:field name="text">
<activiti:expression><![CDATA[Here is a simple Email Notification
from ${fromSender}.]]></activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_EmailNotification">
<bpmndi:BPMNPlane bpmnElement="EmailNotification"
id="BPMNPlane_EmailNotification">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35" width="35" x="170" y="250"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35" width="35" x="410" y="250"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="mailtask1" id="BPMNShape_mailtask1">
<omgdc:Bounds height="55" width="105" x="250" y="240"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="205" y="267"></omgdi:waypoint>
<omgdi:waypoint x="250" y="267"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="355" y="267"></omgdi:waypoint>
<omgdi:waypoint x="410" y="267"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions></programlisting>
<para>In Eclipse, select the project, then right click and select
Create deployment artifacts to generate the components and package them
directory.</para>
<para>After you deploy the .bar, create a script named <filename>
invokes the workflow.</para>
<programlisting language="javascript">
/*
* Calling 'EmailNotification' workflow
*/
var params = {
"fromSender" : "noreply@openidm",
"toEmail" : "jdoe@example.com"
};
{"key":"emailNotification", "keyrequired" : params});
</programlisting>
<para>You can also invoke the workflow over the REST interface with the
following REST command:</para>
<screen width="100"><?dbfo pgwide="1"?>$ curl
--header "X-OpenIDM-Username: openidm-admin"
--header "X-OpenIDM-Password: openidm-admin"
--data '{"key":"emailNotification", "fromSender":"noreply@openidm", "toEmail":"jdoe@example.com"}'
--request POST
</screen>
<para>To schedule the workflow to be invoked regularly, create a schedule
configuration object named
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>
<!-- TODO: Failing to get this to work for now. Observing these log messages:
INFO: Processing resource EmailNotification/EmailNotification.bpmn20.xml
INFO: XMLSchema currently not supported as typeLanguage
INFO: XPath currently not supported as expressionLanguage
-->
</section>
<!-- Add Matthias's simpler example when I've tested it
<section xml:id="example-activiti-list-users-flow">
<title>Example User Add Workflow</title>
<itemizedlist>
<para>This example defines a workflow that does the following:</para>
<listitem><para>Lists all users</para>
</listitem>
<listitem><para>Creates a new user</para>
</listitem>
<listitem><para>Reads the new user</para>
</listitem>
<listitem><para>Updates the new user</para>
</listitem>
<listitem><para>Deletes the new user</para>
</listitem>
<listitem><para>Lists all users again</para>
</listitem>
</itemizedlist>
<programlisting language="xml">
<?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:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="process2" name="process2">
<startEvent id="startevent1" name="Start"></startEvent>
<scriptTask id="scripttask2" name="Read Users" scriptFormat="groovy">
<script><![CDATA[currentUsers = openidm.query('managed/user', ['_queryId':'query-all-ids'])
out:println 'current users in the repository: 'currentUsers]]></script>
</scriptTask>
<scriptTask id="scripttask3" name="Create Dagobert" scriptFormat="groovy">
<script><![CDATA[def user = [ 'userName':'daduck',
'givenName':'Donald',
'familyName':'Duck1',
'email':['daduck@example.com'] ]
openidm.create('managed/user/' + _id, user)]]></script>
</scriptTask>
<scriptTask id="scripttask4" name="Read Dagobert" scriptFormat="groovy">
<script><![CDATA[user2 = openidm.read("managed/user/"+ _id)
out:println 'first read of user2: ' + user2]]></script>
</scriptTask>
<scriptTask id="scripttask1" name="Update Dagobert" scriptFormat="groovy">
<script><![CDATA[
user3 = openidm.read("managed/user/"+ _id)
out:println 'Read into user3 before update : ' + user3
revision = user3['_rev'].toString()
user3['givenName'] = new_givenName
openidm.update("managed/user/"+ _id, revision, user3)]]></script>
</scriptTask>
<scriptTask id="scripttask5" name="Read Dagobert 2" scriptFormat="groovy">
<script><![CDATA[user2 = openidm.read("managed/user/"+ _id)
out:println 'Second read of user2: ' + user2
user4 = openidm.read("managed/user/"+ _id)
out:println 'First read of user4: ' + user4]]></script>
</scriptTask>
<scriptTask id="scripttask6" name="Delete Dagobert" scriptFormat="groovy">
<script><![CDATA[user5 = openidm.read("managed/user/"+ _id)
revision = user5['_rev'].toString()
openidm.delete("managed/user/"+ _id, revision)]]></script>
</scriptTask>
<scriptTask id="scripttask7" name="Read Users" scriptFormat="groovy">
<script><![CDATA[currentUsers = openidm.query('managed/user', ['_queryId':'query-all-ids'])
out:println 'current users in the repository: 'currentUsers]]></script>
</scriptTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="scripttask2"></sequenceFlow>
<sequenceFlow id="flow4" name="" sourceRef="scripttask2" targetRef="scripttask3"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="scripttask3" targetRef="scripttask4"></sequenceFlow>
<sequenceFlow id="flow5" name="" sourceRef="scripttask4" targetRef="scripttask1"></sequenceFlow>
<sequenceFlow id="flow6" name="" sourceRef="scripttask1" targetRef="scripttask5"></sequenceFlow>
<sequenceFlow id="flow7" name="" sourceRef="scripttask5" targetRef="scripttask6"></sequenceFlow>
<sequenceFlow id="flow8" name="" sourceRef="scripttask6" targetRef="scripttask7"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="scripttask7" targetRef="endevent1"></sequenceFlow>
</process>
</programlisting>
</section>
-->
<section xml:id="example-activiti-recon-sunset-flow">
<title>Example Sunset Workflow Triggered By Reconciliation</title>
<para>You can use OpenIDM to manage deferred deprovisioning and disabling
of user accounts. For example, when an external consultant's contract
expires or is up for renewal, you can use the workflow capabilities of
OpenIDM to manage this scenario.</para>
<para>In the following example, the authoritative source is a CSV file. The
target resource is an XML file. A mapping is configured to reconcile accounts
between the CSV file and the XML file (no changes are made to the accounts in
the internal OpenIDM repository). Initially, the user exists in the XML file
only. The user is then added to the CSV file, and a reconciliation process is
run. As a result of the correlation query, the syncronization engine finds the
user in the XML file, and the situation therefore results in a
<literal>FOUND</literal> response.</para>
<orderedlist><para>To replicate the example, follow these steps:</para>
<listitem><para>Set up a CSV file connector to connect to the
authoritative CSV source file. The file contains the following entry:
</para>
<!-- 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="92"><?dbfo pgwide="1"?>
firstName,uid,"lastName","email","employeeNumber",password,"sunrise","sunset","active"
"Darth","DDOE","Doe","doe@example.com","123456","Z29vZA==",
"2012-06-30T00:00:00Z","2012-12-23T00:00:00Z","TRUE"</programlisting>
<para>The authoritative source contains the field,
<literal>sunset</literal>, whose value is a date and time string that
references a time in the future, in this example
<literal>2012-12-23T00:00:00Z</literal>.</para>
<para>For information about configuring the CSV file connector, see
xlink:href="integrators-guide#csv-file-connector">
<citetitle>CSV File Connector</citetitle></link>.</para>
</listitem>
<listitem>
<para>Set up a simple XML connector to replicate the fields in the
authoritative CSV source on a one to one basis.</para>
</listitem>
</orderedlist>
<para></para> <!-- TODO: Howto steps or link to howto -->
<para>Define a Sunset Workflow using Activiti Eclipse BPMN 2.0
Designer.</para>
<mediaobject xml:id="figure-bpmn-sunset-flow">
<alt>Sunset workflow process</alt>
<imageobject>
</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">
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
<process id="sunset" name="sunset usecase">
<startEvent id="start" />
<sequenceFlow sourceRef="start" targetRef="saveInvokeContext"/>
<scriptTask id="saveInvokeContext" scriptFormat="groovy"
activiti:returnVariable="invokecontext">
<script>
<![CDATA[
invokecontext =
]]>
</script>
</scriptTask>
<sequenceFlow sourceRef="saveInvokeContext"
targetRef="timerintermediatecatchevent1"/>
<intermediateCatchEvent id="timerintermediatecatchevent1"
name="TimerCatchEvent">
<extensionElements>
<activiti:field name="t" expression="${time}"
></activiti:field>
</extensionElements>
<timerEventDefinition>
<timeDate >${time}</timeDate>
</timerEventDefinition>
</intermediateCatchEvent>
<sequenceFlow sourceRef="timerintermediatecatchevent1"
targetRef="idmCall"/>
<serviceTask id="idmCall" activiti:delegateExpression="${openidm}"
activiti:async="true">
<extensionElements>
<activiti:field name="userName" expression="${userName}"
></activiti:field>
<activiti:field name="system" expression="${system}"
></activiti:field>
</extensionElements>
</serviceTask>
<sequenceFlow sourceRef="idmCall" targetRef="readUserAccount"/>
<scriptTask id="readUserAccount" scriptFormat="groovy"
activiti:returnVariable="user" >
<script>
<![CDATA[
out:println '********Sunset date for user ' + userName
user = openidm.read(system + userName);
]]>
</script>
</scriptTask>
<sequenceFlow sourceRef="readUserAccount" targetRef="sendMail"/>
<serviceTask id="sendMail" name="Email Notification"
activiti:type="mail">
<extensionElements>
<activiti:field name="to" expression="${toEmail}"
></activiti:field>
<activiti:field name="from" expression="no-reply@forgerock.com"
></activiti:field>
<activiti:field name="subject" expression="Disabling User"
></activiti:field>
<activiti:field name="text">
<activiti:expression><![CDATA[
The following user has been disabled:
${user}]]>
</activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
<sequenceFlow sourceRef="sendMail" targetRef="disableAccount"/>
<scriptTask id="disableAccount" scriptFormat="groovy">
<script>
<![CDATA[
out:println '********Disabling user account ' + userName
user['__ENABLE__'] = false
openidm.update(system + userName, null, user)
]]>
</script>
</scriptTask>
<sequenceFlow sourceRef="disableAccount" targetRef="end"/>
<endEvent id="end"/>
</process>
</definitions></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
invoked when reconciliation encounters a <literal>FOUND</literal>
situation.</para>
<programlisting language="javascript">
/*
* Calling 'sunrise' workflow
*/
var params = {
"userName" : target.__UID__,
"time" : source.sunset,
"toEmail" : "manager@corp.org",
"fromSender" : "noreply@openidm"
};
{"key":"sunrise", "keyrequired" : params});
</programlisting>
<para>Add the following schedule to
installed OpenIDM to invoke the script.</para>
<programlisting language="javascript">
{
"enabled" : true,
"type" : "cron",
"schedule" : "0 08 16 * * ?",
"invokeService" : "sync",
"invokeContext" : {
"action" : "reconcile",
"mapping" : {
"name" : "CSV_XML",
"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>
</section>
</section>
</chapter>