[jbpm-commits] JBoss JBPM SVN: r4875 - in jbpm4/trunk/modules: examples/src/test/java/org/jbpm/examples/async and 17 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Sat May 23 06:38:46 EDT 2009


Author: tom.baeyens at jboss.com
Date: 2009-05-23 06:38:45 -0400 (Sat, 23 May 2009)
New Revision: 4875

Added:
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/AsyncEventListenerTest.java
   jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/
   jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/process.jpdl.xml
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListenerMessage.java
Modified:
   jbpm4/trunk/modules/api/src/main/resources/jpdl-4.0.xsd
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/activity/Application.java
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/Application.java
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/AsyncForkTest.java
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/hql/HqlTest.java
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/inline/InlineMailTest.java
   jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/template/TemplateMailTest.java
   jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/inline/process.jpdl.xml
   jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/template/process.jpdl.xml
   jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinActivity.java
   jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinBinding.java
   jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/MailActivity.java
   jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java
   jbpm4/trunk/modules/jpdl/src/main/resources/jbpm.jpdl.eventlisteners.xml
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ActivityImpl.java
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListener.java
   jbpm4/trunk/modules/pvm/src/main/resources/jbpm.execution.hbm.xml
   jbpm4/trunk/modules/userguide/src/main/docbook/en/modules/ch05-Jpdl.xml
Log:
JBPM-2256 async event listeners

Modified: jbpm4/trunk/modules/api/src/main/resources/jpdl-4.0.xsd
===================================================================
--- jbpm4/trunk/modules/api/src/main/resources/jpdl-4.0.xsd	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/api/src/main/resources/jpdl-4.0.xsd	2009-05-23 10:38:45 UTC (rev 4875)
@@ -239,6 +239,18 @@
             <element ref="tns:transition" minOccurs="0" maxOccurs="unbounded" />
           </sequence>
           <attributeGroup ref="tns:activityAttributes" />
+          <attribute name="multiplicity" type="int" />
+          <attribute name="lockmode" default="upgrade">
+            <simpleType>
+              <restriction base="string">
+                <enumeration value="none"/>
+                <enumeration value="read"/>
+                <enumeration value="upgrade"/>
+                <enumeration value="upgrade_nowait"/>
+                <enumeration value="write"/>
+              </restriction>
+            </simpleType>
+          </attribute>
         </complexType>
       </element>
 

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/activity/Application.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/activity/Application.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/activity/Application.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -28,8 +28,10 @@
 public class Application {
 
   public void generatePdf() {
+    // assume long automatic calculations here
   }
 
   public void calculatePrimes() {
+    // assume long automatic calculations here
   }
 }

Added: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/AsyncEventListenerTest.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/AsyncEventListenerTest.java	                        (rev 0)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/AsyncEventListenerTest.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.examples.async.eventlistener;
+
+import java.io.IOException;
+
+import javax.mail.MessagingException;
+
+import org.jbpm.api.ProcessInstance;
+import org.jbpm.api.job.Job;
+import org.jbpm.test.JbpmTestCase;
+import org.subethamail.wiser.Wiser;
+
+
+/**
+ * @author Tom Baeyens
+ */
+public class AsyncEventListenerTest extends JbpmTestCase {
+
+  long deploymentDbid;
+  Wiser wiser = null;
+
+  protected void setUp() throws Exception {
+    super.setUp();
+    
+    deploymentDbid = repositoryService.createDeployment()
+        .addResourceFromClasspath("org/jbpm/examples/async/eventlistener/process.jpdl.xml")
+        .deploy();
+    
+    // start mail server
+    wiser = new Wiser();
+    wiser.setPort(2525);
+    wiser.start();
+  }
+
+  protected void tearDown() throws Exception {
+    wiser.stop();
+
+    repositoryService.deleteDeploymentCascade(deploymentDbid);
+
+    super.tearDown();
+  }
+
+  public void testAsyncEventListener() throws Exception{
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncEventListener");
+    String processInstanceId = processInstance.getId();
+    
+    assertEquals(0, wiser.getMessages().size());
+    
+    Job job = managementService.createJobQuery()
+      .processInstanceId(processInstanceId)
+      .uniqueResult();
+    managementService.executeJob(job.getDbid());
+    
+    assertEquals(1, wiser.getMessages().size());
+  }
+}


Property changes on: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/eventlistener/AsyncEventListenerTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/Application.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/Application.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/Application.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -28,8 +28,10 @@
 public class Application {
 
   public void shipGoods() {
+    // assume automatic calculations here
   }
 
   public void sendBill() {
+    // assume automatic calculations here
   }
 }

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/AsyncForkTest.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/AsyncForkTest.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/async/fork/AsyncForkTest.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -21,6 +21,7 @@
  */
 package org.jbpm.examples.async.fork;
 
+import java.util.Date;
 import java.util.List;
 
 import org.jbpm.api.ProcessInstance;
@@ -57,8 +58,6 @@
       .processInstanceId(processInstanceId)
       .list();
 
-    System.out.println(jobs);
-
     assertEquals(2, jobs.size());
     
     Job job = jobs.get(0);
@@ -68,5 +67,13 @@
     job = jobs.get(1);
 
     managementService.executeJob(job.getDbid());
+
+    Date endTime = historyService
+      .createHistoryProcessInstanceQuery()
+      .processInstanceId(processInstance.getId())
+      .uniqueResult()
+      .getEndTime();
+
+    assertNotNull(endTime);
   }
 }

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/hql/HqlTest.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/hql/HqlTest.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/hql/HqlTest.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -25,7 +25,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.jbpm.api.Execution;
+import org.jbpm.api.ProcessInstance;
 import org.jbpm.api.task.Task;
 import org.jbpm.test.JbpmTestCase;
 
@@ -75,17 +75,17 @@
   }
 
   public void testHql() {
-    Execution execution = executionService.startProcessInstanceByKey("Hql");
-    String executionId = execution.getId();
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("Hql");
+    String processInstanceId = processInstance.getId();
 
     Set<String> expectedTaskNames = new HashSet<String>();
     expectedTaskNames.add("dishes");
     expectedTaskNames.add("iron");
-    Collection<String> taskNames = (Collection<String>) executionService.getVariable(executionId, "tasknames with i");
+    Collection<String> taskNames = (Collection<String>) executionService.getVariable(processInstanceId, "tasknames with i");
     taskNames = new HashSet<String>(taskNames);
     assertEquals(expectedTaskNames, taskNames);
 
-    Object activities = executionService.getVariable(executionId, "tasks");
+    Object activities = executionService.getVariable(processInstanceId, "tasks");
     assertEquals("3", activities.toString());
   }
 }

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/inline/InlineMailTest.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/inline/InlineMailTest.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/inline/InlineMailTest.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -44,6 +44,8 @@
  */
 public class InlineMailTest extends JbpmTestCase {
 
+  Wiser wiser = null;
+
   protected void setUp() throws Exception {
     super.setUp();
 
@@ -62,9 +64,16 @@
     identityService.createMembership("obrien", "innerparty");
     identityService.createMembership("charr", "thinkpol");
     identityService.createMembership("obrien", "thinkpol");
+    
+    // start mail server
+    wiser = new Wiser();
+    wiser.setPort(2525);
+    wiser.start();
   }
 
   protected void tearDown() throws Exception {
+    wiser.stop();
+
     // delete actors
     identityService.deleteGroup("thinkpol");
     identityService.deleteGroup("innerparty");
@@ -76,54 +85,46 @@
   }
 
   public void testInlineMail() throws MessagingException, IOException {
-    Wiser wiser = new Wiser();
-    wiser.setPort(2525);
-    wiser.start();
-    try {
-      // prepare dynamic values
-      String newspaper = "times";
-      Calendar calendar = Calendar.getInstance();
-      calendar.clear();
-      calendar.set(1983, Calendar.DECEMBER, 3);
-      Date date = calendar.getTime();
-      // assemble variables
-      Map<String, Object> variables = new HashMap<String, Object>();
-      variables.put("newspaper", newspaper);
-      variables.put("date", date);
-      // start process instance
-      executionService.startProcessInstanceByKey("InlineMail", variables);
+    // prepare dynamic values
+    String newspaper = "times";
+    Calendar calendar = Calendar.getInstance();
+    calendar.clear();
+    calendar.set(1983, Calendar.DECEMBER, 3);
+    Date date = calendar.getTime();
+    // assemble variables
+    Map<String, Object> variables = new HashMap<String, Object>();
+    variables.put("newspaper", newspaper);
+    variables.put("date", date);
+    // start process instance
+    executionService.startProcessInstanceByKey("InlineMail", variables);
 
-      // examine produced message
-      List<WiserMessage> wisMessages = wiser.getMessages();
-      // winston, bb, innerparty(obrien), thinkpol(charr, obrien)
-      assertEquals(5, wisMessages.size());
+    // examine produced message
+    List<WiserMessage> wisMessages = wiser.getMessages();
+    // winston, bb, innerparty(obrien), thinkpol(charr, obrien)
+    assertEquals(5, wisMessages.size());
 
-      for (WiserMessage wisMessage : wisMessages) {
-        Message message = wisMessage.getMimeMessage();
-        // from
-        Address[] from = message.getFrom();
-        assertEquals(1, from.length);
-        assertEquals("noreply at jbpm.org", from[0].toString());
-        // to
-        Address[] expectedTo = InternetAddress.parse("winston at minitrue");
-        Address[] to = message.getRecipients(RecipientType.TO);
-        assert Arrays.equals(expectedTo, to) : Arrays.asList(to);
-        // cc
-        Address[] expectedCc = InternetAddress.parse("bb at oceania, obrien at miniluv");
-        System.out.println(Arrays.toString(expectedCc));
-        Address[] cc = message.getRecipients(RecipientType.CC);
-        System.out.println(Arrays.toString(cc));
-        assert Arrays.equals(expectedCc, cc) : Arrays.asList(cc);
-        // bcc - recipients undisclosed
-        assertNull(message.getRecipients(RecipientType.BCC));
-        // subject
-        assertEquals("rectify " + newspaper, message.getSubject());
-        // text
-        assertTextPresent(newspaper + ' ' + date + " reporting bb dayorder", (String) message.getContent());
-      }
+    for (WiserMessage wisMessage : wisMessages) {
+      Message message = wisMessage.getMimeMessage();
+      // from
+      Address[] from = message.getFrom();
+      assertEquals(1, from.length);
+      assertEquals("noreply at jbpm.org", from[0].toString());
+      // to
+      Address[] expectedTo = InternetAddress.parse("winston at minitrue");
+      Address[] to = message.getRecipients(RecipientType.TO);
+      assert Arrays.equals(expectedTo, to) : Arrays.asList(to);
+      // cc
+      Address[] expectedCc = InternetAddress.parse("bb at oceania, obrien at miniluv");
+      System.out.println(Arrays.toString(expectedCc));
+      Address[] cc = message.getRecipients(RecipientType.CC);
+      System.out.println(Arrays.toString(cc));
+      assert Arrays.equals(expectedCc, cc) : Arrays.asList(cc);
+      // bcc - recipients undisclosed
+      assertNull(message.getRecipients(RecipientType.BCC));
+      // subject
+      assertEquals("rectify " + newspaper, message.getSubject());
+      // text
+      assertTextPresent(newspaper + ' ' + date + " reporting bb dayorder", (String) message.getContent());
     }
-    finally {
-      wiser.stop();
-    }
   }
 }

Modified: jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/template/TemplateMailTest.java
===================================================================
--- jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/template/TemplateMailTest.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/java/org/jbpm/examples/mail/template/TemplateMailTest.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -44,6 +44,8 @@
  */
 public class TemplateMailTest extends JbpmTestCase {
 
+  Wiser wiser = null;
+
   protected void setUp() throws Exception {
     super.setUp();
 
@@ -62,9 +64,16 @@
     identityService.createMembership("obrien", "innerparty");
     identityService.createMembership("charr", "thinkpol");
     identityService.createMembership("obrien", "thinkpol");
+    
+    // start mail server
+    wiser = new Wiser();
+    wiser.setPort(2525);
+    wiser.start();
   }
 
   protected void tearDown() throws Exception {
+    wiser.stop();
+    
     // delete actors
     identityService.deleteGroup("thinkpol");
     identityService.deleteGroup("innerparty");
@@ -76,57 +85,49 @@
   }
 
   public void testTemplateMail() throws MessagingException, IOException {
-    Wiser wiser = new Wiser();
-    wiser.setPort(2525);
-    wiser.start();
-    try {
-      // prepare dynamic values
-      String addressee = "winston at minitrue";
-      String newspaper = "times";
-      Calendar calendar = Calendar.getInstance();
-      calendar.clear();
-      calendar.set(1983, Calendar.DECEMBER, 3);
-      Date date = calendar.getTime();
-      String details = "reporting bb dayorder doubleplusungood refs unpersons rewrite "
-          + "fullwise upsub antefiling";
-      // assemble variables
-      Map<String, Object> variables = new HashMap<String, Object>();
-      variables.put("addressee", addressee);
-      variables.put("newspaper", newspaper);
-      variables.put("date", date);
-      variables.put("details", details);
-      // start process instance
-      executionService.startProcessInstanceByKey("TemplateMail", variables);
+    // prepare dynamic values
+    String addressee = "winston at minitrue";
+    String newspaper = "times";
+    Calendar calendar = Calendar.getInstance();
+    calendar.clear();
+    calendar.set(1983, Calendar.DECEMBER, 3);
+    Date date = calendar.getTime();
+    String details = "reporting bb dayorder doubleplusungood refs unpersons rewrite "
+        + "fullwise upsub antefiling";
+    // assemble variables
+    Map<String, Object> variables = new HashMap<String, Object>();
+    variables.put("addressee", addressee);
+    variables.put("newspaper", newspaper);
+    variables.put("date", date);
+    variables.put("details", details);
+    // start process instance
+    executionService.startProcessInstanceByKey("TemplateMail", variables);
 
-      // examine produced messages
-      List<WiserMessage> wisMessages = wiser.getMessages();
-      // winston, bb, innerparty(obrien), thinkpol(charr, obrien)
-      assertEquals(5, wisMessages.size());
+    // examine produced messages
+    List<WiserMessage> wisMessages = wiser.getMessages();
+    // winston, bb, innerparty(obrien), thinkpol(charr, obrien)
+    assertEquals(5, wisMessages.size());
 
-      for (WiserMessage wisMessage : wisMessages) {
-        Message message = wisMessage.getMimeMessage();
-        // from
-        Address[] from = message.getFrom();
-        assertEquals(1, from.length);
-        assertEquals("noreply at jbpm.org", from[0].toString());
-        // to
-        Address[] expectedTo = InternetAddress.parse(addressee);
-        Address[] to = message.getRecipients(RecipientType.TO);
-        assert Arrays.equals(expectedTo, to) : Arrays.asList(to);
-        // cc
-        Address[] expectedCc = InternetAddress.parse("bb at oceania, obrien at miniluv");
-        Address[] cc = message.getRecipients(RecipientType.CC);
-        assert Arrays.equals(expectedCc, cc) : Arrays.asList(cc);
-        // bcc - recipients undisclosed
-        assertNull(message.getRecipients(RecipientType.BCC));
-        // subject
-        assertEquals("rectify " + newspaper, message.getSubject());
-        // text
-        assertEquals(newspaper + ' ' + date + ' ' + details, (String) message.getContent());
-      }
+    for (WiserMessage wisMessage : wisMessages) {
+      Message message = wisMessage.getMimeMessage();
+      // from
+      Address[] from = message.getFrom();
+      assertEquals(1, from.length);
+      assertEquals("noreply at jbpm.org", from[0].toString());
+      // to
+      Address[] expectedTo = InternetAddress.parse(addressee);
+      Address[] to = message.getRecipients(RecipientType.TO);
+      assert Arrays.equals(expectedTo, to) : Arrays.asList(to);
+      // cc
+      Address[] expectedCc = InternetAddress.parse("bb at oceania, obrien at miniluv");
+      Address[] cc = message.getRecipients(RecipientType.CC);
+      assert Arrays.equals(expectedCc, cc) : Arrays.asList(cc);
+      // bcc - recipients undisclosed
+      assertNull(message.getRecipients(RecipientType.BCC));
+      // subject
+      assertEquals("rectify " + newspaper, message.getSubject());
+      // text
+      assertEquals(newspaper + ' ' + date + ' ' + details, (String) message.getContent());
     }
-    finally {
-      wiser.stop();
-    }
   }
 }

Added: jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/process.jpdl.xml
===================================================================
--- jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/process.jpdl.xml	                        (rev 0)
+++ jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/process.jpdl.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<process name="AsyncEventListener" xmlns="http://jbpm.org/4.0/jpdl">
+
+  <start name="start" g="22,69,80,40">
+    <transition to="wait">
+      <mail continue="async">
+        <to addresses="you at yourcompany" />
+        <subject>hello</subject>
+        <text>hello</text>
+      </mail>
+    </transition>
+  </start>
+
+  <state name="wait"/>
+
+</process>


Property changes on: jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/async/eventlistener/process.jpdl.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/inline/process.jpdl.xml
===================================================================
--- jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/inline/process.jpdl.xml	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/inline/process.jpdl.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -2,11 +2,11 @@
 
 <process name="InlineMail">
 
-  <start>
+  <start g="20,25,80,40">
     <transition to="send rectify note" />
   </start>
 
-  <mail name="send rectify note" language="juel">
+  <mail name="send rectify note" language="juel" g="99,25,115,45">
     <to addresses="winston at minitrue" />
     <cc users="bb" groups="innerparty" />
     <bcc groups="thinkpol" />
@@ -27,6 +27,6 @@
     <transition to="end" />
   </mail>
 
-  <state name="end" />
+  <state name="end" g="240,25,98,45"/>
 
 </process>
\ No newline at end of file

Modified: jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/template/process.jpdl.xml
===================================================================
--- jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/template/process.jpdl.xml	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/examples/src/test/resources/org/jbpm/examples/mail/template/process.jpdl.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -1,15 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<process name="TemplateMail">
-
-  <start>
-    <transition to="send rectify note" />
-  </start>
-
-  <mail name="send rectify note" template="rectify-template">
-    <transition to="end" />
-  </mail>
-
-  <state name="end" />
-
+<process name="TemplateMail" xmlns="http://jbpm.org/4.0/jpdl">
+
+  <start g="20,25,80,40">
+    <transition to="send rectify note"/>
+  </start>
+
+  <mail name="send rectify note" 
+        template="rectify-template" 
+        g="99,25,115,45">
+    <transition to="end"/>
+  </mail>
+
+  <state name="end" g="240,25,98,45"/>
+
 </process>
\ No newline at end of file

Modified: jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinActivity.java
===================================================================
--- jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinActivity.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinActivity.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -22,14 +22,15 @@
 package org.jbpm.jpdl.internal.activity;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
+import org.hibernate.LockMode;
+import org.hibernate.Session;
 import org.jbpm.api.Execution;
 import org.jbpm.api.JbpmException;
 import org.jbpm.api.activity.ActivityExecution;
+import org.jbpm.api.env.Environment;
 import org.jbpm.api.model.Activity;
-import org.jbpm.api.model.OpenExecution;
 import org.jbpm.api.model.Transition;
 import org.jbpm.pvm.internal.model.ExecutionImpl;
 
@@ -42,6 +43,7 @@
   private static final long serialVersionUID = 1L;
   
   int multiplicity = -1;
+  LockMode lockMode = LockMode.UPGRADE;
 
   public void execute(ActivityExecution execution) {
     execute((ExecutionImpl)execution);
@@ -60,6 +62,10 @@
       execution.take(transition);
       
     } else if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
+      
+      // force version increment in the parent execution
+      Session session = Environment.getFromCurrent(Session.class);
+      session.lock(execution.getParent(), lockMode);
 
       execution.setState(Execution.STATE_INACTIVE_JOIN);
       execution.waitForSignal();
@@ -118,4 +124,11 @@
       joinedExecution.end();
     }
   }
+
+  public void setMultiplicity(int multiplicity) {
+    this.multiplicity = multiplicity;
+  }
+  public void setLockMode(LockMode lockMode) {
+    this.lockMode = lockMode;
+  }
 }

Modified: jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinBinding.java
===================================================================
--- jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinBinding.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/JoinBinding.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -21,6 +21,7 @@
  */
 package org.jbpm.jpdl.internal.activity;
 
+import org.hibernate.LockMode;
 import org.jbpm.pvm.internal.xml.Parse;
 import org.jbpm.pvm.internal.xml.Parser;
 import org.w3c.dom.Element;
@@ -36,7 +37,29 @@
   }
 
   public Object parse(Element element, Parse parse, Parser parser) {
-    return new JoinActivity();
+    JoinActivity joinActivity = new JoinActivity();
+    
+    if (element.hasAttribute("multiplicicty")) {
+      String multiplicictyText = element.getAttribute("multiplicicty");
+      try {
+        int multiplicity = Integer.parseInt(multiplicictyText);
+        joinActivity.setMultiplicity(multiplicity);
+      } catch (NumberFormatException e) {
+        parse.addProblem("multiplicity "+multiplicictyText+" is not a valid integer", element);
+      }
+    }
+
+    if (element.hasAttribute("lockmode")) {
+      String lockModeText = element.getAttribute("lockmode");
+      LockMode lockMode = LockMode.parse(lockModeText.toUpperCase());
+      if (lockMode==null) {
+        parse.addProblem("lockmode "+lockModeText+" is not a valid lock mode", element);
+      } else {
+        joinActivity.setLockMode(lockMode);
+      }
+    }
+
+    return joinActivity;
   }
 
 }

Modified: jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/MailActivity.java
===================================================================
--- jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/MailActivity.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/activity/MailActivity.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -39,7 +39,6 @@
 
   private static final long serialVersionUID = 1L;
 
-  @Override
   void perform(OpenExecution execution) throws Exception {
     Collection<Message> messages = mailProducer.produce(execution);
     Environment.getFromCurrent(MailSession.class).send(messages);

Modified: jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java
===================================================================
--- jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -250,10 +250,12 @@
             parseTransitions(nestedElement, activity, parse);
 
             String continuationText = XmlUtil.attribute(nestedElement, "continue");
-            if ("async".equals(continuationText)) {
-              activity.setContinuation(Continuation.ASYNCHRONOUS);
-            } else if ("exclusive".equals(continuationText)) {
-              activity.setContinuation(Continuation.EXCLUSIVE);
+            if (continuationText!=null) {
+              if ("async".equals(continuationText)) {
+                activity.setContinuation(Continuation.ASYNCHRONOUS);
+              } else if ("exclusive".equals(continuationText)) {
+                activity.setContinuation(Continuation.EXCLUSIVE);
+              }
             }
 
             ActivityBehaviour activityBehaviour = (ActivityBehaviour) activityBinding.parse(nestedElement, parse, this);
@@ -338,18 +340,20 @@
     }
   }
 
-  public void parseOnEvent(Element element, ObservableElementImpl scopeElement, String eventName, Parse parse) {
+  public void parseOnEvent(Element element, ObservableElementImpl observableElement, String eventName, Parse parse) {
     if (eventName!=null) {
-      EventImpl event = scopeElement.getEvent(eventName);
+      EventImpl event = observableElement.getEvent(eventName);
       if (event==null) {
-        event = scopeElement.createEvent(eventName);
+        event = observableElement.createEvent(eventName);
       }
       
       String continuationText = XmlUtil.attribute(element, "continue");
-      if ("async".equals(continuationText)) {
-        event.setContinuation(Continuation.ASYNCHRONOUS);
-      } else if ("exclusive".equals(continuationText)) {
-        event.setContinuation(Continuation.EXCLUSIVE);
+      if (continuationText!=null) {
+        if ("async".equals(continuationText)) {
+          event.setContinuation(Continuation.ASYNCHRONOUS);
+        } else if ("exclusive".equals(continuationText)) {
+          event.setContinuation(Continuation.EXCLUSIVE);
+        }
       }
 
       for (Element eventListenerElement: XmlUtil.elements(element)) {
@@ -359,15 +363,27 @@
           EventListenerReference eventListenerReference = event.createEventListenerReference(eventListener);
           
           continuationText = XmlUtil.attribute(eventListenerElement, "continue");
-          if ("async".equals(continuationText)) {
-            eventListenerReference.setContinuation(Continuation.ASYNCHRONOUS);
-          } else if ("exclusive".equals(continuationText)) {
-            eventListenerReference.setContinuation(Continuation.EXCLUSIVE);
+          if (continuationText!=null) {
+            if (observableElement instanceof ActivityImpl) {
+              if (observableElement.getName()==null) {
+                parse.addProblem("async continuation on event listener requires activity name", eventListenerElement);
+              }
+            } else if (observableElement instanceof TransitionImpl) {
+              TransitionImpl transition = (TransitionImpl) observableElement;
+              if (transition.getSource().getName()==null) {
+                parse.addProblem("async continuation on event listener requires name in the transition source activity", eventListenerElement);
+              }
+            }
+            if ("async".equals(continuationText)) {
+              eventListenerReference.setContinuation(Continuation.ASYNCHRONOUS);
+            } else if ("exclusive".equals(continuationText)) {
+              eventListenerReference.setContinuation(Continuation.EXCLUSIVE);
+            }
           }
 
         } else {
           String tagName = XmlUtil.getTagLocalName(eventListenerElement);
-          if ( ! ( (scopeElement instanceof TransitionImpl)
+          if ( ! ( (observableElement instanceof TransitionImpl)
                    && ( "condition".equals(tagName)
                         || "timer".equals(tagName)
                       )

Modified: jbpm4/trunk/modules/jpdl/src/main/resources/jbpm.jpdl.eventlisteners.xml
===================================================================
--- jbpm4/trunk/modules/jpdl/src/main/resources/jbpm.jpdl.eventlisteners.xml	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/jpdl/src/main/resources/jbpm.jpdl.eventlisteners.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -5,4 +5,5 @@
   <eventlistener binding="org.jbpm.jpdl.internal.activity.HqlBinding" />
   <eventlistener binding="org.jbpm.jpdl.internal.activity.SqlBinding" />
   <eventlistener binding="org.jbpm.jpdl.internal.activity.ScriptBinding" />
+  <eventlistener binding="org.jbpm.jpdl.internal.activity.MailBinding" />
 </eventlisteners>

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ActivityImpl.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ActivityImpl.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ActivityImpl.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -84,6 +84,7 @@
   public TransitionImpl createOutgoingTransition() {
     // create a new transition
     TransitionImpl transition = new TransitionImpl();
+    transition.setProcessDefinition(processDefinition);
     
     // wire it between the source and destination
     addOutgoingTransition(transition);

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListener.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListener.java	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListener.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -25,7 +25,6 @@
 import org.jbpm.api.model.ObservableElement;
 import org.jbpm.internal.log.Log;
 import org.jbpm.pvm.internal.job.MessageImpl;
-import org.jbpm.pvm.internal.model.ActivityImpl;
 import org.jbpm.pvm.internal.model.EventImpl;
 import org.jbpm.pvm.internal.model.EventListenerReference;
 import org.jbpm.pvm.internal.model.ExecutionImpl;
@@ -82,7 +81,7 @@
   }
 
   public MessageImpl< ? > createAsyncMessage(ExecutionImpl execution) {
-    return null;
+    return new ExecuteEventListenerMessage(execution, observableElement, event, eventListenerReference);
   }
 
   public String toString() {

Added: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListenerMessage.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListenerMessage.java	                        (rev 0)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListenerMessage.java	2009-05-23 10:38:45 UTC (rev 4875)
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.pvm.internal.model.op;
+
+import org.jbpm.api.env.Environment;
+import org.jbpm.api.listener.EventListener;
+import org.jbpm.api.model.ObservableElement;
+import org.jbpm.api.session.RepositorySession;
+import org.jbpm.pvm.internal.job.MessageImpl;
+import org.jbpm.pvm.internal.model.ActivityImpl;
+import org.jbpm.pvm.internal.model.EventImpl;
+import org.jbpm.pvm.internal.model.EventListenerReference;
+import org.jbpm.pvm.internal.model.ExecutionImpl;
+import org.jbpm.pvm.internal.model.ObservableElementImpl;
+import org.jbpm.pvm.internal.model.ProcessDefinitionImpl;
+import org.jbpm.pvm.internal.model.TransitionImpl;
+
+
+/**
+ * @author Tom Baeyens
+ */
+public class ExecuteEventListenerMessage extends MessageImpl<Object> {
+
+  private static final long serialVersionUID = 1L;
+
+  protected String processDefinitionId;
+  protected String activityName;
+  protected Integer transitionIndex;
+  protected String eventName;
+  protected Integer eventListenerIndex;
+  
+  public ExecuteEventListenerMessage() {
+  }
+
+  public ExecuteEventListenerMessage(ExecutionImpl execution, ObservableElement observableElement, EventImpl event, EventListenerReference eventListenerReference) {
+    super(execution);
+    if (observableElement instanceof ProcessDefinitionImpl) {
+      ProcessDefinitionImpl processDefinition = (ProcessDefinitionImpl) observableElement;
+      processDefinitionId = processDefinition.getId(); 
+    } else if (observableElement instanceof ActivityImpl) {
+      ActivityImpl activity = (ActivityImpl) observableElement;
+      processDefinitionId = activity.getProcessDefinition().getId();
+      activityName = activity.getName();
+    } else if (observableElement instanceof TransitionImpl) {
+      TransitionImpl transition = (TransitionImpl) observableElement;
+      processDefinitionId = transition.getProcessDefinition().getId();
+      activityName = transition.getSource().getName();
+      transitionIndex = transition.getSource().getOutgoingTransitions().indexOf(transition);
+    }
+    
+    eventName = event.getName();
+    eventListenerIndex = event.getListenerReferences().indexOf(eventListenerReference);
+  }
+
+  public Object execute(Environment environment) throws Exception {
+    ObservableElementImpl observableElement = null;
+    
+    RepositorySession repositorySession = environment.get(RepositorySession.class);
+    ProcessDefinitionImpl processDefinition = (ProcessDefinitionImpl) repositorySession.findProcessDefinitionById(processDefinitionId);
+    if (activityName!=null) {
+      ActivityImpl activity = processDefinition.findActivity(activityName);
+      if (transitionIndex!=null) {
+        TransitionImpl transition = (TransitionImpl) activity.getOutgoingTransitions().get(transitionIndex);
+        observableElement = transition;
+      } else {
+        observableElement = activity;
+      }
+    } else {
+      observableElement = processDefinition;
+    }
+    
+    EventImpl event = observableElement.getEvent(eventName);
+    EventListenerReference eventListenerReference = event.getListenerReferences().get(eventListenerIndex);
+    EventListener eventListener = eventListenerReference.get();
+    
+    eventListener.notify(execution);
+    
+    return null;
+  }
+}


Property changes on: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/model/op/ExecuteEventListenerMessage.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: jbpm4/trunk/modules/pvm/src/main/resources/jbpm.execution.hbm.xml
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/resources/jbpm.execution.hbm.xml	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/pvm/src/main/resources/jbpm.execution.hbm.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -231,18 +231,21 @@
                  index="IDX_JOB_CMDDESCR"/ -->
 
     <subclass name="org.jbpm.pvm.internal.job.MessageImpl" discriminator-value="Msg">
-      <subclass name="org.jbpm.pvm.internal.model.op.ExecuteActivityMessage" discriminator-value="ExeActivityMsg" />
-      <subclass name="org.jbpm.pvm.internal.model.op.SignalMessage" discriminator-value="SignalMsg">
+      <subclass name="org.jbpm.pvm.internal.model.op.ExecuteActivityMessage" discriminator-value="ExeAct" />
+      <subclass name="org.jbpm.pvm.internal.model.op.SignalMessage" discriminator-value="Signal">
         <property name="signalName" column="SIGNAL_" />
-        <!-- many-to-one name="activity"   
-                     column="NODE_" 
-                     cascade="none"
-                     foreign-key="FK_JOB_NODE"/ -->
       </subclass>
-      <subclass name="org.jbpm.pvm.internal.model.op.TransitionEndActivityMessage" discriminator-value="TrEndActMsg" />
-      <subclass name="org.jbpm.pvm.internal.model.op.TransitionTakeMessage" discriminator-value="TrTakeMsg" />
-      <subclass name="org.jbpm.pvm.internal.model.op.TransitionStartActivityMessage" discriminator-value="TrStartActMsg" />
-      <subclass name="org.jbpm.pvm.internal.job.CommandMessage" discriminator-value="CmdMsg" />
+      <subclass name="org.jbpm.pvm.internal.model.op.TransitionEndActivityMessage" discriminator-value="TrEndAct" />
+      <subclass name="org.jbpm.pvm.internal.model.op.TransitionTakeMessage" discriminator-value="TrTake" />
+      <subclass name="org.jbpm.pvm.internal.model.op.TransitionStartActivityMessage" discriminator-value="TrStartAct" />
+      <subclass name="org.jbpm.pvm.internal.model.op.ExecuteEventListenerMessage" discriminator-value="ExeEvtLst">
+        <property name="processDefinitionId" column="PROCDEF_" />
+        <property name="activityName" column="ACT_" />
+        <property name="transitionIndex" column="TRIDX_" />
+        <property name="eventName" column="EVENT_" />
+        <property name="eventListenerIndex" column="EVTLSTIDX_" />
+      </subclass>
+      <subclass name="org.jbpm.pvm.internal.job.CommandMessage" discriminator-value="Cmd" />
     </subclass>
     
     <subclass name="org.jbpm.pvm.internal.job.TimerImpl" discriminator-value="Timer">

Modified: jbpm4/trunk/modules/userguide/src/main/docbook/en/modules/ch05-Jpdl.xml
===================================================================
--- jbpm4/trunk/modules/userguide/src/main/docbook/en/modules/ch05-Jpdl.xml	2009-05-22 14:06:47 UTC (rev 4874)
+++ jbpm4/trunk/modules/userguide/src/main/docbook/en/modules/ch05-Jpdl.xml	2009-05-23 10:38:45 UTC (rev 4875)
@@ -480,6 +480,41 @@
       <para>With the <literal>fork</literal> and <literal>join</literal> activities, 
       concurrent paths of executions can be modeled.
       </para>
+      <table><title><literal>join</literal> attributes:</title>
+        <tgroup cols="5" rowsep="1" colsep="1">
+          <thead>
+            <row>
+              <entry>Attribute</entry>
+              <entry>Type</entry>
+              <entry>Default</entry>
+              <entry>Required?</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry><literal>multiplicity</literal></entry>
+              <entry>integer</entry>
+              <entry>nbr of incoming transitions</entry>
+              <entry>optional</entry>
+              <entry>The number of executions that should arrive in this join 
+              before the join activates and push an execution out the single 
+              outgoing transition of the join.
+              </entry>
+            </row>
+            <row>
+              <entry><literal>lockmode</literal></entry>
+              <entry>{none, read, upgrade, upgrade_nowait, write}</entry>
+              <entry>upgrade</entry>
+              <entry>optional</entry>
+              <entry>the hibernate lock mode applied on the parent execution to 
+              prevent that 2 concurrent transactions see each other as not yet 
+              arrived at the join, causing a process deadlock.
+              </entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table>
       <para>For example:</para>
       <figure id="process.concurrency">
         <title>The concurrency example process</title>
@@ -2213,7 +2248,7 @@
     asynchronous message is committed and then processed, it will start a new transaction
     and resume execution where it left off. 
     </para>
--   <table><title>Any activity attribute:</title>
+    <table><title>Attribute of any activity, <literal>transition</literal> or <literal>on</literal>:</title>
       <tgroup cols="5" rowsep="1" colsep="1">
         <thead>
           <row>
@@ -2230,15 +2265,35 @@
             <entry>{sync | async | exclusive}</entry>
             <entry>sync</entry>
             <entry>optional</entry>
-            <entry>indicates if an asynchronous continuation should be performed 
-            before the activity is executed.
+            <entry><para>indicates if an asynchronous continuation should be performed 
+              before the element is executed.</para>
             </entry>
           </row>
         </tbody>
       </tgroup>
     </table>
+    <itemizedlist>
+      <listitem><emphasis role="bold">sync</emphasis> (default) keep executing 
+      the element as part of the ongoing transaction.
+      </listitem>
+      <listitem><emphasis role="bold">async</emphasis> introduces an asynchronous
+      continuation (aka safe point).  
+      The ongoing transaction is committed and the element is executed in a
+      new transaction.  Transactional asynchronous messaging is used by the jBPM 
+      implementation to achieve this.
+      </listitem>
+      <listitem><emphasis role="bold">exclusive</emphasis> introduces a asynchronous  
+      continuation (aka safe point).  
+      The ongoing transaction is committed and the element is executed in a
+      new transaction.  Transactional asynchronous messaging is used by the jBPM 
+      implementation to achieve this.  Exclusive messages will not be processed 
+      concurrently.  The JobExecutor(s) will serialize all exclusive job 
+      executions.  This can be used to prevent optimistic locking failures in case 
+      multiple, potentially conflicting jobs are scheduled in the same transaction.
+      </listitem>
+    </itemizedlist>
     <para>Let's look at a couple of examples.</para>
-    
+
     <section id="asyncactivity">
       <title>Async activity</title>
       <figure id="process.async.activity">
@@ -2268,6 +2323,16 @@
   &lt;end name=&quot;end&quot;/&gt;
 
 &lt;/process&gt;</programlisting>
+      <programlisting>public class Application {
+
+  public void generatePdf() {
+    // assume long automatic calculations here
+  }
+
+  public void calculatePrimes() {
+    // assume long automatic calculations here
+  }
+}</programlisting>
       <programlisting>ProcessInstance processInstance = 
      executionService.startProcessInstanceByKey(&quot;AsyncActivity&quot;);
 String processInstanceId = processInstance.getId();</programlisting>
@@ -2277,13 +2342,13 @@
       </para>
       <para>But with <literal>continue=&quot;async&quot;</literal> the execution only 
       goes untill it is about to execute activity <literal>generate pdf</literal>. Then
-      An asynchronous continuation message is send and the <literal>startProcessInstanceByKey</literal> 
+      an asynchronous continuation message is send and the <literal>startProcessInstanceByKey</literal> 
       method returns.
       </para>
-      <para>In a normal configuration, the job executor will pick up the message and execute 
-      it.  But for testing scenarios and for these examples we want to control
-      when messages are executed so the job executor is not configured.  Therefor
-      we have to execute the jobs manually like this:
+      <para>In a normal configuration, the job executor will automatically pick up 
+      the message and execute it.  But for testing scenarios and for these examples we 
+      want to control when messages are executed so the job executor is not configured.  
+      Therefor we have to execute the jobs manually like this:
       </para>
       <programlisting>Job job = managementService.createJobQuery()
   .processInstanceId(processInstanceId)
@@ -2297,6 +2362,91 @@
       is executed, that transaction will run the execution till the end.
       </para>
     </section>
+
+    <section id="asyncfork">
+      <title>Async fork</title>
+      <figure id="process.async.fork">
+        <title>The async fork example process</title>
+        <mediaobject><imageobject><imagedata align="center" fileref="images/process.async.fork.png"/></imageobject></mediaobject>
+      </figure>
+      <programlisting>&lt;process name=&quot;AsyncFork&quot; xmlns=&quot;http://jbpm.org/4.0/jpdl&quot;&gt;
+
+  &lt;start &gt;
+    &lt;transition to=&quot;fork&quot;/&gt;
+  &lt;/start&gt;
+
+  &lt;fork &gt;
+    <emphasis role="bold">&lt;on event=&quot;end&quot; continue=&quot;exclusive&quot; /&gt;</emphasis>
+    &lt;transition /&gt;
+    &lt;transition /&gt;
+  &lt;/fork&gt;
+
+  &lt;java class=&quot;org.jbpm.examples.async.fork.Application&quot; &gt;
+    &lt;transition /&gt;
+  &lt;/java&gt;
+
+  &lt;java class=&quot;org.jbpm.examples.async.fork.Application&quot; &gt;
+    &lt;transition /&gt;
+  &lt;/java&gt;
+
+  &lt;join &gt;
+    &lt;transition to=&quot;end&quot;/&gt;
+  &lt;/join&gt;
+   
+  &lt;end /&gt;
+
+&lt;/process&gt;</programlisting>
+      <programlisting>public class Application {
+
+  public void shipGoods() {
+    // assume automatic calculations here
+  }
+
+  public void sendBill() {
+    // assume automatic calculations here
+  }
+}</programlisting>
+      <para>By placing the asynchronous continuation on the <literal>end</literal>
+      event of the fork (<literal>&lt;on event=&quot;end&quot; continue=&quot;exclusive&quot; /&gt;</literal>), 
+      each forked execution that takes a transition out of the 
+      fork will be continued asynchronously.  
+      </para>
+      <para>Value <literal>exclusive</literal> was selected to serialize the executions of 
+      the 2 asynchonous continuation jobs resulting from the fork. The respective transactions 
+      that will execute activities <literal>ship goods</literal> 
+      and <literal>send bill</literal> will both arrive at the join.  At the join, both 
+      transactions will synchronize on the same execution (read: update the same execution 
+      row in the DB), resulting in a potential optimistic locking failure.   
+      </para>
+      <programlisting>ProcessInstance processInstance = executionService.startProcessInstanceByKey(&quot;AsyncFork&quot;);
+String processInstanceId = processInstance.getId();
+
+List&lt;Job&gt; jobs = managementService.createJobQuery()
+  .processInstanceId(processInstanceId)
+  .list();
+
+assertEquals(2, jobs.size());
+
+Job job = jobs.get(0);
+
+// here we simulate execution of the job, 
+// which is normally done by the job executor
+managementService.executeJob(job.getDbid());
+
+job = jobs.get(1);
+
+// here we simulate execution of the job, 
+// which is normally done by the job executor
+managementService.executeJob(job.getDbid());
+
+Date endTime = historyService
+  .createHistoryProcessInstanceQuery()
+  .processInstanceId(processInstance.getId())
+  .uniqueResult()
+  .getEndTime();
+
+assertNotNull(endTime);</programlisting>
+    </section>
   </section>
 
   <section id="timer">




More information about the jbpm-commits mailing list