2009年11月7日土曜日

workflow patterns/jBPM/Eclipse

jBPM の JPDL の ワークフローパターン への対応を試してみた。とりあえず、前回のポストの "Basic Control Flow Patterns"の 5つ。

やり方は、JUnit でプロセスの実行がパターンの定義どおりに進行している事を assert で確認する。JUnit で動かすための環境は、だいぶ前のポストに書いたとおりにプロジェクトを作って、あとはビルドパスに JUnit 4ライブラリを追加すれば、すぐテスト実行できる。
テストメソッド以外の部分は次のようになる。
package net.yasuabe.trial.jboss_tasks1;

import 省略

public class WorkflowPatternTest {
   private ProcessEngine engine = null;
   private ExecutionService es = null;
   private RepositoryService rs = null;
   @Before
   public void setUpEngine() {
      engine = Configuration.getProcessEngine();
      es = engine.getExecutionService();
      rs = engine.get(RepositoryService.class);
   }
   @After
   public void cleanJBPM4Tables() {
      Db.clean(engine);
   }
}
これに、各パターンごとのテストメソッドを追加していく。
■ Sequence パターン
○─S1─S2
  • フローを開始する。S1 に進んだが、S2 には届いていない事を確認
  • S1 からフローを進める。S1 から出てS2に入った事を確認。
@Test
public void sequencePattern() {
   String jpdl =
      " <process name=\"pat1\"> " +
      "   <start> "+
      "     <transition to=\"S1\" /> " +
      "   </start> "+
      "   <state name=\"S1\"> "+
      "      <transition to=\"S2\" /> " +
      "   </state> "+
      "   <state name=\"S2\"/> " +
      " </process> ";
   rs.createDeployment().addResourceFromString(
         "pat1.jpdl.xml", jpdl).deploy();
   
   ProcessInstance pi = es
         .startProcessInstanceByKey("pat1");
   assertNotNull(pi.findActiveExecutionIn("S1"));
   assertNull(pi.findActiveExecutionIn("S2"));
   
   String exec1Id = pi.findActiveExecutionIn("S1").getId();
   pi = es.signalExecutionById(exec1Id);

   assertNull(pi.findActiveExecutionIn("S1"));
   assertNotNull(pi.findActiveExecutionIn("S2"));
}
■ Parallel Split パターン
    ┌S2
○-S1-F1┤
    └S3
  • フローを開始する
  • 更にS1 から先に進める
  • S1 から出て、実行がS2 S3 に入っている事を確認
@Test
public void parallelSplitPattern() {
   String jpdl =
      " <process name=\"pat2\"> " +
      "   <start> "+
      "     <transition to=\"S1\" /> " +
      "   </start> "+
      "   <state name=\"S1\"> "+
      "      <transition to=\"F1\" /> " +
      "   </state> "+
      "   <fork name=\"F1\"> "+
      "      <transition to=\"S2\" /> " +
      "      <transition to=\"S3\" /> " +
      "   </fork> "+
      "   <state name=\"S2\"/> " +
      "   <state name=\"S3\"/> " +
      " </process> ";
   rs.createDeployment().addResourceFromString(
         "pat2.jpdl.xml", jpdl).deploy();
   
   ProcessInstance pi = es
         .startProcessInstanceByKey("pat2");
   Execution exec1 = pi.findActiveExecutionIn("S1");
   pi = es.signalExecutionById(exec1.getId());

   assertNull(pi.findActiveExecutionIn("S1"));
   assertNotNull(pi.findActiveExecutionIn("S2"));
   assertNotNull(pi.findActiveExecutionIn("S3"));
}
■ Synchronization パターン
   ┌S1┐
○-F1┤ ├J1-S3
   └S2┘
  • フローを開始する
  • 並行してS1, S2 に入っている事を確認
  • S1 だけ進めて,それだけでは S3 には入らない事を確認
  • S2 も進めると、S3 に入った事を確認
@Test
public void synchronizationPattern() {
   String jpdl =
      " <process name=\"pat3\"> " +
      "   <start> "+
      "     <transition to=\"F1\" /> " +
      "   </start> "+
      "   <fork name=\"F1\"> "+
      "      <transition to=\"S1\" /> " +
      "      <transition to=\"S2\" /> " +
      "   </fork> "+
      "   <state name=\"S1\"> " +
      "      <transition to=\"J1\" /> " +
      "   </state> "+
      "   <state name=\"S2\"> " +
      "      <transition to=\"J1\" /> " +
      "   </state> "+
      "   <join name=\"J1\"> " +
      "      <transition to=\"S3\" /> " +
      "   </join> "+
      "   <state name=\"S3\"/> " +
      " </process> ";
   rs.createDeployment().addResourceFromString(
         "pat3.jpdl.xml", jpdl).deploy();
   
   ProcessInstance pi = es
         .startProcessInstanceByKey("pat3");
   assertNotNull(pi.findActiveExecutionIn("S1"));
   assertNotNull(pi.findActiveExecutionIn("S2"));
   assertNull(pi.findActiveExecutionIn("S3"));

   String exec1Id = pi.findActiveExecutionIn("S1").getId();
   pi = es.signalExecutionById(exec1Id);

   assertNull(findActiveExecutionIn("S1"));
   assertNotNull(findActiveExecutionIn("S2"));
   assertNull(pi.findActiveExecutionIn("S3"));

   String exec2Id = pi.findActiveExecutionIn("S2").getId();
   pi = es.signalExecutionById(exec2Id);

   assertNull(pi.findActiveExecutionIn("S1"));
   assertNull(pi.findActiveExecutionIn("S2"));
   assertNull(pi.findActiveExecutionIn("S3"));
}
■ Exclusive Choice パターン
   ┌S1 (color=black)
○-D1┤
   └S2 (color=white)
  • color=white の条件でフローを開始
  • S2 が選択されて実行に入っている事を確認
  • color=red の条件でフローを開始
  • 該当するパスがないので例外が出る事を確認
@Test
public void exclusiveChoicePattern() {
   String jpdl =
      " <process name=\"pat4\"> " +
      "   <start> "+
      "     <transition to=\"D1\" /> " +
      "   </start> "+
      "   <decision name=\"D1\" expr=\"#{color}\"> "+
      "      <transition name=\"black\" to=\"S1\" /> " +
      "      <transition name=\"white\" to=\"S2\" /> " +
      "   </decision> "+
      "   <state name=\"S1\"/> " +
      "   <state name=\"S2\"/> " +
      " </process> ";
   rs.createDeployment().addResourceFromString(
         "pat4.jpdl.xml", jpdl).deploy();
   
   Map<String, Object> v1 = new HashMap<String, Object>();
   v1.put("color", "white");
   ProcessInstance pi1 = es
         .startProcessInstanceByKey("pat4", v1);
   
   assertNull(pi1.findActiveExecutionIn("S1"));
   assertNotNull(pi1.findActiveExecutionIn("S2"));

   boolean exceptionThrown = false;
   try {
      Map<String, Object> v2 = new HashMap<String, Object>();
      v2.put("color", "red");
      es.startProcessInstanceByKey("pat4", v2);
   } catch (JbpmException ex) {
      exceptionThrown = true;
   }
   assertTrue(exceptionThrown);
}
■ Simple Merge パターン
   ┌S1 (color=white)┐
○-D1┤        ├S3
   └S2 (color=black)┘
  • color=white でフローを開始
  • S2 だけに進んでいる事を確認
  • S2 を進めると、S1に関係なく S3 に進んだ事を確認
@Test
public void simpleMergePattern() {
   String jpdl =
      " <process name=\"pat5\"> " +
      "   <start> "+
      "     <transition to=\"D1\" /> " +
      "   </start> "+
      "   <decision name=\"D1\" expr=\"#{color}\"> "+
      "      <transition name=\"black\" to=\"S1\" /> " +
      "      <transition name=\"white\" to=\"S2\" /> " +
      "   </decision> "+
      "   <state name=\"S1\"> " +
      "      <transition to=\"S3\" /> " +
      "   </state> "+
      "   <state name=\"S2\"> " +
      "      <transition to=\"S3\" /> " +
      "   </state> "+
      "   <state name=\"S3\"/> " +
      " </process> ";
   rs.createDeployment().addResourceFromString(
         "pat5.jpdl.xml", jpdl).deploy();
   
   Map<String, Object> v = new HashMap<String, Object>();
   v.put("color", "white");
   ProcessInstance pi = es
         .startProcessInstanceByKey("pat5", v);
   assertNull(pi.findActiveExecutionIn("S1"));
   assertNotNull(pi.findActiveExecutionIn("S2"));
   assertNull(pi.findActiveExecutionIn("S3"));

   String exec2Id = pi.findActiveExecutionIn("S2").getId();
   pi = es.signalExecutionById(exec2Id);

   assertNull(pi.findActiveExecutionIn("S2"));
   assertNotNull(pi.findActiveExecutionIn("S3"));
}

0 件のコメント:

コメントを投稿