Red Hat Training

A Red Hat training course is available for JBoss Enterprise SOA Platform

JBPM リファレンスガイド

JBoss Enterprise SOA Platform 5

JBoss 開発者向け

5.3.1 エディッション

概要

このガイドでは、JBoss Enterprise SOA Platform で JBPM と JPDL を使用する方法を説明します。

第1章 はじめに

この ガイド は、開発者および管理者向けに書かれています。企業環境で jBPM と JPDL を使用する方法を学ぶ際にお読みください。本書は、ソフトウェアの使用方法を説明するだけでなく、その仕組みについても非常に詳細に説明しています。
注記
この ガイド には、多くの用語が含まれています。主な用語の定義は、「 役に立つ定義 」 を参照してください。
JBoss Business Process Manager (jBPM) は、プロセス言語の柔軟で拡張可能な基盤です。jBPM Process Definition Language (JPDL) は、このフレームワークの上に構築された プロセス言語 の 1 つです。これは、ユーザーがビジネスプロセスをグラフィカルに表現できるように設計された直感的な言語です。この表現は、タスク待機状態 (非同期通信の場合)、タイマー、および自動化された アクション を表すことによって行われます。これらの操作をまとめてバインドするために、言語には強力で拡張可能な 制御フローメカニズムが あります。
JPDL には依存関係がほとんどないため、Java ライブラリーと同じくらい簡単にインストールできます。これを行うには、JPDL を クラスター化された J2EE アプリケーションサーバー にデプロイします。これは、極端なスループットが重要な要件である環境で特に役立ちます。
注記
JPDL は、任意のデータベースで使用できるように設定できます。また、任意のアプリケーションサーバーにデプロイすることもできます。

1.1. 概要

このセクションでは、jBPM の動作方法の概要を説明します。
中核となるワークフローおよびビジネスプロセス管理機能は、シンプルな Java ライブラリーとしてパッケージ化されています。このライブラリーには、JPDL データベースプロセスを管理および実行するサービスが含まれています。

図1.1 jPDL コンポーネントの概要

jPDL コンポーネントの概要

1.2. jPDL スイート

このスイートには、すべての jBPM コンポーネントと次のサブディレクトリーが含まれています。
  • config
  • database
  • deploy
  • designer
  • examples
  • lib
  • src
JBoss Application Server は、次のコンポーネントで構成されています。
jBPM Web コンソール
これは Web アーカイブとしてパッケージ化されています。プロセス参加者 と jBPM 管理者の両方がこのコンソールを使用できます。
jBPM テーブル
これらは、デフォルトの Hypersonic データベースに含まれています。(このデータベースには、すでにプロセスが含まれています。)
サンプルプロセス
1 つのサンプルプロセスが、すでに jBPM データベースにデプロイされています。
アイデンティティーコンポーネント
アイデンティティーコンポーネントライブラリーは、コンソール Web アプリケーション の一部です。データベース内にある JBPM_ID_ 接頭辞を持つテーブルを所有しています。

1.3. jPDL グラフィカルプロセスデザイナー

jPDL には、グラフィカルプロセスデザイナー も含まれています。これは、ビジネスプロセスの設計に使用します。(これは Eclipse プラグインであり、JBoss Developer Studio 製品に含まれています。)
ビジネスプロセスのモデリングから実際の実装へのスムーズな移行を容易にし、ビジネスアナリストと技術開発者の両方に役立ちます。

1.4. jBPM コンソール Web アプリケーション

コンソール Web アプリケーション には、3 つの目的があります。1 つ目は、中心的なユーザーインターフェイスとしての機能です。ここでは、プロセスの実行によって生成されたランタイムタスクを操作できます。2 つ目は、ランタイムインスタンスを検査および操作できる管理コンソールおよびモニタリングコンソールとしての機能です。このソフトウェアの 3 つ目の機能は、ビジネスアクティビティーモニターとしての機能です。この機能は、プロセスの実行に関する統計を表示します。この情報を使用すれば、ボトルネックを発見して排除できるため、これはパフォーマンスの最適化を求めるマネージャーに役立ちます。

1.5. jBPM コアライブラリー

Business Process Manager には 2 つのコアコンポーネントがあります。これらは、プロセス定義を管理する "プレーン Java" (J2SE) ライブラリーと、プロセスインスタンスを実行するランタイム環境です。
jBPM 自体は Java ライブラリーです。したがって、Web アプリケーションや Swing アプリケーション、Enterprise Java Bean、Web サービスなど、あらゆる Java 環境で使用できます。
jBPM ライブラリーを ステートレスセッション Enterprise Java Bean としてパッケージ化して公開することもできます。クラスターデプロイメントを作成する必要がある場合、または非常に高いスループットを実現するためにスケーラビリティーを提供する必要がある場合は、これを行ってください。(ステートレスセッション Enterprise Java BeanJ2EE 1.3 仕様に準拠しているため、任意のアプリケーションサーバーにデプロイできます。)
jbpm-jpdl.jar ファイルの一部は、HibernateDom4J などのサードパーティーライブラリーに依存していることに注意してください。
Hibernate は jBPM に 永続性 機能を提供します。また、従来の O/R マッピング を提供することとは別に、Hibernate は競合するデータベースで使用される構造化照会言語の方言間の違いを解決します。この機能により、jBPM は高い移植性を確保しています。
Business Process Manager のアプリケーションプログラミングインターフェイスは、Web アプリケーション、Enterprise Java Bean、Web サービスコンポーネント、メッセージ駆動型 Bean など、プロジェクト内の任意のカスタム Java コードからアクセスできます。

1.6. アイデンティティーコンポーネント

jBPM は、ユーザーデータ (およびその他の組織データ) を含む会社のディレクトリーと統合できます。(組織情報コンポーネントが利用できないプロジェクトの場合は、アイデンティティーコンポーネント を使用します。このコンポーネントには、従来のサーブレット、Enterprise Java Bean、およびポートレットで使用されるモデルよりも "豊富" なモデルがあります。)
注記
このトピックの詳細は、「 アイデンティティーコンポーネント 」 を参照してください。

1.7. jBPM ジョブエグゼキューター

JBoss jBPM ジョブエグゼキューター は、標準の Java 環境でジョブをモニタリングおよび実行する目的で設計されたコンポーネントです。ジョブ は、タイマーと非同期メッセージに使用されます。(エンタープライズ環境では、Java Message Service と Enterprise Java Bean TimerService をこの目的に使用する場合があります。ジョブエグゼキューターは "標準" の環境で使用するのが最適です。)
Job Executor コンポーネントは、コア jbpm-jpdl ライブラリーにパッケージ化されています。次の 2 つのシナリオのいずれかでのみデプロイできます。
  • JbpmThreadsServlet がジョブエグゼキューターを起動するように設定されている場合。
  • 別個の Java 仮想マシンを起動し、その中からジョブエグゼキュータースレッドを実行できるようにした場合。

1.8. まとめ

この章では、jBPM とその構成コンポーネントの概要を説明しました。

第2章 チュートリアル

次のチュートリアルでは、JPDL で基本的な プロセス構造 を使用する方法を説明します。このチュートリアルでは、アプリケーションプログラミングインターフェイスを介してランタイム実行を管理する方法も示します。
このチュートリアルの例は、src/java.examples サブディレクトリーにある JBPM ダウンロードパッケージにあります。
注記
この時点でプロジェクトを作成することを推奨します。そうすることで、各例を自由に試したり、バリエーションを作成したりできます。
まず、JBPM をダウンロードしてインストールします。
jBPM には、例に示されている XML を作成するためのグラフィカルデザイナーツールが含まれています。グラフィカルデザイナーのダウンロード手順は、「ダウンロード可能なリソースの概要」セクションを参照してください。このチュートリアルを完了するためにグラフィカルデザイナーツールは必要ありません。

2.1. "Hello World" の例

プロセス定義 は、ノードと遷移で構成される 有向グラフ です。Hello World プロセス定義には、以下のノードが 3 つあります。(デザイナーツールl を使用せずにこの単純なプロセスを調べて、その仕組みを学習することをお勧めします。) 次の図は、Hello World プロセスをグラフィカルに表現したものです。

図2.1 Hello World プロセスの図

Hello World プロセスの図
public void testHelloWorldProcess() {
  // This method shows a process definition and one execution
  // of the process definition.  The process definition has
  // 3 nodes: an unnamed start-state, a state 's' and an
  // end-state named 'end'.
  // The next line parses a piece of xml text into a
  // ProcessDefinition.  A ProcessDefinition is the formal
  // description of a process represented as a java object.
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition>" +
      "  <start-state>" +
      "    <transition to='s' />" +
      "  </start-state>" +
      "  <state name='s'>" +
      "    <transition to='end' />" +
      "  </state>" +
      "  <end-state name='end' />" +
      "</process-definition>"
  );

    // The next line creates one execution of the process definition.
    // After construction, the process execution has one main path
    // of execution (=the root token) that is positioned in the
    // start-state.
    ProcessInstance processInstance =
      new ProcessInstance(processDefinition);

    // After construction, the process execution has one main path
    // of execution (=the root token).
    Token token = processInstance.getRootToken();

    // Also after construction, the main path of execution is positioned
    // in the start-state of the process definition.
    assertSame(processDefinition.getStartState(), token.getNode());

    // Let's start the process execution, leaving the start-state
    // over its default transition.
    token.signal();
    // The signal method will block until the process execution
    // enters a wait state.

    // The process execution will have entered the first wait state
    // in state 's'. So the main path of execution is now
    // positioned in state 's'
    assertSame(processDefinition.getNode("s"), token.getNode());

    // Let's send another signal.  This will resume execution by
    // leaving the state 's' over its default transition.
    token.signal();
    // Now the signal method returned because the process instance
    // has arrived in the end-state.

    assertSame(processDefinition.getNode("end"), token.getNode());
}

2.2. データベースの例

jBPM の基本的な機能の 1 つに、データベースプロセスが 待機状態 の間、データベースプロセスの実行を永続化する機能があります。次の例は、この機能を示しており、jBPM データベースにプロセスインスタンスを保存しています。
これは、ユーザーコードのさまざまな部分に対して個別の メソッド を作成することによって機能します。たとえば、Web アプリケーション内のユーザーコードの一部がプロセスを起動し、データベース内で実行を "永続化" します。その後、メッセージ駆動型 Bean がそのプロセスインスタンスをロードし、その実行を再開します。
ここでは、ユーザーコードのさまざまな部分に対して個別の メソッド が作成されます。たとえば、Web アプリケーションのコードがプロセスを起動し、データベース内で実行を "永続化" します。その後、メッセージ駆動型 Bean プロセスインスタンスをロードし、その実行を再開します。
注記
jBPM の永続性に関する詳細は、4章 永続性 を参照してください。
public class HelloWorldDbTest extends TestCase {

  static JbpmConfiguration jbpmConfiguration = null; 

  static {
    // An example configuration file such as this can be found in 
    // 'src/config.files'.  Typically the configuration information 
    // is in the resource file 'jbpm.cfg.xml', but here we pass in 
    // the configuration information as an XML string.
    
    // First we create a JbpmConfiguration statically.  One 
    // JbpmConfiguration can be used for all threads in the system, 
    // that is why we can safely make it static.

    jbpmConfiguration = JbpmConfiguration.parseXmlString(
      "<jbpm-configuration>" +
      
      // A jbpm-context mechanism separates the jbpm core 
      // engine from the services that jbpm uses from 
      // the environment.  
      
      "<jbpm-context>"+
      "<service name='persistence' "+
      " factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" +
      "</jbpm-context>"+
      
      // Also all the resource files that are used by jbpm are 
      // referenced from the jbpm.cfg.xml
      
      "<string name='resource.hibernate.cfg.xml' " +
      "  value='hibernate.cfg.xml' />" +
      "<string name='resource.business.calendar' " +
      "  value='org/jbpm/calendar/jbpm.business.calendar.properties' />" +
      "<string name='resource.default.modules' " +
      "  value='org/jbpm/graph/def/jbpm.default.modules.properties' />" +
      "<string name='resource.converter' " +
      "  value='org/jbpm/db/hibernate/jbpm.converter.properties' />" +
      "<string name='resource.action.types' " +
      "  value='org/jbpm/graph/action/action.types.xml' />" +
      "<string name='resource.node.types' " +
      "  value='org/jbpm/graph/node/node.types.xml' />" +
      "<string name='resource.varmapping' " +
      "  value='org/jbpm/context/exe/jbpm.varmapping.xml' />" +
      "</jbpm-configuration>"
    );
  }
  
  public void setUp() {
    jbpmConfiguration.createSchema();
  }
  
  public void tearDown() {
    jbpmConfiguration.dropSchema();
  }

  public void testSimplePersistence() {
    // Between the 3 method calls below, all data is passed via the 
    // database.  Here, in this unit test, these 3 methods are executed
    // right after each other because we want to test a complete process
    // scenario.  But in reality, these methods represent different 
    // requests to a server.
    
    // Since we start with a clean, empty in-memory database, we have to 
    // deploy the process first.  In reality, this is done once by the 
    // process developer.
    deployProcessDefinition();

    // Suppose we want to start a process instance (=process execution)
    // when a user submits a form in a web application...
    processInstanceIsCreatedWhenUserSubmitsWebappForm();

    // Then, later, upon the arrival of an asynchronous message the 
    // execution must continue.
    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
  }

  public void deployProcessDefinition() {
    // This test shows a process definition and one execution 
    // of the process definition.  The process definition has 
    // 3 nodes: an unnamed start-state, a state 's' and an 
    // end-state named 'end'.
    ProcessDefinition processDefinition = 
        ProcessDefinition.parseXmlString(
      "<process-definition name='hello world'>" +
      "  <start-state name='start'>" +
      "    <transition to='s' />" +
      "  </start-state>" +
      "  <state name='s'>" +
      "    <transition to='end' />" +
      "  </state>" +
      "  <end-state name='end' />" +
      "</process-definition>"
    );

    //Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
      // Deploy the process definition in the database 
      jbpmContext.deployProcessDefinition(processDefinition);

    } finally {
      // Tear down the pojo persistence context.
      // This includes flush the SQL for inserting the process definition
      // to the database.
      jbpmContext.close();
    }
  }

  public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {
    // The code in this method could be inside a struts-action 
    // or a JSF managed bean. 

    //Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");
    
      //With the processDefinition that we retrieved from the database, we
      //can create an execution of the process definition just like in the
      //hello world example (which was without persistence).
      ProcessInstance processInstance = 
          new ProcessInstance(processDefinition);
      
      Token token = processInstance.getRootToken(); 
      assertEquals("start", token.getNode().getName());
      // Let's start the process execution
      token.signal();
      // Now the process is in the state 's'.
      assertEquals("s", token.getNode().getName());
      
      // Now the processInstance is saved in the database.  So the 
      // current state of the execution of the process is stored in the 
      // database.  
      jbpmContext.save(processInstance);
      // The method below will get the process instance back out 
      // of the database and resume execution by providing another 
      // external signal.

    } finally {
      // Tear down the pojo persistence context.
      jbpmContext.close();
    }
  }

  public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {
  //The code in this method could be the content of a message driven bean.

    // Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      // First, we need to get the process instance back out of the 
      // database.  There are several options to know what process 
      // instance we are dealing  with here.  The easiest in this simple
      // test case is just to look for the full list of process instances.
      // That should give us only one result.  So let's look up the 
      // process definition.
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");

      //Now search for all process instances of this process definition.
      List processInstances = 
          graphSession.findProcessInstances(processDefinition.getId());
      
      // Because we know that in the context of this unit test, there is 
      // only one execution.  In real life, the processInstanceId can be 
      // extracted from the content of the message that arrived or from 
      // the user making a choice.
      ProcessInstance processInstance = 
          (ProcessInstance) processInstances.get(0);
      
      // Now we can continue the execution.  Note that the processInstance
      // delegates signals to the main path of execution (=the root token).
      processInstance.signal();

      // After this signal, we know the process execution should have 
      // arrived in the end-state.
      assertTrue(processInstance.hasEnded());
      
      // Now we can update the state of the execution in the database
      jbpmContext.save(processInstance);

    } finally {
      // Tear down the pojo persistence context.
      jbpmContext.close();
    }
  }
}

2.3. コンテキストの例: プロセス変数

プロセスが実行されている間、コンテキスト情報は プロセス変数 に保持されます。これらは、変数名を値にマップするという点で java.util.Map クラスに似ており、後者は Java オブジェクトに相当します。(プロセス変数は、プロセスインスタンスの一部として "永続化" されます。)
注記
次の例を単純にするために、変数を操作するために必要なアプリケーションプログラミングインターフェイスのみを示します (永続性機能はありません)。
注記
変数の詳細は、7章 コンテキスト を参照してください。
// This example also starts from the hello world process.
// This time even without modification.
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "<process-definition>" +
  "  <start-state>" +
  "    <transition to='s' />" +
  "  </start-state>" +
  "  <state name='s'>" +
  "    <transition to='end' />" +
  "  </state>" +
  "  <end-state name='end' />" +
  "</process-definition>"
);

ProcessInstance processInstance =
  new ProcessInstance(processDefinition);

// Fetch the context instance from the process instance 
// for working with the process variables.
ContextInstance contextInstance = 
  processInstance.getContextInstance();

// Before the process has left the start-state, 
// we are going to set some process variables in the 
// context of the process instance.
contextInstance.setVariable("amount", new Integer(500));
contextInstance.setVariable("reason", "i met my deadline");

// From now on, these variables are associated with the 
// process instance.  The process variables are now accessible 
// by user code via the API shown here, but also in the actions 
// and node implementations.  The process variables are also  
// stored into the database as a part of the process instance.

processInstance.signal();

// The variables are accessible via the contextInstance. 

assertEquals(new Integer(500), 
             contextInstance.getVariable("amount"));
assertEquals("i met my deadline", 
             contextInstance.getVariable("reason"));

2.4. タスク割り当ての例

次の例は、ユーザーにタスクを割り当てる方法を示しています。jBPM ワークフローエンジンと組織モデルが分離しているため、式言語が常に限定されており、アクターの計算には使用できません。代わりに、AssignmentHandler の実装を指定し、それを使用してタスクのアクターの計算を含めます。
public void testTaskAssignment() {
  // The process shown below is based on the hello world process.
  // The state node is replaced by a task-node.  The task-node 
  // is a node in JPDL that represents a wait state and generates 
  // task(s) to be completed before the process can continue to 
  // execute.  
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<process-definition name='the baby process'>" +
    "  <start-state>" +
    "    <transition name='baby cries' to='t' />" +
    "  </start-state>" +
    "  <task-node name='t'>" +
    "    <task name='change nappy'>" +
    "      <assignment" +
    "       class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />" +
    "    </task>" +
    "    <transition to='end' />" +
    "  </task-node>" +
    "  <end-state name='end' />" +
    "</process-definition>"
  );
  
  // Create an execution of the process definition.
  ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
  Token token = processInstance.getRootToken();
  
  // Let's start the process execution, leaving the start-state 
  // over its default transition.
  token.signal();
  // The signal method will block until the process execution 
  // enters a wait state.   In this case, that is the task-node.
  assertSame(processDefinition.getNode("t"), token.getNode());

  // When execution arrived in the task-node, a task 'change nappy'
  // was created and the NappyAssignmentHandler was called to determine
  // to whom the task should be assigned.  The NappyAssignmentHandler 
  // returned 'papa'.

  // In a real environment, the tasks would be fetched from the
  // database with the methods in the org.jbpm.db.TaskMgmtSession.
  // Since we don't want to include the persistence complexity in 
  // this example, we just take the first task-instance of this 
  // process instance (we know there is only one in this test
  // scenario).
  TaskInstance taskInstance = (TaskInstance)  
      processInstance
        .getTaskMgmtInstance()
        .getTaskInstances()
        .iterator().next();

  // Now, we check if the taskInstance was actually assigned to 'papa'.
  assertEquals("papa", taskInstance.getActorId() );
  
  // Now we suppose that 'papa' has done his duties and mark the task 
  // as done. 
  taskInstance.end();
  // Since this was the last (only) task to do, the completion of this
  // task triggered the continuation of the process instance execution.
  
  assertSame(processDefinition.getNode("end"), token.getNode());
}

2.5. カスタムアクションの例

アクション は、カスタム Java コードを jBPM プロセスにバインドするためのメカニズムです。アクションは独自のノードに関連付けることができます (これらがプロセスのグラフィカル表現に関連している場合)。 または、アクションをイベントに "配置" することもできます (たとえば、遷移を取得するとき、またはノードに進入または退出するときなど)。 アクションがイベントに配置されている場合、アクションはグラフィカル表現の一部として扱われません (ただし、ランタイムプロセスの実行中にイベントが "発生" すると、アクションは引き続き実行されます)。
最初に、次の例で使用されているアクションハンドラー MyActionHandler の実装をご覧ください。それ自体は特に驚くようなものではありません。ブール型変数 isExecutedtrue に設定しているだけです。この変数は静的であるため、アクションハンドラー内から (およびアクション自体から) アクセスして、その値を確認することができます。
注記
"アクション" の詳細は、「アクション」 を参照してください。
// MyActionHandler represents a class that could execute 
// some user code during the execution of a jBPM process.
public class MyActionHandler implements ActionHandler {

  // Before each test (in the setUp), the isExecuted member 
  // will be set to false.
  public static boolean isExecuted = false;  

  // The action will set the isExecuted to true so the 
  // unit test will be able to show when the action
  // is being executed.
  public void execute(ExecutionContext executionContext) {
    isExecuted = true;
  }
}
重要
各テストの前に、静的フィールド MyActionHandler.isExecutedfalse に設定してください。
  // Each test will start with setting the static isExecuted 
  // member of MyActionHandler to false.
  public void setUp() {
    MyActionHandler.isExecuted = false;
  }
最初の例は、遷移に対するアクションを示しています。
public void testTransitionAction() {
    // The next process is a variant of the hello world process.
    // We have added an action on the transition from state 's' 
    // to the end-state.  The purpose of this test is to show 
    // how easy it is to integrate Java code in a jBPM process.
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition>" +
      "  <start-state>" +
      "    <transition to='s' />" +
      "  </start-state>" +
      "  <state name='s'>" +
      "    <transition to='end'>" +
      "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
      "    </transition>" +
      "  </state>" +
      "  <end-state name='end' />" +
      "</process-definition>"
    );
    
    // Let's start a new execution for the process definition.
    ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
    
    // The next signal will cause the execution to leave the start 
    // state and enter the state 's'
    processInstance.signal();

    // Here we show that MyActionHandler was not yet executed. 
    assertFalse(MyActionHandler.isExecuted);
    // ... and that the main path of execution is positioned in 
    // the state 's'
    assertSame(processDefinition.getNode("s"), 
               processInstance.getRootToken().getNode());
    
    // The next signal will trigger the execution of the root 
    // token.  The token will take the transition with the
    // action and the action will be executed during the  
    // call to the signal method.
    processInstance.signal();
    
    // Here we can see that MyActionHandler was executed during 
    // the call to the signal method.
    assertTrue(MyActionHandler.isExecuted);
  }
次の例は、enter-node イベントと leave-node イベントの両方に同じアクションが配置されていることを示しています。ノードには複数のイベントタイプがあることに注意してください。これは、イベントが 1 つしかない 遷移 とは対照的です。したがって、ノードにアクションを配置するときは、常にイベント要素に配置してください。
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "<process-definition>" +
  "  <start-state>" +
  "    <transition to='s' />" +
  "  </start-state>" +
  "  <state name='s'>" +
  "    <event type='node-enter'>" +
  "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
  "    </event>" +
  "    <event type='node-leave'>" +
  "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
  "    </event>" +
  "    <transition to='end'/>" +
  "  </state>" +
  "  <end-state name='end' />" +
  "</process-definition>"
);

ProcessInstance processInstance = 
  new ProcessInstance(processDefinition);

assertFalse(MyActionHandler.isExecuted);
// The next signal will cause the execution to leave the start 
// state and enter the state 's'.  So the state 's' is entered 
// and hence the action is executed. 
processInstance.signal();
assertTrue(MyActionHandler.isExecuted);

// Let's reset the MyActionHandler.isExecuted  
MyActionHandler.isExecuted = false;

// The next signal will trigger execution to leave the  
// state 's'.  So the action will be executed again. 
processInstance.signal();
// Voila.  
assertTrue(MyActionHandler.isExecuted);

第3章 設定

この章では、例を通じて jBPM の設定方法を説明します。
Business Process Manager を設定する最も簡単な方法は、jbpm.cfg.xml 設定ファイルをクラスパスのルートに配置することです。ファイルをリソースとして使用できない場合は、代わりにデフォルトの最小設定が使用されます。この最小設定は jBPM ライブラリー (org/jbpm/default.jbpm.cfg.xml) に含まれています。 jBPM 設定ファイルが提供されている場合、そこに含まれる値がデフォルト値として使用されます。したがって、デフォルトの設定ファイルの値とは異なる値を指定するだけで済みます。
jBPM 設定は、org.jbpm.JbpmConfiguration と呼ばれる Java クラスによって表されます。これは、シングルトン インスタンスメソッド (JbpmConfiguration.getInstance()) を使用して取得します。
注記
別のソースから設定をロードするには、JbpmConfiguration.parseXxxx メソッドを使用します。
static JbpmConfinguration jbpmConfiguration = JbpmConfinguration.parseResource("my.jbpm.cfg.xml");
JbpmConfiguration は "スレッドセーフ" であるため、静的メンバー に保持できます。
すべてのスレッドは、JbpmContext オブジェクトの ファクトリー として JbpmConfiguration を使用できます。JbpmContext は通常、1 つのトランザクションを表します。これらは、次のような コンテキストブロック 内でサービスを利用できるようにします。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  // This is what we call a context block.
  // Here you can perform workflow operations

} finally {
  jbpmContext.close();
}
JbpmContext は、一連のサービスと構成設定の両方を Business Process Manager で使用できるようにします。サービスは、jbpm.cfg.xml ファイルの値によって設定されます。これらは、環境内で利用可能なあらゆるサービスを使用して、jBPM を任意の Java 環境で実行できるようにします。
JbpmContext のデフォルトの構成設定は次のとおりです。
<jbpm-configuration>

<jbpm-context>
    <service name='persistence'
      factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />
    <service name='message'
      factory='org.jbpm.msg.db.DbMessageServiceFactory' />
    <service name='scheduler'
      factory='org.jbpm.scheduler.db.DbSchedulerServiceFactory' />
    <service name='logging'
      factory='org.jbpm.logging.db.DbLoggingServiceFactory' />
    <service name='authentication'
      factory=
'org.jbpm.security.authentication.DefaultAuthenticationServiceFactory' />
</jbpm-context>

<!-- configuration resource files pointing to default
     configuration files in jbpm-{version}.jar -->
<string name='resource.hibernate.cfg.xml' value='hibernate.cfg.xml' />
  
  <!-- <string name='resource.hibernate.properties'
       value='hibernate.properties' /> -->
  <string name='resource.business.calendar'
    value='org/jbpm/calendar/jbpm.business.calendar.properties' />
  <string name='resource.default.modules'
    value='org/jbpm/graph/def/jbpm.default.modules.properties' />
  <string name='resource.converter'
    value='org/jbpm/db/hibernate/jbpm.converter.properties' />
  <string name='resource.action.types'
    value='org/jbpm/graph/action/action.types.xml' />
  <string name='resource.node.types'
    value='org/jbpm/graph/node/node.types.xml' />
  <string name='resource.parsers'
    value='org/jbpm/jpdl/par/jbpm.parsers.xml' />
  <string name='resource.varmapping'
    value='org/jbpm/context/exe/jbpm.varmapping.xml' />
  <string name='resource.mail.templates' 
    value='jbpm.mail.templates.xml' />

  <int name='jbpm.byte.block.size' value="1024" singleton="true" />
  <bean name='jbpm.task.instance.factory' 
    class='org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl'
    singleton='true' />
    
  <bean name='jbpm.variable.resolver' 
    class='org.jbpm.jpdl.el.impl.JbpmVariableResolver'
    singleton='true' />
    
    <string name='jbpm.mail.smtp.host' value='localhost' />
    
    <bean name='jbpm.mail.address.resolver' 
        class='org.jbpm.identity.mail.IdentityAddressResolver' 
        singleton='true' />
    <string name='jbpm.mail.from.address' value='jbpm@noreply' />

    <bean name='jbpm.job.executor' 
        class='org.jbpm.job.executor.JobExecutor'>
      <field name='jbpmConfiguration'><ref bean='jbpmConfiguration' />
      </field>
      <field name='name'><string value='JbpmJobExecutor' /></field>
      <field name='nbrOfThreads'><int value='1' /></field>
      <field name='idleInterval'><int value='60000' /></field>
      <field name='retryInterval'><int value='4000' /></field>
      <!-- 1 hour -->
      <field name='maxIdleInterval'><int value='3600000' /></field> 
      <field name='historyMaxSize'><int value='20' /></field>
      <!-- 10 minutes -->
      <field name='maxLockTime'><int value='600000' /></field> 
      <!-- 1 minute -->
      <field name='lockMonitorInterval'><int value='60000' /></field> 
       <!-- 5 seconds -->
      <field name='lockBufferTime'><int value='5000' /></field>
    </bean>
</jbpm-configuration>
上記のファイルには、次の 3 つの部分が含まれています。
  1. JbpmContext を設定する一連の サービス実装。(可能な設定オプションは、特定のサービスの実装に関する章で詳しく説明されています。)
  2. 設定リソースへの参照をリンクするすべてのマッピング。設定ファイルの 1 つをカスタマイズする場合は、これらのマッピングを更新します。これを行うには、必ず最初にデフォルトの設定ファイル (jbpm-3.x.jar) をクラスパス上の別の場所にバックアップしてください。その後、jBPM が使用するカスタマイズされたバージョンを指すように、このファイルの参照を更新してください。
  3. jBPM で使用するためのさまざまな設定。(これらは、該当する特定のトピックに関する章で説明されています。)
デフォルト設定は、依存関係が最小限の単純な Web アプリケーション環境向けに最適化されています。永続性サービスは、他のすべてのサービスで使用される JDBC 接続を取得します。したがって、すべてのワークフロー操作は、JDBC 接続上の単一のトランザクションに配置されるため、一元化されます (トランザクションマネージャーは必要ありません)。
JbpmContext には、ほとんどの一般的なプロセス操作のための 便利なメソッド が含まれています。これらを次のコードサンプルに示します。
public void deployProcessDefinition(ProcessDefinition processDefinition)
public List getTaskList()
public List getTaskList(String actorId)
public List getGroupTaskList(List actorIds)
public TaskInstance loadTaskInstance(long taskInstanceId)
public TaskInstance loadTaskInstanceForUpdate(long taskInstanceId)
public Token loadToken(long tokenId)
public Token loadTokenForUpdate(long tokenId)
public ProcessInstance loadProcessInstance(long processInstanceId)
public ProcessInstance loadProcessInstanceForUpdate(long processInstanceId)
public ProcessInstance newProcessInstance(String processDefinitionName)
public void save(ProcessInstance processInstance)
public void save(Token token)
public void save(TaskInstance taskInstance)
public void setRollbackOnly()
注記
save メソッドを明示的に呼び出す必要はありません。XxxForUpdate メソッドは、ロードされたオブジェクトを "自動保存" に登録するように設計されているためです。
複数の jbpm-context を指定することができます。これを行うには、それぞれに一意の name 属性を指定する必要があります。(JbpmConfiguration.createContext(String name); を使用して、名前付きコンテキストを取得します。)
service 要素は、独自の名前と、関連する サービスファクトリー を指定します。このサービスは、JbpmContext.getServices().getService(String name) によって作成を要求された場合にのみ作成されます。
注記
ファクトリー は、属性ではなく 要素 として指定することもできます。これは、一部の設定情報をファクトリーオブジェクトに挿入するときに必要です。
オブジェクトの作成とワイヤリング、および XML の解析を担当するコンポーネントは、オブジェクトファクトリー と呼ばれることに注意してください。

3.1. ファクトリーのカスタマイズ

警告
ファクトリーをカスタマイズする際によく発生する間違いは、長い表記と短い表記を混在させることです。(短い表記の例は、デフォルトの設定ファイルで確認できます。)
Hibernate は、StateObjectStateException 例外をログに記録し、スタックトレース を生成します。後者を削除するには、org.hibernate.event.def.AbstractFlushingEventListenerFATAL に設定します。(log4j を使用している場合、削除するには設定に log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=FATAL 行を設定します。
&lt;service name='persistence' 
    factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' /&gt;
重要
サービスに関する特定のプロパティーを記述する必要がある場合は、長い表記しか使用できません。
<service name="persistence">
    <factory>
        <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
            <field name="dataSourceJndiName">
                <string value="java:/myDataSource"/>
            </field>
            <field name="isCurrentSessionEnabled"><true /></field>
            <field name="isTransactionEnabled"><false /></field>
        </bean>
    </factory>
</service>

3.2. 設定プロパティー

jbpm.byte.block.size
添付ファイルとバイナリー変数は、固定サイズのバイナリーオブジェクトリストの形式でデータベースに格納されます。(これの目的は、異なるデータベース間の移植性を向上させることです。これにより、jBPM をより簡単に埋め込むこともできます。) このパラメーターは、これらの固定長チャンクのサイズを制御します。
jbpm.task.instance.factory
タスクインスタンスの作成方法をカスタマイズするには、このプロパティーに対して完全修飾クラス名を指定します。(これは、TaskInstance Bean をカスタマイズして新しいプロパティーを追加する場合に必要になることがよくあります。) 指定されたクラス名が org.jbpm.taskmgmt.TaskInstanceFactory インターフェイスを実装していることを確認してください。(詳細は、「 タスクインスタンスのカスタマイズ 」 を参照してください。)
jbpm.variable.resolver
これを使用して、"JSF" ライクな式に含まれる最初の用語を jBPM が検索する方法をカスタマイズします。
jbpm.class.loader
このプロパティーを使用して jBPM クラスをロードします。
jbpm.sub.process.async
このプロパティーを使用して、サブプロセスの非同期シグナリングを許可します。
jbpm.job.retries
この設定は、失敗したジョブを破棄するタイミングを決定します。設定ファイルを確認すれば、そのようなジョブを破棄する前に指定した回数だけ処理を試行するようにエントリーを設定できます。
jbpm.mail.from.address
このプロパティーは、ジョブがどこから来たかを表示します。デフォルトは jbpm@noreply です。

3.3. その他の設定ファイル

jBPM には、カスタマイズ可能な多数の設定ファイルがあります。
hibernate.cfg.xml
これには、Hibernate マッピングリソースファイルへの参照と設定の詳細が含まれます。
jBPM が使用する hibernate.cfg.xml ファイルを変更するには、jbpm.cfg.xml ファイルで次のプロパティーを設定します。
<string name="resource.hibernate.cfg.xml" value="new.hibernate.cfg.xml"/> jbpm.cfg.xml ファイルは、${soa.home}/jboss-as/server/${server.config}/jbpm.esb にあります。
org/jbpm/db/hibernate.queries.hbm.xml
このファイルには、jBPM セッション (org.jbpm.db.*Session) で使用される Hibernate クエリーが含まれています。
org/jbpm/graph/node/node.types.xml
このファイルは、XML ノード要素を Node 実装クラスにマップするために使用されます。
org/jbpm/graph/action/action.types.xml
このファイルは、XML アクション要素を Action 実装クラスにマップするために使用されます。
org/jbpm/calendar/jbpm.business.calendar.properties
これには、"業務時間" と "自由時間" の定義が含まれています。
org/jbpm/context/exe/jbpm.varmapping.xml
これは、プロセス変数の値 (Java オブジェクト) を変数インスタンスに変換して jBPM データベースに保存する方法を指定します。
org/jbpm/db/hibernate/jbpm.converter.properties
これは、id-to-classname マッピングを指定します。id はデータベースに格納されます。org.jbpm.db.hibernate.ConverterEnumType クラスは、識別子を シングルトン オブジェクトにマップするために使用されます。
org/jbpm/graph/def/jbpm.default.modules.properties
これは、新しい ProcessDefinition にデフォルトで追加するモジュールを指定します。
org/jbpm/jpdl/par/jbpm.parsers.xml
これは、プロセスアーカイブ解析 のフェーズを指定します。

3.4. 楽観的な並行処理の例外のロギング

jBPM をクラスター設定で実行すると、jBPM は 楽観的ロック を使用してデータベースと同期します。これは、各操作をトランザクションで実行し、最後に競合が検出された場合、そのトランザクションをロールバックし、再試行して処理する必要があることを意味します。これにより、org.hibernate.StateObjectStateException 例外が発生することがあります。これが発生した場合、Hibernate は単純なメッセージで例外をログに記録します。
optimistic locking
            failed
.
Hibernate は、スタックトレース付きで StateObjectStateException をログに記録することもできます。これらのスタックトレースを削除するには、org.hibernate.event.def.AbstractFlushingEventListener クラスを FATAL に設定します。次の設定を使用して、log4j でこれを行います。
log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=FATAL
jBPM スタックトレースをログに記録するには、パッケージのログカテゴリーのしきい値を ERROR より上に設定します。

3.5. オブジェクトファクトリー

オブジェクトファクトリー は、"Bean ライク" な XML 設定ファイルに含まれる仕様に合わせてオブジェクトを構築できます。このファイルは、完全なオブジェクトグラフを形成するためにオブジェクトを作成、設定、およびワイヤリングする方法を指示します。また、オブジェクトファクトリーを使用して、設定と他の Bean を単一の Bean に注入します。
以下の例に示すように、最も基本的な形式では、オブジェクトファクトリーはそのような設定から基本 と Java Bean の両方を作成できます。
<beans>
    <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance"/>
    <string name="greeting">hello world</string>
    <int name="answer">42</int>
    <boolean name="javaisold">true</boolean>
    <float name="percentage">10.2</float>
    <double name="salary">100000000.32</double>
    <char name="java">j</char>
    <null name="dusttodust" />
</beans>
ObjectFactory of = ObjectFactory.parseXmlFromAbove();
assertEquals(TaskInstance.class, of.getNewObject("task").getClass());
assertEquals("hello world", of.getNewObject("greeting"));
assertEquals(new Integer(42), of.getNewObject("answer"));
assertEquals(Boolean.TRUE, of.getNewObject("javaisold"));
assertEquals(new Float(10.2), of.getNewObject("percentage"));
assertEquals(new Double(100000000.32), of.getNewObject("salary"));
assertEquals(new Character('j'), of.getNewObject("java"));
assertNull(of.getNewObject("dusttodust"));]]>
このコードは、リストを設定する方法を示しています。
<beans>
    <list name="numbers">
        <string>one</string>
        <string>two</string>
        <string>three</string>
    </list>
</beans>
このコードは、マップを設定する方法を示しています。
<beans>
    <map name="numbers">
        <entry>
            <key><int>1</int></key>
            <value><string>one</string></value>
        </entry>
        <entry>
            <key><int>2</int></key>
            <value><string>two</string></value>
        </entry>
        <entry>
            <key><int>3</int></key>
            <value><string>three</string></value>
        </entry>
    </map>
</beans>
ダイレクトフィールドインジェクション とプロパティーの setter メソッドを使用して、Bean を設定します。
<beans>
    <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
        <field name="name"><string>do dishes</string></field>
        <property name="actorId"><string>theotherguy</string></property>
    </bean>
</beans>
Bean を参照できます。参照されるオブジェクトは、Bean 自体である必要はありません。文字列や整数など、必要なものを参照できます。
<beans>
    <bean name="a" class="org.jbpm.A" />
    <ref name="b" bean="a" />
</beans>
次のコードが示すように、Bean は任意のコンストラクターで構築できます。
<beans>
    <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
        <constructor>
            <parameter class="java.lang.String">
                <string>do dishes</string>
            </parameter>
            <parameter class="java.lang.String">
                <string>theotherguy</string>
            </parameter>
        </constructor>
    </bean>
</beans>
Bean は、factory メソッドを使用して構築できます。
<beans>
    <bean name="taskFactory" 
        class="org.jbpm.UnexistingTaskInstanceFactory" 
        singleton="true"/>

    <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
        <constructor factory="taskFactory" method="createTask" >
            <parameter class="java.lang.String">
                <string>do dishes</string>
            </parameter>
            <parameter class="java.lang.String">
                <string>theotherguy</string>
            </parameter>
        </constructor>
    </bean>
</beans>
Bean は、クラスの static factory メソッドを使用して構築できます。
<beans>
    <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
        <constructor
            factory-class="org.jbpm.UnexistingTaskInstanceFactory"
            method="createTask" >
            <parameter class="java.lang.String">
                <string>do dishes</string>
            </parameter>
            <parameter class="java.lang.String">
                <string>theotherguy</string>
            </parameter>
        </constructor>
    </bean>
</beans>
属性 singleton="true" を使用して、名前付きの各オブジェクトを シングルトン としてマークします。そうすることで、特定の オブジェクトファクトリー が各リクエストに対して常に同じオブジェクトを返すようになります。
注記
シングルトン は、異なるオブジェクトファクトリー間で共有できません。
シングルトン 機能は、getObjectgetNewObject というメソッドの区別を生み出します。通常は、getNewObject を使用する必要があります。これを使用すると、新しいオブジェクトグラフを構築する前に、オブジェクトファクトリーオブジェクトキャッシュ がクリアされるためです。
オブジェクトグラフの構築時に、非シングルトンオブジェクト は、オブジェクトファクトリー のキャッシュに格納されます。これにより、1 つのオブジェクトへの参照を共有できます。シングルトンオブジェクトキャッシュプレーンオブジェクトキャッシュ とは異なることに注意してください。シングルトン キャッシュは決してクリアされませんが、プレーンキャッシュは getNewObject メソッドが開始されるたびにクリアされます。
この章では、jBPM のさまざまな設定方法について詳しく説明しました。

第4章 永続性

この章では、Business Process Manager の "永続性" 機能について詳しく説明します。
ほとんどの場合、jBPM は複数のトランザクションにまたがるプロセスを実行するために使用されます。永続性 機能の主な目的は、待機状態 が発生したときにプロセスの実行を保存することです。プロセスの実行は、ステートマシン として考えると理解しやすくなります。その目的は、プロセス実行ステートマシンを、1 つのトランザクションの中で、ある状態から次の状態に移動させることです。
プロセス定義は、XML、Java オブジェクト、または jBPM データベースレコードの 3 つの異なる形式のいずれかで表すことができます。(また、ランタイムデータとログ情報は、Java オブジェクトまたは jBPM データベースレコードの形式で表すことができます。)

図4.1 変換とさまざまな形式

変換とさまざまな形式
注記
プロセス定義とプロセスアーカイブの XML 表現の詳細は、14章 jBPM プロセス定義言語 を参照してください。
注記
プロセスアーカイブをデータベースにデプロイする方法の詳細は、「 プロセスアーカイブのデプロイ 」 を参照してください。

4.1. 永続性アプリケーションプログラミングインターフェイス

4.1.1. 設定フレームワークとの関係

永続性アプリケーションプログラミングインターフェイスは、設定フレームワークと統合されています (3章 設定 を参照)。 これは、JbpmContextconvenience persistence メソッドの一部を公開し、jBPM の コンテキストブロック が永続性 API 操作を呼び出せるようにすることで実現されています。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  // Invoke persistence operations here
} finally {
  jbpmContext.close();
}

4.1.2. JbpmContext の便利なメソッド

最も一般的に実行される 3 つの永続性操作は次のとおりです。
  1. プロセスデプロイメント
  2. 新しいプロセスの実行開始
  3. プロセスの実行継続
プロセスデプロイメント は通常、Graphical Process Designer または deployprocess ant タスクから直接実行されます。ただし、Java から直接実行するには、次のコードを使用します。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  ProcessDefinition processDefinition = ...;
  jbpmContext.deployProcessDefinition(processDefinition);
} finally {
  jbpmContext.close();
}
新しいプロセス実行のインスタンス化の対象になるプロセス定義を指定して、新しいプロセス実行を作成します。これを行う最も一般的な方法は、プロセスの名前を参照することです。すると、jBPM はデータベース内でそのプロセスの最新バージョンを検索します。以下にデモコードを示します。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	String processName = ...;
	ProcessInstance processInstance = 
		jbpmContext.newProcessInstance(processName);
} finally {
	jbpmContext.close();
}
プロセスの実行を続行するには、データベースからプロセスインスタンス、トークン、または taskInstance を取得し、POJO (Plain Old Java Object) jBPM オブジェクトでいくつかのメソッドを呼び出します。さrない、processInstance に加えられた更新をデータベースに保存します。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	long processInstanceId = ...;
	ProcessInstance processInstance = 
		jbpmContext.loadProcessInstance(processInstanceId);
		processInstance.signal();
		jbpmContext.save(processInstance);
} finally {
	jbpmContext.close();
}
ForUpdate メソッドを JbpmContext クラスで使用する場合、jbpmContext.save メソッドを明示的に呼び出す必要はないことに注意してください。これは、jbpmContext クラスが閉じられると、保存プロセスが自動的に実行されるためです。たとえば、taskInstance が完了したことを jBPM に通知したい場合があります。これにより実行の継続が引き起こされることがあるため、taskInstance に関連する processInstance を保存する必要があります。これを行う最も便利な方法は、loadTaskInstanceForUpdate メソッドを使用することです。
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	long taskInstanceId = ...;
	TaskInstance taskInstance = 
		jbpmContext.loadTaskInstanceForUpdate(taskInstanceId);
	taskInstance.end();
	} 
finally {
		jbpmContext.close();
}
重要
以下の説明を読み、jBPM による永続性機能の管理方法と Hibernate の機能の使用方法を確認してください。
JbpmConfiguration は、一連の ServiceFactories を維持します。これらは jbpm.cfg.xml ファイルを介して設定され、必要に応じてインスタンス化されます。
DbPersistenceServiceFactory は、最初に必要になったときにのみインスタンス化されます。その後、ServiceFactoryJbpmConfiguration で維持されます。
DbPersistenceServiceFactoryHibernate ServiceFactory を管理しますが、これは最初に要求されたときにのみインスタンス化されます。
DbPersistenceServiceFactory パラメーター:
  1. isTransactionEnabled
  2. sessionFactoryJndiName
  3. dataSourceJndiName
  4. isCurrentSessionEnabled

図4.2 永続性関連のクラス

永続性関連のクラス
jbpmConfiguration.createJbpmContext () クラスが呼び出されると、JbpmContext のみが作成されます。この時点では、それ以上の永続性関連の初期化は行われません。JbpmContext は、DbPersistenceService クラスを管理します。このクラスは、最初に要求されたときにインスタンス化されます。DbPersistenceService クラスは、Hibernate セッションを管理します。これも、最初に必要になったときにのみインスタンス化されます。(つまり、Hibernate セッションは、永続性を必要とする最初の操作が呼び出されたときにのみ開かれます。)

4.2. 永続性サービスの設定

4.2.1. DbPersistenceServiceFactory

DbPersistenceServiceFactory クラスには、さらに isTransactionEnabledsessionFactoryJndiName、および dataSourceJndiName という 3 つの設定プロパティーがあります。jbpm.cfg.xml ファイルでこれらのプロパティーを指定するには、factory 要素内で Bean として Service Factory を指定します。このサンプルコードは、その方法を示しています。
<jbpm-context>
  <service name="persistence">
    <factory>
      <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
        <field name="isTransactionEnabled"><false /></field>
        <field name="sessionFactoryJndiName">
          <string value="java:/myHibSessFactJndiName" />
        </field>
        <field name="dataSourceJndiName">
          <string value="java:/myDataSourceJndiName" />
        </field>
      </bean>
    </factory>
  </service>
...
</jbpm-context>
重要
ファクトリーを設定する際に、短い表記と長い表記を混在させないでください。(「 ファクトリーのカスタマイズ 」 も併せて参照してください。) ファクトリーがクラスの単なる新しいインスタンスである場合は、ファクトリー属性を使用してそのファクトリークラス名を参照します。一方、ファクトリーのプロパティーに設定が必要な場合は、長い表記を使用する必要があり、ファクトリーと Bean をネストされた要素として組み合わせる必要があります。
isTransactionEnabled
デフォルトでは、jBPM はセッションが初めて取得されたときに Hibernate トランザクションを開始します。jbpmContext が閉じられると、Hibernate トランザクションは終了します。その後、jbpmContext.setRollbackOnly が呼び出されたかどうかに応じて、トランザクションはコミットまたはロールバックされます。(isRollbackOnly プロパティーは TxService で維持されます。) トランザクションを無効にし、jBPM が Hibernate でそれらを管理するのを禁止するには、isTransactionEnabled プロパティー値を false に設定します。(このプロパティーは jbpmContext の動作のみを制御します。DbPersistenceService.beginTransaction() は、isTransactionEnabled 設定を無視するアプリケーションプログラミングインターフェイスを使用して直接呼び出すことができます。) トランザクションの詳細は、「 Hibernate トランザクション 」 を参照してください。
sessionFactoryJndiName
デフォルトでは、これは null です。これは、セッションファクトリーが JNDI から取得されないことを意味します。このプロパティーが設定されていて、Hibernate セッションを作成するためにセッションファクトリーが必要な場合は、JNDI から取得されます。
dataSourceJndiName
デフォルトでは、これは null です。その結果、JDBC 接続が作成され、Hibernate に委譲されます。データソースを指定すると、Business Process Manager は新しいセッションを開くとともに、データソースから JDBC 接続を取得し、それを Hibernate に提供します。

4.2.1.1. Hibernate セッションファクトリー

デフォルトでは、DbPersistenceServiceFactory はクラスパスのルートにある hibernate.cfg.xml ファイルを使用して、Hibernate セッションファクトリーを作成します。Hibernate 設定ファイルのリソースが jbpm.hibernate.cfg.xml にマップされていることに注意してください。これは、jbpm.cfg.xml を再設定してカスタマイズします。
<jbpm-configuration>
    <!-- configuration resource files pointing to default
         configuration files in jbpm-{version}.jar -->
    <string name='resource.hibernate.cfg.xml' 
        value='hibernate.cfg.xml' />
    <!-- <string name='resource.hibernate.properties' 
        value='hibernate.properties' /> -->
</jbpm-configuration>
重要
resource.hibernate.properties が指定されている場合、そのリソースファイルのプロパティーは、hibernate.cfg.xml のすべてのプロパティーを上書きします。データベースを参照するように hibernate.cfg.xml を更新するのではなく、hibernate.properties を使用して jBPM のアップグレードを処理してください。そうすることで、変更を再適用することなく hibernate.cfg.xml ファイルをコピーできます。

4.2.1.2. C3PO 接続プールの設定

http://www.hibernate.org/214.html で Hibernate のドキュメントを参照してください。

4.2.1.3. ehCache プロバイダーの設定

JBossCache を使用して jBPM を設定する方法については、http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmConfiguration を参照してください。
Hibernate と連携するようにキャッシュプロバイダーを設定する方法については、http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-cache を参照してください。
jBPM に付属する hibernate.cfg.xml ファイルには、次の行が含まれています。
<property name="hibernate.cache.provider_class">
    org.hibernate.cache.HashtableCacheProvider
</property>
これは、ユーザーがクラスパスの設定を気にする必要がないように提供されています。
警告
HibernateHashtableCacheProvider を実稼働環境で使用しないでください。
HashtableCacheProvider の代わりに ehcache を使用するには、関連する行をクラスパスから削除し、代わりに ehcache.jar を使用します。お使いの環境と互換性のある正しい ehcache ライブラリーバージョンの検索が必要になる場合があることに注意してください。

4.2.2. Hibernate トランザクション

デフォルトでは、jBPM は "トランザクションごとのセッション" パターンを使用してトランザクションを Hibernate に委譲します。jbpmContext で永続的な操作が呼び出されたときに初めてセッションが開かれると、jBPM は Hibernate トランザクションを開始します。トランザクションは、Hibernate セッションが閉じられる直前にコミットされます。これは jbpmContext.close() 内で実行されます。
jbpmContext.setRollbackOnly() を使用して、ロールバック対象のトランザクションをマークします。そうすることで、セッションが jbpmContext.close() メソッド内で閉じられる直前にトランザクションがロールバックされます。
Business Process Manager が Hibernate アプリケーションプログラミングインターフェイスを介してトランザクションメソッドを呼び出すことを禁止するには、isTransactionEnabled プロパティーを false に設定します。これについては、「DbPersistenceServiceFactory」 で詳しく説明しています。

4.2.3. JTA トランザクション

マネージドトランザクションは、jBPM が JBoss Application Server で使用されている場合に最もよく見られます。次のコードサンプルは、トランザクションを JTA にバインドする一般的な方法を示しています。
<jbpm-context>
  <service name="persistence">
    <factory>
      <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
        <field name="isTransactionEnabled"><false /></field>
        <field name="isCurrentSessionEnabled"><true /></field>
        <field name="sessionFactoryJndiName">
          <string value="java:/myHibSessFactJndiName" />
        </field>
      </bean>
    </factory>
  </service>
</jbpm-context>
次に、データソースを使用するように Hibernate セッションファクトリーを設定して、Hibernate 自体を Transaction Manager にバインドします。複数のデータソースを使用する場合は、それらのデータベースを XA datasource にバインドします。
<hibernate-configuration>
    <session-factory>

        <!-- hibernate dialect -->
        <property name="hibernate.dialect">
            org.hibernate.dialect.HSQLDialect
        </property>

        <!-- DataSource properties (begin) -->
        <property name="hibernate.connection.datasource">
            java:/JbpmDS
        </property>

        <!-- JTA transaction properties (begin) -->
        <property name="hibernate.transaction.factory_class">
            org.hibernate.transaction.JTATransactionFactory
        </property>
    
        <property name="hibernate.transaction.manager_lookup_class">
            org.hibernate.transaction.JBossTransactionManagerLookup
        </property>

        <property name="jta.UserTransaction">
            java:comp/UserTransaction
        </property>

    </session-factory>
</hibernate-configuration>
注記
Hibernate を Transaction Manager にバインドする方法の詳細は、http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#configuration-optional-transactionstrategy を参照してください。
次に、XA datasource を使用するように Hibernate を設定します。
これらの設定により、エンタープライズ Bean が CMT を使用できるようになり、Web コンソールが BMT を使用できるようになります。(jta.UserTransaction も指定されているのはそのためです。)

4.2.4. クエリーのカスタマイズ

jBPM が使用するすべての SQL クエリーは、1 つの中央設定ファイルにあります。このリソースファイルは、hibernate.cfg.xml 設定ファイルで参照されます。
<hibernate-configuration>
    <!-- hql queries and type defs -->
    <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
</hibernate-configuration>
これらのクエリーの 1 つ以上をカスタマイズするには、元のファイルのバックアップを作成します。次に、カスタマイズしたバージョンをクラスパスの任意の場所に配置し、カスタマイズしたバージョンを参照するように hibernate.cfg.xmlorg/jbpm/db/hibernate.queries.hbm.xml への参照を更新します。

4.2.5. データベースの互換性

jBPM は、Hibernate でサポートされているすべてのデータベースで実行されます。

4.2.5.1. JDBC 接続の分離レベル

JDBC 接続のデータベース分離レベルを少なくともREAD_COMMITTED に設定します。
警告
READ_UNCOMMITTED (分離レベルゼロ、Hypersonic でサポートされる唯一の分離レベル) に設定されている場合、ジョブエグゼキューター で競合状態が発生する可能性があります。これは、複数のトークンの同期が行われているときにも発生する可能性があります。

4.2.5.2. データベースの変更

別のデータベースを使用するように Business Process Manger を再設定するには、次の手順に従います。
  • JDBC ドライバーライブラリーアーカイブをクラスパスに配置します。
  • jBPM が使用する Hibernate 設定を更新します。
  • 新しいデータベースにスキーマを作成します。

4.2.5.3. データベーススキーマ

jbpm.db サブプロジェクトには、ユーザーが選択したデータベースの使用を開始するのに役立つドライバー、手順、およびスクリプトが含まれています。詳細は、jbpm.db プロジェクトのルートにある readme.html を参照してください。
注記
JBPM は任意のデータベースの DDL スクリプトを生成できますが、これらのスキーマは常に効率的であるとは限りません。会社のデータベース管理者に生成された DDL を確認してもらい、列の型とインデックスを最適化できるようにすることを検討してください。
次の Hibernate 設定オプションは、開発環境で使用できます。hibernate.hbm2ddl.autocreate-drop に設定すると、データベースがアプリケーションで初めて使用されるときに、スキーマが自動的に作成されます。アプリケーションが終了すると、スキーマは削除されます。
4.2.5.3.1. プログラムによるデータベーススキーマ操作
jBPM は、org.jbpm.JbpmConfiguration のメソッド createSchema および dropSchema を介して、データベーススキーマを作成および削除するための API を提供します。これらのメソッドの呼び出しには、設定されたデータベースユーザーの権限以外に制約がないことに注意してください。
注記
前述の API は、クラス org.jbpm.db.JbpmSchema によって提供される、より広範な機能へのファサードを構成します。

4.2.5.4. Hibernate クラスの組み合わせ

Hibernate と jBPM の永続クラスを組み合わせると、2 つの大きなメリットが得られます。セッション、接続、およびトランザクションの管理が容易になります。これは、これらを 1 つの Hibernate セッションファクトリーに結合することで、Hibernate セッションと JDBC 接続が 1 つだけになるためです。したがって、jBPM の更新は、ドメインモデルの更新と同じトランザクションになります。これにより、Transaction Manager が不要になります。
第 2 に、Hibernate 永続オブジェクトを追加作業なしでプロセス変数にドロップできます。
これを実現するには、中央の hibernate.cfg.xml ファイルを 1 つ作成します。デフォルトの jBPM hibernate.cfg.xml を出発点として使用し、独自の Hibernate マッピングファイルへの参照を追加してカスタマイズするのが最も簡単です。

4.2.5.5. jBPM Hibernate マッピングファイルのカスタマイズ

次の手順に従って、jBPM Hibernate マッピングファイルをカスタマイズします。
  1. ソース (src/jbpm-jpdl-sources.jar) から jBPM Hibernate マッピングファイルをコピーします。
  2. コピーをクラスパスの任意の場所に配置します (以前と同じ場所でないことを確認します)。
  3. hibernate.cfg.xml 内のカスタマイズしたマッピングファイルへの参照を更新します。

4.2.5.6. 2 次キャッシュ

jBPM は Hibernate二次キャッシュ を使用して、一度ロードされたプロセス定義をメモリーに保持します。プロセス定義のクラスとコレクションは、キャッシュ要素が次のようになるように、Hibernate マッピングファイルで設定されます。
<cache usage="nonstrict-read-write"/>
プロセス定義は決して変更されないため、2 次キャッシュに保持しても問題ありません。(「 デプロイされたプロセス定義の変更 」 も併せて参照してください。)
デフォルトのキャッシュストラテジーは、nonstrict-read-write に設定されています。ランタイム実行時に、プロセス定義が静的なままとなるため、最大限のキャッシュを実現できます。理論的には、read-only キャッシュストラテジーのほうがランタイム実行にはさらに適していますが、その設定では新しいプロセス定義のデプロイメントが許可されません。
この章では、Hibernate を最大限に活用する方法など、jBPM の永続性のトピックに関連する多くの理論的情報と実践的なアドバイスを説明しました。

第5章 Java EE アプリケーションサーバーの機能

この章でが、jBPM が提供する、Java EE インフラストラクチャーを活用するために使用できる機能を説明します。

5.1. エンタープライズ Bean

CommandServiceBeanステートレスセッション Bean であり、別の jBPM コンテキスト内の execute メソッドを呼び出すことにより、Business Process Manager コマンドを実行します。使用可能な環境エントリーとカスタマイズ可能なリソースを次の表にまとめます。

表5.1 コマンドサービス Bean 環境

名前 タイプ 説明
JbpmCfgResource 環境エントリー これは、jBPM 設定の読み取り元となるクラスパスリソースです。オプションであり、デフォルトは jbpm.cfg.xml です。
ejb/TimerEntityBean EJB 参照 これは、スケジューラーサービスを実装するローカルエンティティー Bean へのリンクです。タイマーを含むプロセスに必要です。
jdbc/JbpmDataSource リソースマネージャー参照 これは、jBPM 永続性サービスへの JDBC 接続を提供するデータソースの論理名です。Hibernate 設定ファイルの hibernate.connection.datasource プロパティーと一致する必要があります。
jms/JbpmConnectionFactory リソースマネージャー参照 これは、JMS 接続を jBPM メッセージサービスに提供するファクトリーの論理名です。非同期継続を含むプロセスに必要です。
jms/JobQueue メッセージ宛先参照 jBPM メッセージサービスは、ジョブメッセージをこのキューに送信します。ジョブリスナー Bean のメッセージ受信元のキューとこのキューが必ず同じになるように、message-destination-link は共通の論理宛先である JobQueue を参照します。
jms/CommandQueue メッセージ宛先参照 コマンドリスナー Bean は、このキューからメッセージを受信します。コマンドメッセージ送信先のキューとこのキューが必ず同じになるように、message-destination-link 要素は共通の論理宛先である CommandQueue を参照します。
CommandListenerBean は、コマンドメッセージの CommandQueue をリッスンするメッセージ駆動型 Bean です。この Bean は、コマンドの実行を CommandServiceBean に委譲します。
メッセージの本文は、org.jbpm.Command インターフェイスを実装できる Java オブジェクトでなければなりません。(メッセージプロパティーがある場合は無視されます。) メッセージが想定される形式でない場合、メッセージは DeadLetterQueue に転送され、それ以上処理されません。宛先参照が存在しない場合も、メッセージは拒否されます。
受信したメッセージで宛先 replyTo が指定されている場合、コマンド実行結果が オブジェクトメッセージ にラップされて送信されます。
コマンド接続ファクトリー環境参照 は、Java Message Service 接続を提供するために使用されるリソースマネージャーを参照します。
逆に、JobListenerBean は、非同期継続 をサポートするために、ジョブメッセージの JbpmJobQueue をリッスンするメッセージ駆動型 Bean です。
注記
メッセージには、long 型の jobId というプロパティーが必要であることに注意してください。このプロパティには、データベース内にある保留中の Job への参照が含まれている必要があります。メッセージ本文が存在する場合、それは無視されます。
この Bean は CommandListenerBean を拡張します。後者の環境エントリーとカスタマイズ可能なそのリソース参照を継承します。

表5.2 コマンド/ジョブリスナー Bean 環境

名前 タイプ 説明
ejb/LocalCommandServiceBean EJB 参照 これは、別の jBPM コンテキストでコマンドを実行するローカルセッション Bean へのリンクです。
jms/JbpmConnectionFactory リソースマネージャー参照 これは、結果メッセージを生成するための Java Message Service 接続を提供するファクトリーの論理名です。返信先を示すコマンドメッセージに必要です。
jms/DeadLetterQueue メッセージ宛先参照 コマンドを含まないメッセージは、ここで参照されているキューに送信されます。これはオプションです。これがない場合、そのようなメッセージは拒否され、コンテナーが再配信されることがあります。
-   
メッセージ宛先参照 コマンドを含まないメッセージは、ここで参照されているキューに送信されます。これがない場合、そのようなメッセージは拒否され、コンテナーが再配信されることがあります。 
TimerEntityBean は、Enterprise Java Bean タイマーサービス でスケジューリングに使用されます。Bean の有効期限が切れると、タイマーの実行が command service Bean に委譲されます。
TimerEntityBean は、Business Process Manager のデータソースへのアクセスを必要とします。Enterprise Java Bean デプロイメント記述子は、エンティティー Bean をデータベースにマップする方法を定義しません。(これはコンテナー提供者に任されています。) JBoss Application Server では、jbosscmp-jdbc.xml 記述子が、データソースの JNDI 名とリレーショナルマッピングデータ (テーブル名や列名など) を定義します。
注記
JBoss CMP (コンテナー管理永続性) 記述子は、リソースマネージャー参照 (java:comp/env/jdbc/JbpmDataSource) とは異なり、グローバル JNDI 名 (java:JbpmDS) を使用します。
注記
以前のバージョンの Business Process Manager は、TimerServiceBean というステートレスセッション Bean を使用して、Enterprise Java Bean タイマーサービスと対話していました。セッション方式は、cancelation メソッドのボトルネックが避けられなかったため、断念せざるを得ませんでした。セッション Bean には ID がないため、タイマーサービスは すべて のタイマーをイテレートして、キャンセルする必要のあるタイマーを見つける必要がありました。
この Bean は、下位互換性を確保する目的で引き続き使用できます。TimerEntityBean と同じ環境で動作するため、移行が容易です。

表5.3 タイマーエンティティー/サービス Bean 環境

名前 タイプ 説明
ejb/LocalCommandServiceBean EJB 参照 これは、別の jBPM コンテキストでタイマーを実行するローカルセッション Bean へのリンクです。

5.2. jBPM エンタープライズ設定

次の設定項目が jbpm.cfg.xml に含まれています。
<jbpm-context>
  <service name="persistence"
    factory="org.jbpm.persistence.jta.JtaDbPersistenceServiceFactory" />
  <service name="message"
    factory="org.jbpm.msg.jms.JmsMessageServiceFactory" />
  <service name="scheduler"
    factory="org.jbpm.scheduler.ejbtimer.EntitySchedulerServiceFactory" />
</jbpm-context>
JtaDbPersistenceServiceFactory により、Business Process Manager は JTA トランザクションに参加できます。既存のトランザクションが進行中の場合、JTA 永続性サービスはそれを "保持" します。そうでない場合は、新しいトランザクションを開始します。Business Process Manager のエンタープライズ Bean は、トランザクション管理をコンテナーに委譲するように設定されています。ただし、トランザクションがアクティブでない環境 (Web アプリケーションなど) で JbpmContext を作成すると、新しいトランザクションが自動的に開始されます。 JTA 永続性サービスファクトリー には、以下で説明する設定可能なフィールドが含まれています。
isCurrentSessionEnabled
これを true に設定すると、Business Process Manager は、進行中の JTA トランザクションに関連付けられた "現在" の Hibernate セッションを使用します。これはデフォルト設定です。詳細は、http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture.html#architecture-current-session を参照してください。
コンテキストセッションメカニズムを利用して、アプリケーションの他の部分で jBPM と同じセッションを使用します。これは、SessionFactory.getCurrentSession() の呼び出しを通じて行われます。または、isCurrentSessionEnabledfalse に設定し、JbpmContext.setSession(session) メソッドを介してセッションを注入することで、jBPM に Hibernate セッションを提供します。これにより、jBPM がアプリケーションの他の部分と同じ Hibernate セッションを使用することも保証されます。
注記
Hibernate セッションは (永続性コンテキストなどを介して) ステートレスセッション Bean に注入できます。
isTransactionEnabled
true に設定すると、jBPM は Hibernatetransaction API を介してトランザクションを開始し、JbpmConfiguration.createJbpmContext() メソッドを使用してトランザクションをコミットします。(JbpmContext.close() が呼び出されると、Hibernate セッションは閉じられます。)
警告
これは、Business Process Manager が EAR としてデプロイされている場合に望ましい動作ではないため、isTransactionEnabled はデフォルトでは false に設定されています。(詳細は、http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html#transactions-demarcation を参照してください。)
JmsMessageServiceFactory は、 Java Message Service インターフェイスを通じて公開される信頼性の高い通信インフラストラクチャーを利用して、非同期継続メッセージJobListenerBean に配信します。JmsMessageServiceFactory は、次の設定可能なフィールドを公開します。
connectionFactoryJndiName
これは、JNDI 初期コンテキストでの JMS 接続ファクトリーの名前です。デフォルトは java:comp/env/jms/JbpmConnectionFactory です。
destinationJndiName
これは、ジョブメッセージ送信先の JMS 宛先の名前です。これは、JobListenerBean がメッセージを受信する宛先と一致する必要があります。デフォルトは java:comp/env/jms/JobQueue です。
isCommitEnabled
これは、Business Process Manager が JbpmContext.close() で Java Message Service セッションをコミットする必要があるかどうかを指定します。JMS メッセージサービスによって生成されたメッセージは、現在のトランザクションがコミットされる前に受信されることを意図したものではありません。そのため、このサービスによって作成されたセッションは、常にトランザクション処理されます。デフォルト値は false です。これは、使用中の 接続ファクトリー が XA 対応である場合に適しています。Java Message Service セッションによって生成されるメッセージは、JTA トランザクション全体によって制御されるためです。JMS 接続ファクトリーが XA 対応でない場合は、Business Process Manager が JMS セッションのローカルトランザクションを明示的にコミットするように、このフィールドを true に設定する必要があります。
EntitySchedulerServiceFactory は、ビジネスプロセスタイマーをスケジュールするために使用されます。これは、Enterprise Java Bean コンテナーによって提供される時間指定イベントのトランザクション通知サービスに基づいて構築することによって行われます。EJB スケジューラーサービスファクトリー には、以下で説明する設定可能なフィールドがあります。
timerEntityHomeJndiName
これは、JNDI 初期コンテキストでの TimerEntityBean のローカルホームインターフェイスの名前です。デフォルト値は java:comp/env/ejb/TimerEntityBean です。

5.3. Hibernate エンタープライズ設定

hibernate.cfg.xml ファイルには、次の設定項目が含まれています。他のデータベースまたはアプリケーションサーバーをサポートするには、これらを変更します。
<!-- sql dialect -->
<property name="hibernate.dialect">
    org.hibernate.dialect.HSQLDialect
</property>

<property name="hibernate.cache.provider_class">
  org.hibernate.cache.HashtableCacheProvider
</property>

<!-- DataSource properties (begin) -->
<property name="hibernate.connection.datasource">
    java:comp/env/jdbc/JbpmDataSource
</property>
<!-- DataSource properties (end) -->

<!-- JTA transaction properties (begin) -->
<property name="hibernate.transaction.factory_class">
  org.hibernate.transaction.JTATransactionFactory
</property>
<property name="hibernate.transaction.manager_lookup_class">
  org.hibernate.transaction.JBossTransactionManagerLookup
</property>
<!-- JTA transaction properties (end) -->

<!-- CMT transaction properties (begin) ===
<property name="hibernate.transaction.factory_class">
  org.hibernate.transaction.CMTTransactionFactory
</property>
<property name="hibernate.transaction.manager_lookup_class">
  org.hibernate.transaction.JBossTransactionManagerLookup
</property>
==== CMT transaction properties (end) -->
hibernate.dialect 設定は、お使いのデータベース管理システムに適した設定に置き換えます。(詳細は、http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html#configuration-optional-dialects を参照してください。)
HashtableCacheProvider は、サポートされている他のキャッシュプロバイダーに置き換えることができます。(詳細は、http://www.hibernate.org/hib_docs/v3/reference/en/html/performance.html#performance-cache を参照してください。)
初期状態で、jBPM は JTATransactionFactory を使用するように設定されています。既存のトランザクションが進行中の場合、JTA トランザクションファクトリーはそれを使用します。それ以外の場合は、新しいトランザクションが作成されます。jBPM のエンタープライズ Bean は、トランザクション管理をコンテナーに委譲するように設定されています。ただし、アクティブなトランザクションがないコンテキスト (Web アプリケーションなど) で jBPM API が使用されている場合、トランザクションが自動的に開始されます。
コンテナー管理トランザクション を使用するときに意図しないトランザクションが作成されるのを防ぐには、CMTTransactionFactory に切り替えます。この設定により、Hibernate が常に既存のトランザクションを探し、見つからない場合は問題を報告するようになります。

5.4. クライアントコンポーネント

エンタープライズサービスを活用できる Business Process Manager API に対して直接記述されたクライアントコンポーネントのデプロイメント記述子に対して、適切な環境参照が設定されていることを確認してください。以下の記述子は、クライアントセッション Bean の典型的なものと見なすことができます。
<session>

  <ejb-name>MyClientBean</ejb-name>
  <home>org.example.RemoteClientHome</home>
  <remote>org.example.RemoteClient</remote>
  <local-home>org.example.LocalClientHome</local-home>
  <local>org.example.LocalClient</local>
  <ejb-class>org.example.ClientBean</ejb-class>
  <session-type>Stateless</session-type>
  <transaction-type>Container</transaction-type>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home>
    <local>org.jbpm.ejb.LocalTimerEntity</local>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <res-type>javax.jms.ConnnectionFactory</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <message-destination-type>javax.jms.Queue</message-destination-type>
    <message-destination-usage>Produces</message-destination-usage>
  </message-destination-ref>

</session>
上記の環境参照は、次のようにターゲット運用環境のリソースにバインドできます。JNDI 名は、Business Process Manager エンタープライズ Bean で使用される値と一致することに注意してください。
<session>

  <ejb-name>MyClientBean</ejb-name>
  <jndi-name>ejb/MyClientBean</jndi-name>
  <local-jndi-name>java:ejb/MyClientBean</local-jndi-name>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <jndi-name>java:JbpmDS</jndi-name>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <jndi-name>java:JmsXA</jndi-name>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <jndi-name>queue/JbpmJobQueue</jndi-name>
  </message-destination-ref>

</session>
クライアントコンポーネントがエンタープライズ Bean ではなく Web アプリケーションである場合、デプロイメント記述子は次のようになります。
<web-app>

  <servlet>
    <servlet-name>MyClientServlet</servlet-name>
    <servlet-class>org.example.ClientServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MyClientServlet</servlet-name>
    <url-pattern>/client/servlet</url-pattern>
  </servlet-mapping>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home>
    <local>org.jbpm.ejb.LocalTimerEntity</local>
    <ejb-link>TimerEntityBean</ejb-link>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <res-type>javax.jms.ConnectionFactory</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <message-destination-type>javax.jms.Queue</message-destination-type>
    <message-destination-usage>Produces</message-destination-usage>
    <message-destination-link>JobQueue</message-destination-link>
  </message-destination-ref>

</web-app>
上記の環境参照は、次のコードサンプルのとおり、ターゲット運用環境のリソースにバインドすることもできます。
<jboss-web>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <jndi-name>java:JbpmDS</jndi-name>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <jndi-name>java:JmsXA</jndi-name>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <jndi-name>queue/JbpmJobQueue</jndi-name>
  </message-destination-ref>

</jboss-web>

5.5. まとめ

この章では、Java EE インフラストラクチャーを活用するために使用できる jBPM によって提供される機能を詳しく説明しました。本章の説明をもとに、お客様の企業環境でこれらをテストすることをお勧めします。

第6章 プロセスモデリング

6.1. 役に立つ定義

このセクションでは、このガイドの他の章で使用されている用語について説明します。
プロセス定義 は、ビジネスプロセスの正式な仕様を表すものであり、有向グラフ に基づいています。グラフは、ノードと遷移で構成されます。グラフ内のすべてのノードは、特定のタイプのノードです。ノードタイプはランタイムの動作を定義します。プロセス定義には、開始状態が 1 つしかありません。
トークン は、実行の 1 つのパスです。トークンは、グラフ内のノードへのポインターを保持するランタイムの概念です。
プロセスインスタンス は、プロセス定義の 1 つの実行です。プロセスインスタンスが作成されると、実行のメインパスに対してトークンが生成されます。このトークンはプロセスインスタンスの ルートトークン と呼ばれ、プロセス定義の 開始状態 に配置されます。
シグナルは、トークンにグラフ実行を続行するように指示します。トークンは名前のないシグナルを受信すると、デフォルトの 退出遷移 を介して現在のノードから退出します。シグナルで transition-name が指定されている場合、トークンは指定された遷移を介してそのノードから退出します。プロセスインスタンスに与えられたシグナルは、ルートトークンに委譲されます。
トークンがノードに進入すると、ノードが実行されます。ノード自体は、グラフ実行を継続させる役割があります。グラフ実行の継続は、トークンをノードから退出させることで実現されます。各タイプのノードは、グラフ実行の継続のために異なる動作を実装できます。実行を渡さないノードは 状態 として動作します。
アクション は、プロセス実行中にイベントで実行される Java コードの一部です。グラフ は、ソフトウェア要件の伝達において重要な手段ですが、作成中のソフトウェアの 1 つのビュー (プロジェクション) にすぎません。多くの技術的な詳細は、そこには表示されません。アクションは、グラフィカルな表現以外の技術的な詳細を追加するために使用されるメカニズムです。グラフを配置したら、アクションで装飾することができます。主な イベントタイプ は、ノード進入ノード退出遷移取得 です。
これらの定義を理解したら、次に進み、プロセスモデリングの仕組みを確認してください。

6.2. プロセスグラフ

プロセス定義は、ノードと遷移で構成されるグラフです。この情報は XML で表現され、processdefinition.xml というファイルにあります。各ノードには タイプ が必要です (状態決定フォーク、および ジョイン など)。 各ノードには、一連の 退出遷移 があります。ノードから退出する遷移には、それらを区別するために名前を付けることができます。たとえば、次の図は、オークションプロセスのプロセスグラフを示しています。

図6.1 オークションプロセスのグラフ

オークションプロセスのグラフ
以下は、同じオークションプロセスを XML で表したプロセスグラフです。
<process-definition>

  <start-state>
    <transition to="auction" />
  </start-state>
  
  <state name="auction">
    <transition name="auction ends" to="salefork" />
    <transition name="cancel" to="end" />
  </state>
  
  <fork name="salefork">
    <transition name="shipping" to="send item" />
    <transition name="billing" to="receive money" />
  </fork>
  
  <state name="send item">
    <transition to="receive item" />
  </state>

  <state name="receive item">
    <transition to="salejoin" />
  </state>
  
  <state name="receive money">
    <transition to="send money" />
  </state>

  <state name="send money">
    <transition to="salejoin" />
  </state>
  
  <join name="salejoin">
    <transition to="end" />
  </join>
  
  <end-state name="end" />
  
</process-definition>

6.3. ノード

プロセスグラフは、ノードと遷移で構成されます。各ノードは特定のタイプのノードです。ノードタイプにより、ランタイム時にノードに実行が到達したときの動作が決定されます。Business Process Manager は、使用する一連のノードタイプを提供します。または、カスタムコードを記述して、特定のノードの動作を実装することもできます。

6.3.1. ノードの役割

各ノードには 2 つの主な役割があります。まず、ノードはプレーンな Java コードを実行できます。このコードは、通常はノードの機能に関連するものです。2 番目の役割は、プロセスの実行を渡すことです。
ノードは、プロセスの実行を渡そうとするときに、次の選択肢に直面することがあります。ノードは最も適切なコースに従います。
  1. 実行を伝播できない。(ノードは 待機状態 として動作します。)
  2. ノードの 退出遷移 の 1 つを介して実行を伝播できる。(これは、最初にノードに到達したトークンが、API 呼び出し executionContext.leaveNode(String) により 退出遷移 の 1 つを介して渡されることを意味します。) ノードは、いくつかのカスタムプログラミングロジックを実行し、待機せずにプロセスの実行を自動的に続行するという意味で、自動的に動作します。
  3. 新しいトークンの作成を "決定" できる。新しい各トークンは、新しい実行パスを表します。これらの新しいトークンはそれぞれ、ノードの 退出遷移 を介して起動できます。この種の動作の良い例は フォークノード です。
  4. 実行パスを終了できる。これは、トークンが終了したことを意味します。
  5. プロセスインスタンスの ランタイム構造 全体を変更できる。ランタイム構造は、トークンのツリーを含むプロセスインスタンスであり、それぞれが実行パスを表します。ノードは、トークンを作成および終了し、各トークンをグラフのノードに配置し、遷移を介してトークンを起動できます。
Business Process Manager には、事前に実装された一連のノードタイプが含まれており、それぞれに特定の設定と動作があります。ただし、独自のノード動作を記述してプロセスで使用することもできます。

6.3.2. ノードタイプ: タスクノード

タスクノード は、手動で実行する必要がある 1 つ以上のタスクを表します。そのため、実行プロセスがノードに到達すると、ワークフロー参加者に属するリストにタスクインスタンスが作成されます。その後、ノードは 待機状態 に入ります。ユーザーがタスクを完了すると、実行がトリガーされて再開されます。

6.3.3. ノードタイプ: 状態

状態 は "必要最小限" の 待機状態 です。これは、どのタスクリストに対してもタスクインスタンスが作成されないという点で、タスクノードとは異なります。これは、プロセスが外部システムを待機している場合に役立ちます。その後、プロセスは待機状態になります。外部システムが応答メッセージを送信すると、通常 token.signal() が呼び出され、プロセス実行の再開をトリガーします。

6.3.4. ノードタイプ: 決定

決定をモデル化する方法は 2 つあります。どちらを使用するかは、ユーザーの裁量に任されています。オプションは次のとおりです。
  1. プロセスによって決定を行う。そのため、決定をプロセス定義で指定する。
  2. 外部エンティティーによって決定を行う。
プロセスによって決定を行う場合は、決定ノード を使用します。2 つの方法のいずれかで決定基準を指定します。最も簡単な方法は、条件要素を遷移に追加することです。(条件は、ブール値を返す EL 式または Beanshell スクリプトです。)
ランタイム時に、決定ノードは、条件が指定されている 退出遷移 をループします。決定ノードは、XML で指定された順序でこれらの遷移を最初に評価します。条件が true に解決される最初の遷移が取得されます。すべての遷移の条件が false に解決された場合、デフォルトの遷移 (XML の最初の遷移) が代わりに取得されます。デフォルトの遷移が見つからない場合、JbpmException が出力されます。
2 つ目の方法は、取得する遷移の名前を返す式を使用することです。expression 属性を使用して、決定の式を指定します。この式は、決定ノードのいずれかの 退出遷移 に解決する必要があります。
また、決定で handler 要素を使用することもできます。この要素を使用すると、決定ノードで指定できる DecisionHandler インターフェイスを実装できます。このシナリオでは、決定は Java クラスによって計算され、選択された 退出遷移DecisionHandler 実装に属する decide メソッドによって返されます。
外部エンティティーによって決定を行う場合は、状態 または 待機状態 ノードから退出する複数の遷移を常に使用してください。待機状態 が完了した後に実行を再開する外部トリガーで退出遷移を指定できます (Token.signal(String transitionName)TaskInstance.end(String transitionName) など)。

6.3.5. ノードタイプ: フォーク

フォークは、単一の実行パスを複数の同時実行パスに分割します。デフォルトでは、フォークは、そこから退出する遷移ごとに子トークンを作成します (これにより、フォークに到達するトークン間に親子関係が作成されます)。

6.3.6. ノードタイプ: ジョイン

デフォルトでは、ジョインは、その内部に到達したすべてのトークンが同じ親の子であると想定します。(この状況は、上記のようにフォークを使用した場合、およびフォークによって作成されたすべてのトークンが同じジョインに到達した場合に発生します。)
ジョインは、それに進入するすべてのトークンを終了します。次に、それらのトークンの親子関係を調べます。すべての兄弟トークンがジョインに到達すると、親トークンは 退出遷移 に渡されます。アクティブな兄弟トークンがまだある場合、ジョインは 待機状態 として動作します。

6.3.7. ノードタイプ: ノード

このノードは、カスタムコードの作成を回避するために使用します。このノードには、1 つのサブ要素アクションのみが必要です。サブ要素アクションは、実行がノードに到達したときに実行されます。actionhandler に記述されたカスタムコードは、任意の処理を実行できますが、実行を渡す役割もあることに注意してください。詳細は、「 ノードの役割 」 を参照してください。
このノードは、Java API を利用して企業のビジネスアナリスト向けの機能ロジックを実装する場合にも使用できます。そのようにすると、ノードがプロセスのグラフィカル表現に表示されたままになるため、便利です。(プロセスのグラフィカル表現には表示されないコードを追加するには、アクションを使用します。)

6.4. 遷移

遷移には、ソースノードと宛先ノードの両方があります。ソースノードは、from プロパティー、宛先ノードは to プロパティーによって表されます。
必要に応じて、遷移に名前を付けることができます。(Business Process Manager のほとんどの機能は、遷移に固有の名前が付けられていることに依存しています。) 複数の遷移が同じ名前の場合、最初の遷移が取得されます。(ノードで遷移名が重複する場合、Map getLeavingTransitionsMap() メソッドは、List getLeavingTransitions() よりも少ない要素を返します。)

6.5. アクション

アクションは、プロセス実行中にイベントで実行される Java コードの一部です。グラフは、ソフトウェア要件の伝達において重要な手段です。しかし、グラフは作成中のソフトウェアの 1 つのビュー (プロジェクション) にすぎません。多くの技術的な詳細は、そこには表示されません。アクションは、グラフィカルな表現以外の技術的な詳細を追加するためのメカニズムです。グラフを配置したら、アクションで装飾することができます。これにより、グラフの構造を変更せずに Java コードをグラフに関連付けることができます。主なイベントタイプは、ノード進入、ノード退出、遷移取得です。
重要
イベントに配置されるアクションとノードに配置されるアクションには違いがあります。イベントに配置されたアクションは、イベントが発生したときに実行されます。このアクションには、プロセスの制御の流れに影響を与える方法はありません。(これは、オブザーバーパターン に似ています。) 対照的に、ノードに配置されたアクションには、実行を渡す役割があります。
このセクションでは、イベントに対するアクションの例を説明します。この例は、特定の遷移でデータベースの更新を行う方法を示しています。(データベースの更新は技術的には重要ですが、ビジネスアナリストにとっては重要ではありません。)

図6.2 データベース更新アクション

データベース更新アクション
public class RemoveEmployeeUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // get the fired employee from the process variables.
    String firedEmployee =
      (String) ctx.getContextInstance().getVariable("fired employee");
    
    // by taking the same database connection as used for the jbpm
    // updates, we reuse the jbpm transaction for our database update.
    Connection connection =
    ctx.getProcessInstance().getJbpmSession().getSession().getConnection();
    Statement statement = connection.createStatement();
    statement.execute("DELETE FROM EMPLOYEE WHERE ...");
    statement.execute(); 
    statement.close();
  }
}
<process-definition name="yearly evaluation">
  <state name="fire employee">
    <transition to="collect badge">
      <action class="com.nomercy.hr.RemoveEmployeeUpdate" />
    </transition>
  </state>
  
  <state name="collect badge">
  
</process-definition>

6.5.1. アクションリファレンス

アクションには名前を付けることができます。これにより、アクションが指定されている他の場所からアクションを参照できます。名前付きアクションは、子要素 としてプロセス定義に追加することもできます。
この機能を使用して、アクション設定の重複を制限します。(これは、アクションの設定が複雑な場合や、ランタイムアクションをスケジュールまたは実行する必要がある場合に特に役立ちます。)

6.5.2. イベント

イベントは、プロセスの実行における特定の瞬間です。Business Process Manager のエンジンは、グラフ実行 中にイベントを "発生" させます。これは、ソフトウェアが次の状態を計算するとき (つまり、シグナルを処理するとき) に行われます。 イベントは常に、プロセス定義内の要素に関連しています。
ほとんどのプロセス要素は、さまざまなタイプのイベントを起動することができます。たとえば、ノードは node-enter イベントと node-leave イベントの両方を発生させることができます。(イベントはアクションの "フック" です。各イベントにはアクションのリストがあります。jBPM エンジンがイベントを発生させると、アクションのリストが実行されます。)

6.5.3. イベントの受け渡し

スーパーステート は、プロセス定義の要素に親子関係を作成します。(スーパーステートに含まれるノードと遷移は、そのスーパーステートを親として持ちます。最上位の要素はプロセス定義を親として持ち、プロセス定義自体はそれより上の親を持ちません。) イベントが発生すると、イベントは親階層に渡されます。これにより、プロセス内のすべての遷移イベントをキャプチャーし、一元化された場所を介してアクションをこれらのイベントに関連付けることができます。

6.5.4. スクリプト

スクリプト は、Beanshell スクリプトを実行するアクションです。(Beanshell の詳細は、http://www.beanshell.org/ を参照してください。) デフォルトでは、すべてのプロセス変数をスクリプト変数として使用できますが、スクリプト変数はプロセス変数に書き込まれません。使用できるスクリプト変数は次のとおりです。
  • executionContext
  • トークン (token)
  • node
  • task
  • taskInstance
<process-definition>
  <event type="node-enter">
    <script>
      System.out.println("this script is entering node "+node);
    </script>
  </event>
  ...
</process-definition>
スクリプトへの変数の読み込みと保存のデフォルトの動作をカスタマイズするには、variable 要素をスクリプトのサブ要素として使用します。その場合は、スクリプト式もサブ要素 expression としてスクリプトに配置します。
<process-definition>
  <event type="process-end">
    <script>
      <expression>
        a = b + c;
      </expression>
      <variable name='XXX' access='write' mapped-name='a' />
      <variable name='YYY' access='read' mapped-name='b' />
      <variable name='ZZZ' access='read' mapped-name='c' />
    </script>
  </event>
  ...
</process-definition>
スクリプトが起動する前に、プロセス変数 YYY および ZZZ が、それぞれスクリプト変数 b および c としてスクリプトで使用できるようになります。スクリプトが完了すると、スクリプト変数 a の値が、プロセス変数 XXX に格納されます。
変数の access 属性に read が含まれている場合、プロセス変数は、スクリプトが評価される前にスクリプト変数としてロードされます。access 属性に write が含まれている場合、スクリプト変数は評価後にプロセス変数として保存されます。mapped-name 属性を使用すると、スクリプト内でプロセス変数を別の名前で使用できます。これは、プロセス変数名にスペースやその他の無効な文字が含まれている場合に使用します。

6.5.5. カスタムイベント

GraphElement.fireEvent(String eventType, ExecutionContext executionContext); メソッドを呼び出すことで、プロセスの実行中に自由にカスタムイベントを実行できます。イベントタイプの名前は自由に選択してください。

6.6. スーパーステート

スーパーステートはノードのグループです。これらは再帰的にネストでき、プロセス定義に階層を追加するために使用されます。たとえば、この機能は、プロセスに属するノードをフェーズごとにグループ化するのに役立ちます。
アクションは、スーパーステートイベントに関連付けることができます。ネストされたノードのトークンによって発生したイベントは、プロセス定義までスーパーステート階層をバブルアップします。したがって、トークンは階層内のすべてのノードに同時に存在するものとして機能します。これは、たとえば、プロセス実行が起動フェーズにあるかどうかを確認する場合に便利です。

6.6.1. スーパーステート遷移

スーパーステートから退出する遷移は、そのスーパーステート内の任意のノードに配置されたトークンによって取得できます。この機能は、随時取得できる キャンセル 遷移のモデル化などに使用できます。
遷移はスーパーステートにも到達することがあります。その場合、トークンはドキュメント順の最初のノードにリダイレクトされます。さらに、スーパーステートの外側にあるノードは、スーパーステートの内側にあるノードに直接遷移することができ、逆もまた同様です。最後に、他のノードと同様に、スーパーステートも自己遷移できます。

6.6.2. スーパーステートイベント

スーパーステートには、superstate-enter および superstate-leave という固有のイベントが 2 つあります。これらのイベントは、ノードがどの遷移に進入したか、またはどの遷移から退出したかに関係なく、発生します。スーパーステート内でトークンが遷移を取得する限り、これらのイベントは発生しません。
注記
ステートとスーパーステートには別々のイベントタイプがあります。このソフトウェアは、実際のスーパーステートイベントとスーパーステート内から渡されたノードイベントを簡単に区別できるように設計されています。

6.6.3. 階層名

ノード名は、スコープ 内で一意である必要があります。ノードのスコープは、その ノードコレクション です。プロセス定義とスーパーステートは、どちらもノードコレクションです。スーパーステートのノードを参照するには、スラッシュ (/) 区切りの相対名で指定します。ノード名はスラッシュで区切られます。.. を使用して、上位レベルを参照します。次の例は、スーパーステートのノードを参照する方法を示しています。
<process-definition>
  <state name="preparation">
    <transition to="phase one/invite murphy"/>
  </state>
  <super-state name="phase one">
    <state name="invite murphy"/>
  </super-state>
</process-definition>
次の例は、スーパーステート階層を上に移動する方法を示しています。
<process-definition>
  <super-state name="phase one">
    <state name="preparation">
      <transition to="../phase two/invite murphy"/>
    </state>
  </super-state>
  <super-state name="phase two">
    <state name="invite murphy"/>
  </super-state>
</process-definition>

6.7. 例外処理

Business Process Manager の例外処理メカニズムは、Java 例外に対してのみ機能します。グラフ実行自体が問題を引き起こすことはありません。例外が発生する可能性があるのは、委譲クラス が実行されたときだけです。
exception-handler のリストは、process-definitionnodetransition で指定できます。これらの各例外ハンドラーには、アクションのリストがあります。委譲クラスで例外が発生すると、プロセス要素の親階層で適切な exception-handler が検索され、そのアクションが実行されます。
重要
Business Process Manager の例外処理は、Java の例外処理といくつかの点で異なります。Java では、キャッチされた例外が 制御フロー に影響を与える可能性があります。jBPM の場合、例外処理メカニズムが制御フローを変更することはできません。例外は、キャッチされるか、キャッチされないかのいずれかです。キャッチされていない例外は、token.signal() メソッドを呼び出したクライアントに出力されます。キャッチされた例外がある場合は、何も発生しなかったかのようにグラフ実行が続行されます。
注記
例外処理 アクション のグラフ内にある任意のノードにトークンを配置するには、Token.setNode(Node node) を使用します。

6.8. プロセス構成

Business Process Manager は、process-state によって プロセス構成 をサポートしています。この状態は、別のプロセス定義に関連付けられている状態です。グラフ実行が process-state に到達すると、サブプロセスの新しいインスタンスが作成されます。このサブプロセスは、プロセス状態に到達した実行パスに関連付けられます。スーパープロセスの実行パスは、サブプロセスが終了するまで待機し、その後プロセス状態から退出し、スーパープロセスでグラフ実行を継続します。
<process-definition name="hire">
  <start-state>
    <transition to="initial interview" />
  </start-state>
  <process-state name="initial interview">
    <sub-process name="interview" />
    <variable name="a" access="read,write" mapped-name="aa" />
    <variable name="b" access="read" mapped-name="bb" />
    <transition to="..." />
  </process-state>
  ...
</process-definition>
上記の例では、hire プロセスには、interview プロセスを生成する process-state が含まれています。first interview に実行が到達すると、interview プロセスの新しい実行 (つまり、プロセスインスタンス) が作成されます。バージョンが明示的に指定されていない場合、サブプロセスの最新バージョンが使用されます。Business Process Manager で特定のバージョンをインスタンス化するには、オプションの version 属性を指定します。サブプロセスが実際に作成されるまで、指定したバージョンまたは最新バージョンのバインドを延期するには、オプションの binding 属性を late に設定します。
次に、hire プロセス変数 ainterview プロセス変数 aa にコピーします。同様に、hire 変数 b を インタビュー変数 bb にコピーします。面接プロセスが完了すると、変数 aa のみが a 変数に再びコピーされます。
一般に、サブプロセスが開始されると、読み取りアクセス権を持つすべての変数がスーパープロセスから読み取られ、新しく作成されたサブプロセスに取り込まれます。これは、開始状態から退出するシグナルが与えられる前に行われます。サブプロセスインスタンスが完了すると、書き込みアクセス権を持つすべての変数がサブプロセスからスーパープロセスにコピーされます。変数の mapped-name 属性を使用して、サブプロセスで使用する変数名を指定します。

6.9. カスタムノードの動作

任意のビジネスロジックを実行できるだけでなく、グラフ実行を渡す役割を持つ ActionHandler の特別な実装を使用して、カスタムノードを作成します。以下の例では、ERP システムから値を読み取り、(プロセス変数から) 金額を追加し、結果を ERP システムに保存します。金額の大きさに基づいて、 small amounts 遷移または large amounts 遷移を使用して終了します。

図6.3 ERP 更新用のプロセススニペットの例

ERP 更新用のプロセススニペットの例
public class AmountUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // business logic
    Float erpAmount = ...get amount from erp-system...;
    Float processAmount = (Float) ctx.getContextInstance().getVariable("amount");
    float result = erpAmount.floatValue() + processAmount.floatValue();
    ...update erp-system with the result...;
    
    // graph execution propagation
    if (result > 5000) {
      ctx.leaveNode(ctx, "big amounts");
    } else {
      ctx.leaveNode(ctx, "small amounts");
    }
  }
}
注記
カスタムノード実装でトークンを作成して結合することもできます。これを行う方法については、jBPM ソースコードのフォークノードおよびジョインノードの実装を参照してください。

6.10. グラフ実行

Business Process Manager のグラフ実行モデルは、プロセス定義の解釈と "チェーンオブコマンド" パターンに基づいています。
プロセス定義データはデータベースに格納され、プロセス実行中に使用されます。
注記
ランタイム時に定義情報をロードしないように、Hibernate の 2 次キャッシュが使用されることに注意してください。プロセス定義は変更されないため、Hibernate はそれらをメモリーにキャッシュできます。
"チェーンオブコマンドパターン" は、グラフ内の各ノードにプロセス実行の受け渡しを担当させます。ノードがプロセス実行を渡さない場合、ノードは 待機状態 のように動作します。
プロセスインスタンスで実行を開始すると、実行は 待機状態 に進入するまで続行されます。
トークンは実行のパスを表します。トークンには、プロセスグラフ内のノードへのポインターがあります。トークンは、待機状態 の間、データベースで永続化することができます。
このアルゴリズムは、トークンの実行を計算するために使用されます。シグナルがトークンに送信されると実行が開始され、コマンドオブチェーンパターンを介して遷移とノードに渡されます。関連するメソッドは次のとおりです。

図6.4 グラフ実行関連のメソッド

グラフ実行関連のメソッド
トークンがノードにあるとき、そのトークンにシグナルを送信できます。シグナルは実行開始の命令として扱われるため、トークンの現在のノードからの 退出遷移 を指定する必要があります。最初の遷移がデフォルトです。トークンにシグナルが送信されると、遷移はトークンの現在のノードを取得し、Node.leave(ExecutionContext,Transition) メソッドを呼び出します。(ExecutionContext は、その中の主要なオブジェクトがトークンであるため、トークンと考えるのが最適です。) Node.leave(ExecutionContext,Transition) メソッドは node-leave イベントを発生させ、Transition.take(ExecutionContext) を呼び出します。このメソッドは遷移イベントを実行し、遷移の宛先ノードで Node.enter(ExecutionContext) を呼び出します。さらに、このメソッドが node-enter イベントを発生させ、Node.execute(ExecutionContext) を呼び出します。
すべてのタイプのノードには独自の動作があります。これらは execute メソッドを介して実装されます。各ノードには、Node.leave(ExecutionContext,Transition) を再度呼び出して、グラフ実行を受け渡す役割があります。つまり、以下のようになります。
  • Token.signal(Transition)
  • Node.leave(ExecutionContext,Transition)
  • Transition.take(ExecutionContext)
  • Node.enter(ExecutionContext)
  • Node.execute(ExecutionContext)
注記
次の状態 (アクションの呼び出しを含む) は、クライアントのスレッドを介して計算されます。よくある誤解として、すべての計算をこの方法で行う必要があるというものがあります。むしろ、非同期呼び出し の場合と同様に、非同期メッセージング を (Java Message Service 経由で) 使用できます。メッセージがプロセスインスタンスの更新と同じトランザクションで送信されると、すべての同期の問題が正しく処理されます。一部のワークフローシステムは、グラフ内のすべてのノード間で非同期メッセージングを使用しますが、高スループット環境では、このアルゴリズムは、ビジネスプロセスのパフォーマンスを最大化を求めるユーザーに、はるかに多くの制御と柔軟性を提供します。

6.11. トランザクションの境界

「 グラフ実行 」 で説明したように、Business Process Manager はクライアントのスレッドでプロセスを実行し、その性質上、同期的です。実際には、これはつまり、プロセスが新しい 待機状態 に進入したときにのみ、token.signal() または taskInstance.end() が返されることを意味します。
注記
このセクションで説明されている jPDL 機能の詳細は、10章 非同期継続 を参照してください。
プロセスの実行をサーバー側のトランザクションに簡単にバインドできるため、ほとんどの場合、これが最も単純なアプローチです。プロセスは、1 つのトランザクションの空間内で 1 つの状態から次の状態に移動します。
処理中の計算には時間がかかる場合があるため、この動作は望ましくない場合があります。この問題に対処するために、Business Process Mangerには非同期メッセージングシステムが組み込まれています。このシステムは、その名前が示すように非同期でプロセスを続行できます。(もちろん、Java エンタープライズ環境では、組み込みのメッセージングシステムの代わりに Java Message Service ブローカーを使用するように jBPM を設定できます。)
jPDL は、すべてのノードで async="true" 属性をサポートします。非同期ノードは、クライアントのスレッドでは実行されません。代わりに、非同期メッセージングシステムを介してメッセージが送信され、スレッドがクライアントに返されます (つまり、token.signal() または taskInstance.end() が返されます。)
Business Process Manger のクライアントコードは、トランザクションをコミットできます。プロセスの更新を含むトランザクションと同じトランザクションでメッセージを送信します。このようなトランザクションの全体的な結果として、トークンは次のノード (まだ実行されていないノード) に移動され、org.jbpm.command.ExecuteNodeCommand メッセージが非同期メッセージングシステムから jBPM Command Executor に送信されます。これにより、キューからコマンドが読み取られ、実行されます。org.jbpm.command.ExecuteNodeCommand の場合、ノード実行時に処理が継続されます。(各コマンドは別々のトランザクションで実行されます。)
重要
非同期プロセスを続行できるように、jBPM Command Executor が実行されていることを確認してください。これを行うには、Web アプリケーションの CommandExecutionServlet を設定します。
注記
プロセスモデル管理者は、非同期メッセージングに過度に配慮する必要はありません。覚えておくべき主なポイントは、トランザクションの境界です。デフォルトでは、Business Process Manger はクライアントトランザクションで動作し、プロセスが 待機状態 に進入するまで計算全体を実行します。(async="true" を使用して、プロセス内のトランザクションを区別します。)
以下に例を示します。
<start-state>
  <transition to="one" />
</start-state>
<node async="true" name="one">
  <action class="com...MyAutomaticAction" />
  <transition to="two" />
</node>
<node async="true" name="two">
  <action class="com...MyAutomaticAction" />
  <transition to="three" />
</node>
<node async="true" name="three">
  <action class="com...MyAutomaticAction" />
  <transition to="end" />
</node>
<end-state name="end" />
...
プロセス実行の開始と再開の両方に必要なクライアントコードは、通常の同期プロセスに必要なコードとまったく同じです。
//start a transaction
JbpmContext jbpmContext = jbpmConfiguration.createContext();
try {
  ProcessInstance processInstance =
    jbpmContext.newProcessInstance("my async process");
  processInstance.signal();
  jbpmContext.save(processInstance);
} finally {
  jbpmContext.close();
}
この最初のトランザクションが発生すると、プロセス実行の root tokennode one を参照し、ExecuteNodeCommand メッセージがコマンドエグゼキューターに送信されます。
後続のトランザクションで、コマンドエグゼキューターはキューからメッセージを読み取り、node one を実行します。アクションは、実行を渡すか、待機状態 に進入するかを決定できます。実行を渡すことを選択した場合、実行が node two に到達したときにトランザクションは終了します。

第7章 コンテキスト

この章では プロセス変数 について説明します。プロセス変数は、プロセスインスタンス関連の情報を保持するキーと値のペアです。
注記
コンテキスト をデータベースに保存できるようにするために、いくつかの小さな制限が適用されます。

7.1. プロセス変数へのアクセス

org.jbpm.context.exe.ContextInstance は、プロセス変数の中心的なインターフェイスとして機能します。次の方法で、プロセスインスタンスから ContextInstance を取得します。
ProcessInstance processInstance = ...;
ContextInstance contextInstance = 
	(ContextInstance) processInstance.getInstance(ContextInstance.class);
基本的な操作は次のとおりです。
void ContextInstance.setVariable(String variableName, Object value);
void ContextInstance.setVariable(
	String variableName, Object value, Token token);

Object ContextInstance.getVariable(String variableName);
Object ContextInstance.getVariable(String variableName, Token token);
変数名は java.lang.String です。デフォルトでは、Business Process Manager は次の値の型をサポートしています。(Hibernate で永続化できる他の型もサポートしています。)
java.lang.Stringjava.lang.Boolean
java.lang.Characterjava.lang.Float
java.lang.Doublejava.lang.Long
java.lang.Bytejava.lang.Integer
java.util.Datebyte[]
java.io.Serializable 
注記
型指定のない null 値 も永続的に格納できます。
警告
プロセス変数に他の型が格納されている場合は、プロセスインスタンスを保存しないでください。例外エラーが発生します。

7.2. 変数の寿命

プロセスアーカイブで変数を宣言する必要はありません。ランタイム時に、任意の Java オブジェクトを変数に格納します。変数が存在しない場合は、単純な java.util.Map と同じ方法で作成されます。また、変数は削除することもできます。
ContextInstance.deleteVariable(String variableName);
ContextInstance.deleteVariable(String variableName, Token token);
型は自動的に変更できます。これは、異なる型の値の変数を型で上書きできることを意味します。単純な列の更新よりも多くのデータベースとの通信が発生するため、型の変更の数は、常に制限することが重要です。

7.3. 可変永続性

変数はプロセスインスタンスの一部です。プロセスインスタンスをデータベースに保存すると、データベースがプロセスインスタンスと同期されます。(これにより、変数が作成、更新、および削除されます。) 詳細は、4章 永続性 を参照してください。

7.4. 可変スコープ

各実行パス (トークン とも呼ばれます) には、独自のプロセス変数のセットがあります。変数は常に実行パスで要求されます。プロセスインスタンスには、これらのパスのツリーがあります。変数が要求されたときにパスが指定されていない場合、ルートトークン がデフォルトで使用されます。
変数の検索は再帰的に行われます。検索は、特定の実行パスの親で実行されます。(これは、プログラミング言語で変数のスコープを設定する方法に似ています。)
存在しない変数が実行パスに設定されると、その変数は ルートトークン に作成されます。(そのため、各変数にはデフォルトでプロセススコープがあります。) 変数トークンを "ローカル" にするには、次の例に従って明示的に作成します。
ContextInstance.createVariable(String name, Object value, Token token);

7.4.1. 変数のオーバーロード

変数のオーバーロード とは、各実行パスが同じ名前の変数の独自のコピーを持つことができることを意味します。これらのコピーはすべて別々に扱われ、異なる型を取ることができます。変数のオーバーロードは、同じ遷移で複数の同時実行パスを起動する場合に有用なことがあります。これらのパスを区別するのは、それぞれの変数のセットだけであるからです。

7.4.2. 変数のオーバーライド

変数のオーバーライド とは、ネストされた実行パス 内の変数が、よりグローバルな実行パス内の変数をオーバーライドすることを意味します。一般に、"ネストされた実行パス" は同時実行に関連しています。フォークとジョインの間の実行パスは、フォークに到達した (ネストされた) 実行パスの子です。たとえば、プロセスインスタンススコープ内の contact という名前の変数を、ネストされた実行パス shipping および billing 内のこの変数でオーバーライドできます。

7.4.3. タスクインスタンス変数のスコープ

タスクインスタンス変数については、「 タスクインスタンス変数 」 を参照してください。

7.5. 一時変数

プロセスインスタンスがデータベースに永続化されると、通常の変数も永続化されます。しかし、変数をデータベースに格納せずに委譲クラスで使用する必要がある場合もあります。これは、一時変数 を使用して実現できます。
注記
一時変数の寿命は、ProcessInstance Java オブジェクトの寿命と同じです。
注記
その性質上、一時変数は実行パスとは関係ありません。そのため、プロセスインスタンスオブジェクトには、それらのマップが 1 つしかありません。
一時変数には、コンテキストインスタンス内の独自のメソッドセットを介してアクセスできます。processdefinition.xml ファイルで宣言する必要はありません。
Object ContextInstance.getTransientVariable(String name);
void ContextInstance.setTransientVariable(String name, Object value);
この章では、プロセス変数について詳しく説明しました。これで、このトピックの理解に自信を持てるはずです。

第8章 タスク管理

jBPM の中心的な役割は、プロセスの実行を 永続化 することです。この機能は、ユーザーのタスクとタスクリストを管理する場合に非常に便利です。jBPM を使用すると、プロセス全体を記述するソフトウェアを指定できます。このようなソフトウェアは、ヒューマンタスクの 待機状態 を持つことができます。

8.1. タスク

タスク はプロセス定義の一部です。タスクは、プロセスの実行中にタスクインスタンスを作成して割り当てる方法を定義します。
タスクは、task-node および process-definition で定義します。最も一般的な方法は、task-node で 1 つ以上の タスク を定義することです。その場合、task-node はユーザーが引き受けるタスクを表します。プロセスの実行は、アクターがタスクを完了するまで待機する必要があります。アクターがタスクを完了すると、プロセスの実行が続行されます。task-node に追加のタスクが指定されている場合、デフォルトの動作では、すべてのタスクが終了するまで待機します。
タスクは、process-definition で指定することもできます。この方法で指定したタスクは、名前を検索して見つけることができます。task-node 内からそれらを参照したり、アクション内から使用したりすることもできます。実際、名前が付けられたすべてのタスク (または task-node) は、process-definition で見つけることができます。
各タスク名は、必ず一意のものにします。また、タスクには 優先度 を設定します。これは、このタスク用に作成された各タスクインスタンスの初期優先度として使用されます。(この初期優先度は、後でタスクインスタンスによって変更できます。)

8.2. タスクインスタンス

タスクインスタンスは、actorId (java.lang.String) に割り当てることができます。すべてのタスクインスタンスは 1 つのテーブル (JBPM_TASKINSTANCE) に格納されます。 特定のユーザーのタスクリストを取得するには、このテーブルを照会し、特定の actorId のすべてのタスクインスタンスを取得します。
jBPM のタスクリストメカニズムを使用すると、jBPM のタスクを他のタスクと組み合わせることができます。それらの他のタスクがプロセスの実行に関連していない場合でも、これは可能です。このようにして、一元的なリポジトリーで、jBPM-process-task を他のアプリケーションのタスクと簡単に組み合わせることができます。

8.2.1. タスクインスタンスのライフサイクル

タスクインスタンスのライフサイクルは単純です。インスタンスを作成すると、インスタンスを起動できます。起動後は、インスタンスを終了できます。終了すると、完了としてマークされます。
注記
柔軟性を確保するために、割り当て はライフサイクルには含まれていません。
  1. タスクインスタンスは通常、プロセスの実行が task-node に進入したときに (TaskMgmtInstance.createTaskInstance(...) メソッドを介して) 作成されます。
  2. ユーザーインターフェイスコンポーネントが、データベースにタスクリストを照会します。これは、TaskMgmtSession.findTaskInstancesByActorId(...) メソッドを使用して行われます。
  3. ユーザーからの入力を収集した後、UI コンポーネントが TaskInstance.assign(String)TaskInstance.start()、または TaskInstance.end(...) を呼び出します。
タスクインスタンスは、次の 3 つの日付プロパティーによって状態を維持します。
  1. create
  2. start
  3. end
TaskInstance にあるそれぞれの "ゲッター" を介してこれらのプロパティーにアクセスします。
完了したタスクインスタンスは、終了日でマークされます。これは、後続のクエリーがタスクリストを検索するときに、完了したタスクインスタンスを取得しないようにするためです。ただし、完了したタスクは JBPM_TASKINSTANCE テーブルに残ります。

8.2.2. タスクインスタンスとグラフ実行

タスクインスタンス は、アクターのタスクリスト内にある項目です。シグナリングタスクインスタンスは、完了時にトークンにシグナルを送信してプロセスの実行を続行するタスクインスタンスです。ブロッキングタスクインスタンスは、タスクインスタンスが完了する前に、関連するトークン (実行パス) が task-node から退出することを禁止するタスクインスタンスです。デフォルトでは、タスクインスタンスはシグナリングおよび非ブロッキングになるように設定されています。
複数のタスクインスタンスが task-node に関連付けられている場合、プロセス開発者は、タスクインスタンスの完了がプロセスの継続に影響を与える方法を指定できます。以下の値のいずれかを task-nodesignal-property に与えます。
last
これはデフォルトです。最後のタスクインスタンスが完了すると、実行が続行されます。このノードへの進入時にタスクが作成されていない場合、実行が続行されます。
last-wait
最後のタスクインスタンスが完了すると、実行が続行されます。このノードへの進入時にタスクが作成されていない場合、タスクが作成されるまで、タスクノードで実行が待機されます。
first
最初のタスクインスタンスが完了すると、実行が続行されます。このノードへの進入時にタスクが作成されていない場合、実行が続行されます。
first-wait
最初のタスクインスタンスが完了すると、実行が続行されます。このノードへの進入時にタスクが作成されていない場合、タスクが作成されるまで、タスクノードで実行が待機されます。
unsynchronized
この場合、タスクが作成されているか未完了であるかに関係なく、実行は常に続行されます。
never
この場合、タスクが作成されているか未完了であるかに関係なく、実行は続行されません。
タスクインスタンスの作成は、ランタイム時の計算に基づいて行うことができます。このような場合、ActionHandlertask-nodenode-enter イベントに追加し、create-tasks="false" を設定します。以下に例を示します。
public class CreateTasks implements ActionHandler {
  public void execute(ExecutionContext executionContext) throws Exception {
    Token token = executionContext.getToken();
    TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
      
    TaskNode taskNode = (TaskNode) executionContext.getNode();
    Task changeNappy = taskNode.getTask("change nappy");

    // now, 2 task instances are created for the same task.
    tmi.createTaskInstance(changeNappy, token);
    tmi.createTaskInstance(changeNappy, token);
  }
}
ここで、作成するタスクは task-node で指定します。また、process-definition で指定して、TaskMgmtDefinition から取得することもできます。(TaskMgmtDefinition は、タスク管理情報を追加してプロセス定義を拡張します。)
TaskInstance.end() メソッドは、タスクインスタンスを完了としてマークするために使用します。必要に応じて、end メソッドで遷移を指定できます。このタスクインスタンスの完了によって実行の継続がトリガーされた場合、指定された遷移を介して task-node から退出します。

8.3. 割り当て

プロセス定義にはタスクノードが含まれます。task-node には、0 個以上のタスクが含まれます。タスクは、プロセス定義の一部を静的に記述したものです。ランタイム時にタスクを実行すると、タスクインスタンスが作成されます。タスクインスタンスは、個人のタスクリストの 1 つのエントリーに対応します。
jBPM を使用すると、タスク割り当てのプッシュモデル (個人タスクリスト) とプルモデル (グループタスクリスト) を組み合わせて適用できます。プロセスは、タスクの責任者を決定し、責任者のタスクリストにプッシュします。タスクをアクターのプールに割り当てることもできます。この場合、各アクターはタスクをプルして、個人のタスクリストに入れます。

8.3.1. 割り当てのインターフェイス

AssignmentHandler インターフェイスを介してタスクインスタンスを割り当てます。
public interface AssignmentHandler extends Serializable {
  void assign( Assignable assignable, ExecutionContext executionContext );
}
タスクインスタンスの作成時に、割り当てハンドラーの実装が呼び出されます。その時点で、タスクインスタンスは 1 つ以上のアクターに割り当てられます。AssignmentHandler の実装は、割り当て可能なメソッド (setActorId または setPooledActors) を呼び出し、タスクを割り当てます。割り当て可能な項目は、TaskInstance または SwimlaneInstance (プロセスロール) です。
public interface Assignable {
  public void setActorId(String actorId);
  public void setPooledActors(String[] pooledActors);
}
TaskInstanceSwimlaneInstance の両方を、特定のユーザーまたはアクターのプールに割り当てることができます。TaskInstance をユーザーに割り当てるには、Assignable.setActorId(String actorId) を呼び出します。TaskInstance を候補アクターのプールに割り当てるには、Assignable.setPooledActors(String[] actorIds) を呼び出します。
プロセス定義の各タスクをハンドラー実装に関連付けることで、ランタイム時に割り当てを実行できます。
プロセス内の複数のタスクを同じ個人またはアクターのグループに割り当てる必要がある場合は、スイムレーンの使用を検討してください (「 スイムレーン 」 を参照)。
再利用可能な AssignmentHandler を作成するには、processdefinition.xml ファイルを使用してそれぞれを設定します。(割り当てハンドラーに設定を追加する方法の詳細は、「委譲」 を参照してください。)

8.3.2. 割り当てのデータモデル

タスクインスタンスとスイムレーンインスタンスのアクターへの割り当てを管理するためのデータモデルを以下に示します。各 TaskInstance には、actorId とプールされたアクターのセットがあります。

図8.1 割り当てモデルのクラス図

割り当てモデルのクラス図
actorId はタスクの責任者です。プールされたアクターのセットは、候補者のコレクションを表します。候補者はタスクを引き受けると、責任者になります。actorIdpooledActors はどちらもオプションであり、組み合わせることもできます。

8.3.3. 個人タスクリスト

個人タスクリストは、特定の個人に割り当てられたすべてのタスクインスタンスを表します。これは、TaskInstanceactorId プロパティーの存在によって示されます。次のいずれかの方法で、TaskInstance を個人のタスクリストに追加します。
  • タスク要素の actor-id 属性に式を指定する
  • コード内の任意の場所から TaskInstance.setActorId(String) メソッドを使用する
  • AssignmentHandlerassignable.setActorId(String) を使用する
特定のユーザーの個人タスクリストを取得するには、TaskMgmtSession.findTaskInstances(String actorId) を使用します。

8.3.4. グループタスクリスト

プールされたアクターは、タスクを与えられる候補者のグループです。1 人の候補者がタスクを引き受ける必要があります。多くのユーザーが同じタスクの作業を開始すると、競合が発生する可能性があるため、ユーザーはすぐにタスクの作業を開始できません。これを防ぐために、ユーザーはグループタスクリストからのみタスクインスタンスを取得して個人タスクリストに移動できます。ユーザーが作業を開始できるのは、ユーザーの個人タスクリストにタスクが追加された場合のみです。
taskInstance をユーザーのグループタスクリストに入れるには、ユーザーの actorId またはユーザーの groupId の 1 つを pooledActorIds に追加します。プールされたアクターを指定するには、次のいずれかの方法を使用します。
  • プロセス内のタスク要素の属性 pooled-actor-ids に式を指定する
  • コード内のいずれかの場所で TaskInstance.setPooledActorIds(String[]) を使用する
  • AssignmentHandler で assignable.setPooledActorIds(String[]) を使用する
特定のユーザーのグループタスクリストを取得するには、ユーザーの actorId と、そのユーザーが属するすべてのグループの actorId を含むコレクションを作成します。TaskMgmtSession.findPooledTaskInstances(String actorId) または TaskMgmtSession.findPooledTaskInstances(List actorIds) を使用して、個人タスクリストになく (actorId==null)、プールされた actorId に一致するタスクインスタンスを検索します。
注記
このソフトウェアは、アイデンティティーコンポーネントを jBPM のタスク割り当てから分離するために、このように設計されました。jBPM は、文字列のみを actorId として格納します。ユーザーとグループの関係やその他のアイデンティティー情報は認識しません。
actorId は、プールされたアクターを常にオーバーライドします。したがって、actorIdpooledActorId のリストを持つ taskInstance は、アクターの個人タスクリストにのみ表示されます。タスクインスタンスをグループに戻すには、taskInstanceactorId プロパティを null に設定して、pooledActorId を保持します。

8.4. タスクインスタンス変数

タスクインスタンスは、独自の変数セットを持つことができ、プロセス変数を "認識" することもできます。タスクインスタンスは通常、実行パス (トークン) に作成されます。これにより、トークンとタスクインスタンスの間に親子関係が作成されます。これは、トークンの間の親子関係に似ています。通常のスコープ規則が適用されることに注意してください。
コントローラー を使用して、タスクインスタンススコープとプロセスのスコープ付き変数の間で変数の作成、入力、および送信を行います。
これは、タスクインスタンスが自身の変数に加えて、関連するトークンのすべての変数を '認識' できることを意味します。
コントローラーを使用すると、タスクインスタンススコープとプロセスのスコープ付き変数の間で変数の作成、入力、および送信を行うことができます。

8.5. タスクコントローラー

タスクインスタンスを作成したら、タスクコントローラーを使用してタスクインスタンス変数を設定できます。タスクインスタンスが終了すると、タスクコントローラーを使用して、タスクインスタンスに属するデータをプロセス変数に送信できます。
注記
タスクコントローラーの使用はオプションです。タスクインスタンスは、そのトークンに関連するプロセス変数を "認識" することもできます。タスクコントローラーは、次のタスクの実行に使用します。
  • タスクインスタンス変数のコピーを作成して、プロセスが完了するまでタスクインスタンス変数への中間更新がプロセス変数に影響を与えないようにします。この時点で、コピーがプロセス変数に送信されます。
  • タスクインスタンス変数は、プロセス変数と 1 対 1 の関係を持ちません。たとえば、プロセスに sales in Januarysales in January sales、および sales in March という名前の変数がある場合、タスクインスタンスのフォームで、それらの 3 か月の平均売上を表示しなければならないこともあります。
タスクは、ユーザーからの入力を収集します。ただし、ユーザーにタスクを提示するために使用できるユーザーインターフェイスは多数あります。たとえば、Web アプリケーション、Swing アプリケーション、インスタントメッセンジャー、メールフォームなどがあります。そのため、タスクコントローラーは、プロセス変数 (= プロセスコンテキスト) とユーザーインターフェイスアプリケーションの間の橋渡しを行います。タスクコントローラーは、プロセス変数のビューをユーザーインターフェイスアプリケーションに提供します。
タスクインスタンスが作成されると、タスクコントローラーは、プロセス変数があればそれをタスク変数に変換します。タスク変数は、ユーザーインターフェイスフォームの入力として機能します。ユーザー入力自体はタスク変数に格納されます。ユーザーがタスクを終了すると、タスクコントローラーはタスクインスタンスデータに基づいてプロセス変数を更新します。

図8.2 タスクコントローラー

タスクコントローラー
単純なシナリオでは、プロセス変数とフォームパラメーターの間に 1 対 1 のマッピングがあります。タスクコントローラーをタスク要素で指定します。このような場合、デフォルトの JBPM タスクコントローラーを使用できます。このコントローラーは、プロセス変数をタスク変数にどのようにコピーするかを表す variable 要素のリストを取得します。
次の例は、プロセス変数に基づいて、タスクインスタンス変数の個別のコピーを作成する方法を示しています。
<task name="clean ceiling">
  <controller>
    <variable name="a" access="read" mapped-name="x" />
    <variable name="b" access="read,write,required" mapped-name="y" />
    <variable name="c" access="read,write" />
  </controller>
</task>
name 属性は、プロセス変数の名前を参照します。mapped-name はオプションで、タスクインスタンス変数の名前を参照します。mapped-name 属性を省略すると、mapped-name のデフォルトは名前に設定されます。mapped-name は、Web アプリケーションのタスクインスタンスフォームにおけるフィールドのラベルとしても使用されます。
access 属性を使用して、タスクインスタンスの作成時にコピーした変数を、タスクインスタンスの終了時にプロセス変数に書き戻すかどうかを指定します。(この情報は、適切なフォームコントロールを生成するためにユーザーインターフェイスで使用できます。) access 属性はオプションで、デフォルトのアクセスは read,write です。
task-node は多くのタスクを持つことができますが、start-state は 1 つのタスクしか持ちません。
プロセス変数とフォームパラメーター間の単純な 1 対 1 のマッピングでは制約が大きすぎる場合は、カスタムの TaskControllerHandler 実装を作成します。そのためのインターフェイスを以下に示します。
public interface TaskControllerHandler extends Serializable {
  void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
  void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}
次のコードサンプルは、その設定方法を示しています。
<task name="clean ceiling">
  <controller class="com.yourcom.CleanCeilingTaskControllerHandler">
    -- here goes your task controller handler configuration --
  </controller>
</task>

8.6. スイムレーン

スイムレーン はプロセスロールです。このメカニズムを使用して、プロセス内の複数のタスクを同じアクターが引き受けるよう指定します。特定のスイムレーンの最初のタスクインスタンスが作成されると、同じスイムレーン内の後続のすべてのタスクでアクターが "記憶" されます。そのため、スイムレーンには 1 つの 割り当て があります。総裁は、「 割り当て 」 を参照してください。
特定のスイムレーンの最初のタスクが作成されると、AssignmentHandler が呼び出されます。AssignmentHandler に渡される Assignable (割り当て可能な) 項目は、SwimlaneInstance です。特定のスイムレーンのタスクインスタンスで引き受けられた割り当ては、すべてスイムレーンインスタンスに伝播されます。タスクを引き受けるユーザーはその特定のプロセスに関する知識を持っているため、これがデフォルトの動作となっています。したがって、そのスイムレーンの後続のタスクインスタンスは、自動的にそのユーザーに割り当てられます。

8.7. 開始タスクのスイムレーン

スイムレーンは、開始タスクに関連付けることができます。これは、プロセスの開始者をキャプチャーするために行います。
タスクは start-state で指定できます。これにより、タスクがスイムレーンに関連付けられます。新しいタスクインスタンスが作成されると、現在の認証済みアクターが Authentication.getAuthenticatedActorId() メソッドによりキャプチャーされます。アクターは開始タスクのスイムレーンに格納されます。
<process-definition>
  <swimlane name='initiator' />
  <start-state>
    <task swimlane='initiator' />
    <transition to='...' />
  </start-state>
  ...
</process-definition>
通常のメソッドを使用して、開始タスクに変数を追加します。これにより、タスクに関連付けられたフォームを定義します。詳細は、「 タスクコントローラー 」 を参照してください。

8.8. タスクイベント

タスクにはアクションを関連付けることができます。4 つの標準イベントタイプがあります。
  1. task-create: タスクインスタンスが作成されたときに発生します。
  2. task-assign: タスクインスタンスが割り当てられるときに発生します。このイベントで実行されるアクションでは、executionContext.getTaskInstance().getPreviousActorId(); メソッドを使用して前のアクターにアクセスできます。
  3. task-start: TaskInstance.start() メソッドが呼び出されたときに発生します。このオプション機能は、ユーザーが実際にタスクインスタンスの作業を開始していることを示すために使用します。
  4. task-end: TaskInstance.end(...) が呼び出されたときに発生します。これは、タスクの完了を示します。タスクがプロセスの実行に関連している場合、この呼び出しによってプロセスの実行が再開されることがあります。
注記
タスクには例外ハンドラーを関連付けることができます。詳細は、「 例外処理 」 を参照してください。

8.9. タスクタイマー

タスクにはタイマーを指定できます。「 タイマー 」 を参照してください。
タスクタイマーの cancel-event はカスタマイズできます。デフォルトでは、タスクタイマーはタスクが終了するとキャンセルされますが、タイマーの cancel-event 属性を使用すると、それを task-assign または task-start にカスタマイズできます。cancel-event は複数のイベントをサポートしています。cancel-event のタイプを組み合わせるには、属性のコンマ区切りリストでそれらを指定します。

8.10. タスクインスタンスのカスタマイズ

タスクインスタンスをカスタマイズするには、次の手順に従います。
  1. TaskInstance のサブクラスを作成します。
  2. org.jbpm.taskmgmt.TaskInstanceFactory 実装を作成します。
  3. jbpm.task.instance.factory 設定プロパティーを jbpm.cfg.xml ファイルの完全修飾クラス名に設定し、実装を設定します。
  4. TaskInstance のサブクラスを使用する場合は、サブクラスの Hibernate マッピングファイルを作成します (extends="org.jbpm.taskmgmt.exe.TaskInstance" を使用)。
  5. そのマッピングファイルを hibernate.cfg.xml のリストに追加します。

8.11. アイデンティティーコンポーネント

ユーザー、グループ、および権限の管理は、アイデンティティー管理 と呼ばれます。jBPM には、オプションの アイデンティティーコンポーネントが含まれています。これは、自社のデータストアに簡単に置き換えることができます。
jBPM アイデンティティー管理コンポーネントは、組織モデルの情報を保持し、これを使用してタスクを割り当てます。このモデルは、ユーザー、グループ、システム、およびこれらの間の関係を記述します。オプションで、権限とロールも含めることができます。
jBPM は、このモデルを、アクターをプロセスの実際の参加者として定義することで処理します。アクターは、actorId と呼ばれる ID によって識別されます。jBPM は、actorId に関する情報のみを持ちます。actorId は、最大限の柔軟性を得るために、java.lang.String として表されます。そのため、組織モデルとそのデータの構造に関する情報は、jBPM のコアエンジンの範囲外です。
jBPM の拡張機能として、単純なユーザーロールモデルを管理するためのコンポーネントが (将来的に) 提供される予定です。ユーザーとロール間のこのような多対多の関係は、J2EE およびサーブレット仕様で定義されているものと同じモデルであり、新規開発の出発点として役立つ可能性があります。
サーブレット、ejb、およびポートレットの仕様で使用されているユーザーロールモデルは、タスクの割り当てを処理するには不十分であることに注意してください。そのモデルは、ユーザーとロール間の多対多の関係です。これには、プロセスに関与するチームやユーザーの組織構造に関する情報は含まれません。

8.11.1. アイデンティティーモデル

図8.3 アイデンティティーモデルのクラス図

アイデンティティーモデルのクラス図
黄色のクラスは、後述する式の割り当てハンドラーに関連するクラスです。
User は、ユーザーまたはサービスを表します。Group は、あらゆる種類のユーザーグループです。グループをネストすることで、チーム、ビジネスユニット、および会社全体の間の関係をモデル化できます。グループには、階層グループと髪の色グループなどを区別するためのタイプがあります。Membership は、ユーザーとグループ間の多対多の関係を表します。メンバーシップは、会社内の役職を表すために使用できます。メンバーシップの名前は、ユーザーがグループ内で果たす役割を示すために使用できます。

8.11.2. 割り当て式

アイデンティティーコンポーネントには、タスク割り当て時にアクターを計算するための式を評価する 1 つの実装が付属しています。プロセス定義で割り当て式を使用する例を次に示します。
<process-definition>
  <task-node name='a'>
    <task name='laundry'>
      <assignment expression='previous --> group(hierarchy) --> member(boss)' />
    </task>
    <transition to='b' />
  </task-node>
      
      <para>Syntax of the assignment expression is like this:</para>
      first-term --> next-term --> next-term --> ... --> next-term

where

first-term ::= previous |
               swimlane(swimlane-name) |
               variable(variable-name) |
               user(user-name) |
               group(group-name)

and 

next-term ::= group(group-type) |
              member(role-name)
</programlisting>

8.11.2.1. 最初の項

式は左から右に解決されます。first-term は、アイデンティティーモデルの User または Group を指定します。後続の項は、中間のユーザーまたはグループからの次の項を計算します。
previous は、タスクが現在の認証済みアクターに割り当てられていることを意味します。これは、プロセスの前のステップを実行したアクターを意味します。
swimlane(swimlane-name) は、指定されたスイムレーンインスタンスからユーザーまたはグループを取得することを意味します。
variable(variable-name) は、指定された変数インスタンスからユーザーまたはグループを取得することを意味します。変数インスタンスには java.lang.String を含めることができます。その場合、そのユーザーまたはグループは、アイデンティティーコンポーネントから取得されます。または、変数インスタンスに User または Group オブジェクトが含まれています。
user(user-name) は、アイデンティティーコンポーネントから特定のユーザーを取得することを意味します。
group(group-name) は、アイデンティティーコンポーネントから特定のグループを取得することを意味します。

8.11.2.2. 次の項

group(group-type) は、ユーザーのグループを取得します。これは、前の項の結果が User である必要があることを意味します。この項は、ユーザーのすべてのメンバーシップで、指定された group-type のグループを検索します。
member(role-name) は、グループの特定のロールを実行するユーザーを取得します。前の項の結果が Group である必要があります。この項は、メンバーシップの名前が特定の role-name と一致するグループのメンバーシップを持つユーザーを検索します。

8.11.3. アイデンティティーコンポーネントの削除

会社のユーザーデータベースや LDAP システムなどの組織情報に独自のデータソースを使用する場合は、jBPM アイデンティティーコンポーネントを削除できます。必要な唯一のことは、hibernate.cfg.xml から次の行を削除することです。
<mapping resource="org/jbpm/identity/User.hbm.xml"/>
<mapping resource="org/jbpm/identity/Group.hbm.xml"/>
<mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
ExpressionAssignmentHandler は、アイデンティティーコンポーネントに依存しているため、そのまま使用することはできません。ExpressionAssignmentHandler を再利用してユーザーデータストアにバインドする場合は、ExpressionAssignmentHandler から拡張し、getExpressionSession メソッドをオーバーライドできます。
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);

第9章 スケジューラー

この章では、Business Process Manager における タイマー の役割について説明します。
タイマーは、プロセス内のイベントで作成できます。アクションの実行またはイベントの遷移をトリガーするようにタイマーを設定します。

9.1. タイマー

タイマーを設定する最も簡単な方法は、timer 要素 をノードに追加することです。次のサンプルコードは、その方法を示しています。
<state name='catch crooks'>
  <timer name='reminder' 
         duedate='3 business hours' 
         repeat='10 business minutes'
         transition='time-out-transition' >
    <action class='the-remainder-action-class-name' />
  </timer>
  <transition name='time-out-transition' to='...' />
</state>
ノードで指定されたタイマーは、そのノードが終了した後は実行されません。遷移とアクションはどちらもオプションです。タイマーが実行されると、以下のイベントが順番に発生します。
  1. タイプが timer のイベントが発生します。
  2. アクションが指定されている場合、それが実行されます。
  3. シグナルにより、指定された遷移で実行が再開されます。
すべてのタイマーには一意の名前が必要です。timer 要素に名前が指定されていない場合、デフォルトでノードの名前が使用されます。
アクション要素 (actionscript など) をサポートするには、タイマーアクションを使用します。
タイマーは、アクションによって作成およびキャンセルされます。関連する 2 つの action-element は、create-timercancel-timer です。実際には、上記の timer 要素は、node-enter での create-timer アクションと node-leave での cancel-timer アクションの簡略表記です。

9.2. スケジューラーのデプロイメント

プロセスの実行により、タイマーが作成およびキャンセルされ、タイマーストア に保存されます。別の タイマーランナー がこのストアを確認し、各タイマーを適切なタイミングで実行します。

図9.1 スケジューラーコンポーネントの概要

スケジューラーコンポーネントの概要

第10章 非同期継続

10.1. 概念

jBPM は グラフ指向プログラミング (GOP) をベースとしています。基本的に、GOP では、同時実行パスを処理できる単純状態マシンを指定しますが、特定の実行アルゴリズムでは、すべての状態遷移が単一スレッドのクライアント操作で行われます。デフォルトでは、クライアントのスレッドで状態遷移を実行するのが適切なアプローチです。このアプローチは、サーバー側のトランザクションに自然に適合するためです。プロセスの実行は、1 つのトランザクションの空間内で、ある "待機" 状態から別の "待機" 状態に移行します。
状況によっては、開発者はプロセス定義でトランザクションの境界を微調整する必要があります。jPDL では、プロセスの実行を非同期的に継続することを async="true" 属性で指定できます。async="true" は、イベントでトリガーされた場合にのみサポートされますが、すべてのノードタイプとすべてのアクションタイプで指定できます。

10.2. 例

通常、ノードは、トークンがノードに進入した後に常に実行されます。したがって、ノードはクライアントのスレッドで実行されます。2 つの例を見て、非同期継続について説明します。最初の例は、3 つのノードを持つプロセスの一部です。ノード 'a' は待機状態、ノード 'b' は自動化されたステップ、ノード 'c' は待機状態です。このプロセスには非同期の動作は含まれません。以下にその図を示します。
最初のフレームは、開始状況を示しています。トークンはノード 'a' を指しています。これは、実行パスが外部のトリガーを待機していることを意味します。そのトリガーは、トークンにシグナルを送信することによって与える必要があります。シグナルが到達すると、トークンはノード 'a' からノード 'b' への遷移を介して渡されます。トークンがノード 'b' に到達すると、ノード 'b' が実行されます。ノード 'b' は、待機状態として動作しない自動化されたステップ (メールの送信など) であることに注意してください。そのため、2 番目のフレームは、ノード 'b' が実行されているときに取得したスナップショットです。ノード 'b' はプロセスの自動化されたステップであるため、ノード 'b' の実行には、ノード 'c' への遷移を介したトークンの伝播が含まれます。ノード 'c' は待機状態です。そのため、3 番目のフレームはシグナルメソッドが返された後の最終的な状況を示しています。

図10.1 例 1: 非同期継続のないプロセス

例 1: 非同期継続のないプロセス
jBPM では "永続性" は必須ではありませんが、通常、シグナルはトランザクション内で呼び出されます。そのようなトランザクションの更新を見てみましょう。まず、トークンはノード 'c' を指すように更新されます。この更新は、JDBC 接続での GraphSession.saveProcessInstance の結果として、Hibernate によって生成されます。次に、自動化されたアクションが何らかのトランザクションリソースにアクセスして更新する場合、そのような更新は結合するか、同じトランザクションの一部にする必要があります。
2 番目の例は最初の例のバリアントであり、ノード 'b' に非同期継続が導入されています。ノード 'a' と 'c' は、最初の例と同じように動作します。つまり、待機状態として動作します。jPDL では、ノードは async="true" 属性を設定することによって非同期としてマークされます。
async="true" をノード 'b' に追加すると、プロセスの実行が 2 つの部分に分割されます。1 つ目の部分は、ノード 'b' が実行されるポイントまでプロセスを実行します。2 つ目の部分は、ノード 'b' を実行します。 その実行は待機状態 'c' で停止します。
したがって、トランザクションは、2 つの個別のトランザクション (各部分に 1 つずつ) に分割されます。1 つ目のトランザクションでノード 'a' から退出するには、外部のトリガー (Token.signal メソッドの呼び出し) が必要ですが、2 つ目のトランザクションは jBPM が自動的にトリガーして実行します。

図10.2 非同期継続を伴うプロセス

非同期継続を伴うプロセス
アクションについても、原則は同様です。async="true" 属性でマークされたアクションは、プロセスを実行するスレッドの外部で実行されます。永続性が設定されている場合 (デフォルトの設定)、アクションは別のトランザクションで実行されます。
jBPM では、非同期メッセージングシステムを使用して非同期継続を実現します。プロセスの実行が非同期で実行されるべきポイントに到達すると、jBPM は実行を中断し、コマンドメッセージを生成してコマンドエグゼキューターに送信します。コマンドエグゼキューターは、メッセージを受信すると、停止されたプロセスの実行を再開する別のコンポーネントです。
jBPM は、JMS プロバイダーまたはそのビルトインの非同期メッセージングシステムを使用するように設定できます。ビルトインのメッセージングシステムは、機能面でかなり制限されていますが、JMS を使用できない環境でもこの機能をサポートできます。

10.3. ジョブエグゼキューター

ジョブエグゼキューター は、プロセスの実行を非同期的に再開するコンポーネントです。ジョブメッセージが非同期メッセージングシステムを介して到達するのを待機し、それらを実行します。非同期継続に使用される 2 つのジョブメッセージは、ExecuteNodeJobExecuteActionJob です。
これらのジョブメッセージは、プロセスの実行によって生成されます。プロセスの実行中、非同期で実行する必要があるノードまたはアクションごとに、Job (Plain Old Java Object) が MessageService に送信されます。メッセージサービスは、JbpmContext に関連付けられており、送信する必要があるすべてのメッセージを収集します。
メッセージは JbpmContext.close() の一部として送信されます。このメソッドは、close() の呼び出しを、関連するすべてのサービスに対してカスケードします。実際のサービスは jbpm.cfg.xml で設定できます。サービスの 1 つである JmsMessageService は、デフォルトで設定されており、新しいジョブメッセージが利用可能であることをジョブエグゼキューターに通知します。
グラフ実行メカニズムは、インターフェイス MessageServiceFactory および MessageService を使用してメッセージを送信します。これは、非同期メッセージングサービスを設定可能にするためです (これも jbpm.cfg.xml にあります)。Java EE 環境では、DbMessageServiceJmsMessageService に置き換えて、アプリケーションサーバーの機能を活用できます。
以下は、ジョブエグゼキューターの仕組みの簡単な要約です。
"ジョブ" はデータベース内のレコードです。また、ジョブはオブジェクトであり、実行可能なものです。タイマーと非同期メッセージはどちらもジョブです。非同期メッセージの場合、dueDate は、メッセージが挿入されたときに現在の時刻に設定されます。ジョブエグゼキューターは、ジョブを実行する必要があります。これは 2 つのフェーズで行われます。
  • ディスパッチャースレッドはジョブを取得する必要があります。
  • エグゼキュータースレッドはジョブを実行する必要があります。
ジョブの取得とジョブの実行は、2 つの別個のトランザクションで行われます。ディスパッチャースレッドは、このノード上のすべてのエグゼキュータースレッドに代わって、データベースからジョブを取得します。エグゼキュータースレッドは、ジョブを受け取ると、その名前をジョブの所有者フィールドに追加します。各スレッドには、IP アドレスとシーケンス番号に基づく一意の名前があります。
ジョブの取得と実行の間に、スレッドが失敗することがあります。このような状況が発生した後にクリーンアップを行うために、ロック時間を確認するロックモニタースレッドがジョブエグゼキューターごとに 1 つあります。ロックモニタースレッドは、10 分以上ロックされていたジョブのロックを解除し、別のジョブ実行スレッドで実行できるようにします。
Hibernate の楽観的ロックを正しく機能させるためには、分離レベルを REPEATABLE_READ に設定する必要があります。REPEATABLE_READ に設定すると、競合するトランザクションのうちの 1 つに含まれる 1 つの行だけが、このクエリーによって確実に更新されます。
update JBPM_JOB job
set job.version = 2
    job.lockOwner = '192.168.1.3:2'
where 
    job.version = 1
繰り返し不可能な読み取りに設定すると、異常が発生する可能性があります。トランザクションは、以前に読み取ったデータを再び読み取り、トランザクションの前回の読み取り以降にコミットされた別のトランザクションによってデータが変更されていることを検出します。
繰り返し不可能な読み取りは楽観的ロックの問題です。そのため、分離レベル READ_COMMITTED では、繰り返し不可能な読み取りが発生する可能性があるため、十分ではありません。そのため、複数のジョブ実行スレッドを設定する場合は、REPEATABLE_READ が必要です。
ジョブエグゼキューターに関連する設定プロパティーは次のとおりです。
jbpmConfiguration
設定の取得元の Bean。
name
このエグゼキューターの名前。
重要
1 台のマシンで複数の jBPM インスタンスが起動している場合、この名前はノードごとに一意である必要があります。
nbrOfThreads
起動しているエグゼキュータースレッドの数。
idleInterval
保留中のジョブがない場合に、ジョブキューを確認するまでにディスパッチャースレッドが待機する間隔。
注記
ジョブがキューに追加されると、ディスパッチャースレッドに自動的に通知されます。
retryInterval
実行中にジョブが失敗した場合に、ジョブが次の再試行まで待機する間隔。このデフォルト値は 3 回です。
注記
再試行の最大回数は、jbpm.job.retries で設定されます。
maxIdleInterval
idleInterval の最長期間。
historyMaxSize
このプロパティーは非推奨であり、効果はありません。
maxLockTime
ロックモニタースレッドがロックを解除する前にジョブをロックできる最大時間。
lockMonitorInterval
ロックされたジョブの確認の間にロックモニタースレッドがスリープする期間。
lockBufferTime
このプロパティーは非推奨であり、効果はありません。

10.4. jBPM のビルトインの非同期メッセージング

jBPM のビルトインの非同期メッセージングを使用する場合、ジョブメッセージはデータベースに永続化されて送信されます。このメッセージの永続化は、jBPM プロセスの更新と同じトランザクションまたは JDBC 接続で行うことができます。
ジョブメッセージは JBPM_JOB テーブルに格納されます。
POJO コマンドエグゼキューター (org.jbpm.msg.command.CommandExecutor) は、データベーステーブルからメッセージを読み取り、実行します。POJO コマンドエグゼキューターの典型的なトランザクションは次のようになります。
  1. 次のコマンドメッセージの読み取り
  2. コマンドメッセージの実行
  3. コマンドメッセージの削除
コマンドメッセージの実行が失敗した場合、トランザクションはロールバックされます。その後、データベース内のメッセージにエラーメッセージを追加する新しいトランザクションが開始されます。コマンドエグゼキューターは、例外を含むメッセージをすべて除外します。

図10.3 POJO コマンドエグゼキューターのトランザクション

POJO コマンドエグゼキューターのトランザクション
コマンドメッセージに例外を追加するトランザクションが失敗すると、ロールバックされます。メッセージは例外のない状態でキューに残り、後で再試行されます。
重要
jBPM のビルトインの非同期メッセージングシステムは、マルチノードロックをサポートしていません。POJO コマンドエグゼキューターを複数回デプロイして、同じデータベースを使用するように設定することはできません。

第11章 ビジネスカレンダー

この章では、タスクとタイマーの期日を計算するために使用する Business Process Manager のカレンダー機能について説明します。
これは、基準日に期間を加算または減算することによって行われます。(基準日を省略した場合、デフォルトで現在の日付が使用されます。)

11.1. 期日

期日は、期間と基準日で構成されます。使用する式は次のとおりです。duedate ::= [<basedate> +/-] <duration>

11.1.1. 期間

期間は、次の式を使用して、絶対時間または営業時間で指定されます。duration ::= <quantity> [business] <unit>
上記の計算の <quantity> は、Double.parseDouble (quantity) で解析可能なテキストである必要があります。<unit> は、秒、分、時、日、週、月、年のいずれかになります。オプションの business フラグを追加すると、当該の期間で営業時間のみが考慮されます。(これがない場合、期間は絶対時間として解釈されます。)

11.1.2. 基準日

基準日は次のように計算されます。basedate ::= <EL>
上記の式の <EL> は、Java Date または Calendar オブジェクトに解決される任意の Java Expression Language 式にすることができます。
警告
他のオブジェクトタイプの変数を参照しないでください。JbpmException エラーが発生します。
基準日は、単純なタイマーの duedate 属性、タスクリマインダー、タスク内のタイマーなど、多くの場所でサポートされています。しかし、これらの要素の repeat 属性ではサポートされていません。

11.1.3. 期日の例

次の使用法はすべて有効です。
<timer name="daysBeforeHoliday" duedate="5 business days">...</timer>
<timer name="pensionDate" duedate="#{dateOfBirth} + 65 years" >...</timer>
<timer name="pensionReminder" duedate="#{dateOfPension} - 1 year" >...</timer>
<timer name="fireWorks" duedate="#{chineseNewYear} repeat="1 year" >...</timer>
<reminder name="hitBoss" duedate="#{payRaiseDay} + 3 days" repeat="1 week" />

11.2. カレンダーの設定

org/jbpm/calendar/jbpm.business.calendar.properties ファイルで営業時間を定義します。(この設定ファイルをカスタマイズするには、変更したコピーをクラスパスのルートに配置します。)
以下は、jbpm.business.calendar.properties にあるデフォルトの営業時間の仕様です。
hour.format=HH:mm
#weekday ::= [<daypart> [&amp; <daypart>]*]
#daypart ::= <start-hour>-<to-hour>
#start-hour and to-hour must be in the hour.format
#dayparts have to be ordered
weekday.monday=    9:00-12:00 &amp; 12:30-17:00
weekday.tuesday=   9:00-12:00 &amp; 12:30-17:00
weekday.wednesday= 9:00-12:00 &amp; 12:30-17:00
weekday.thursday=  9:00-12:00 &amp; 12:30-17:00
weekday.friday=    9:00-12:00 &amp; 12:30-17:00
weekday.saturday=
weekday.sunday=

day.format=dd/MM/yyyy
# holiday syntax: <holiday>
# holiday period syntax: <start-day>-<end-day>
# below are the belgian official holidays
holiday.1=  01/01/2005 # nieuwjaar
holiday.2=  27/3/2005  # pasen 
holiday.3=  28/3/2005  # paasmaandag 
holiday.4=  1/5/2005   # feest van de arbeid
holiday.5=  5/5/2005   # hemelvaart 
holiday.6=  15/5/2005  # pinksteren 
holiday.7=  16/5/2005  # pinkstermaandag 
holiday.8=  21/7/2005  # my birthday 
holiday.9=  15/8/2005  # moederkesdag 
holiday.10= 1/11/2005  # allerheiligen 
holiday.11= 11/11/2005 # wapenstilstand 
holiday.12= 25/12/2005 # kerstmis 

business.day.expressed.in.hours=             8
business.week.expressed.in.hours=           40
business.month.expressed.in.business.days=  21
business.year.expressed.in.business.days=  220

11.3. 例

次の例は、カレンダーのさまざまな使用方法を示しています。
<timer name="daysBeforeHoliday" duedate="5 business days">...</timer>

<timer name="pensionDate" duedate="#{dateOfBirth} + 65 years" >...</timer>

<timer name="pensionReminder" duedate="#{dateOfPension} - 1 year" >...</timer>

<timer name="fireWorks" duedate="#{chineseNewYear} repeat="1 year" >...</timer>

<reminder name="hitBoss" duedate="#{payRaiseDay} + 3 days" repeat="1 week" />
hour.format=HH:mm
#weekday ::= [<daypart> [&amp; <daypart>]*]
#daypart ::= <start-hour>-<to-hour>
#start-hour and to-hour must be in the hour.format
#dayparts have to be ordered
weekday.monday=    9:00-12:00 &amp; 12:30-17:00
weekday.tuesday=   9:00-12:00 &amp; 12:30-17:00
weekday.wednesday= 9:00-12:00 &amp; 12:30-17:00
weekday.thursday=  9:00-12:00 &amp; 12:30-17:00
weekday.friday=    9:00-12:00 &amp; 12:30-17:00
weekday.saturday=
weekday.sunday=

day.format=dd/MM/yyyy
# holiday syntax: <holiday>
# holiday period syntax: <start-day>-<end-day>
# below are the belgian official holidays
holiday.1=  01/01/2005 # nieuwjaar
holiday.2=  27/3/2005  # pasen 
holiday.3=  28/3/2005  # paasmaandag 
holiday.4=  1/5/2005   # feest van de arbeid
holiday.5=  5/5/2005   # hemelvaart 
holiday.6=  15/5/2005  # pinksteren 
holiday.7=  16/5/2005  # pinkstermaandag 
holiday.8=  21/7/2005  # my birthday 
holiday.9=  15/8/2005  # moederkesdag 
holiday.10= 1/11/2005  # allerheiligen 
holiday.11= 11/11/2005 # wapenstilstand 
holiday.12= 25/12/2005 # kerstmis 

business.day.expressed.in.hours=             8
business.week.expressed.in.hours=           40
business.month.expressed.in.business.days=  21
business.year.expressed.in.business.days=  220
この章では、ビジネスカレンダーの仕組みを説明しました。

第12章 メールサポート

この章では、JPDL で利用できる "標準" のメールサポートについて説明します。メール機能のさまざまな要素を設定する方法は、以下の情報を参照してください。

12.1. JPDL におけるメール

プロセスからメールを送信する時点を指定する方法は、4 つあります。

12.1.1. メールアクション

プロセスグラフ内のノードとしてメールを表示しない理由がある場合は、メールアクション を使用します。
注記
メールアクションは、通常のアクションを追加できる場所なら、どこでもプロセスに追加できます。
&lt;mail actors="#{president}" subject="readmylips" text="nomoretaxes" /&gt;
次のように、件名とテキストの属性を要素として指定します。
<mail actors="#{president}" >
    <subject>readmylips</subject>
    <text>nomoretaxes</text>
</mail>
各フィールドには、JSF ライクな式を含めることができます。
<mail
    to='#{initiator}'
    subject='websale'
    text='your websale of #{quantity} #{item} was approved' />
注記
式の詳細は、「 式 」 を参照してください。
受信者は、actorsto という 2 つの属性で指定します。to 属性は、メールアドレスのセミコロン区切りリストに "解決" される必要があります。actors 属性は、actorIds のセミコロン区切りリストに解決される必要があります。これらの actorIds は、さらにメールアドレスに解決されます。詳細は、「 アドレス解決 」 を参照してください。
<mail 
    to='admin@mycompany.com' 
    subject='urgent'
    text='the mailserver is down :-)' />
注記
受信者を指定する方法は、「 メールの受信者の指定 」 を参照してください。
メールは、テンプレートを使用して定義できます。テンプレートプロパティーを次のように上書きします。
<mail template='sillystatement' actors="#{president}" />
注記
テンプレートの詳細は、「 メールテンプレート 」 を参照してください。

12.1.2. メールノード

メールアクションと同様に、メールを送信するアクションも、ノードとしてモデル化できます。この場合、ランタイムの動作は同じですが、メールは プロセスグラフ 内のノードとして表示されます。
メールノードは、メールアクション とまったく同じ属性と要素をサポートします。(詳細は、「 メールアクション 」 を参照してください。)
<mail-node  name="send email" 
            to="#{president}"
            subject="readmylips" 
            text="nomoretaxes">
    <transition to="the next node" />
</mail-node>
重要
メールノードにある 退出 遷移が 1 つだけであることを必ず確認してください。

12.1.3. "タスク割り当て" メール

タスクがアクターに割り当てられたときに、通知メールを送信できます。この機能を設定するには、次の方法でタスクに notify="yes" 属性を追加します。
<task-node name='a'>
    <task name='laundry' swimlane="grandma" notify='yes' />
    <transition to='b' />
</task-node>
notify を yestrue、または on に設定すると、Business Process Manager は、タスクに割り当てられているアクターにメールを送信します。(このメールはテンプレートに基づいており、Web アプリケーションのタスクページへのリンクが含まれています。)

12.1.4. "タスクリマインダー" メール

タスクのリマインダーとしてメールを送信できます。JPDL の reminder 要素はタイマーを利用します。最も一般的に使用される属性は、duedaterepeat です。アクションを指定する必要はありません。
<task-node name='a'>
    <task name='laundry' swimlane="grandma" notify='yes'>
        <reminder duedate="2 business days" repeat="2 business hours"/>
    </task>
    <transition to='b' />
</task-node>

12.2. メールにおける式

フィールド torecipientssubject、および text には、JSF ライクな式を含めることができます。(式の詳細は、「 式 」 を参照してください。)
式では、変数 swimlanesprocess variables、および transient variables beans を使用できます。これらは、jbpm.cfg.xml ファイルで設定します。
式は、アドレス解決 機能と組み合わせることができます。詳細は、「 アドレス解決 」 を参照してください。
この例では、president という名前の swimlane が存在することを前提としています。
<mail   actors="#{president}" 
        subject="readmylips" 
        text="nomoretaxes" />
このコードは、その特定のプロセスの実行において president として振る舞うユーザーにメールを送信します。

12.3. メールの受信者の指定

12.3.1. 複数の受信者

複数の受信者を、actors および to フィールドにリストできます。リスト内の項目は、コロンまたはセミコロンで区切ります。

12.3.2. BCC アドレスへのメールの送信

ブラインドカーボンコピー (BCC) 受信者にメッセージを送信するには、プロセス定義の bccActors 属性と bcc 属性のいずれかを使用します。
<mail to='#{initiator}' 
      bcc='bcc@mycompany.com' 
      subject='websale' 
      text='your websale of #{quantity} #{item} was approved' />
別の方法として、jbpm.cfg.xml で一元的に設定されている場所に BCC メッセージを常に送信する方法があります。次の例は、その方法を示しています。
<jbpm-configuration>
    ...
    <string name="jbpm.mail.bcc.address" value="bcc@mycompany.com" />
</jbpm-configuration>

12.3.3. アドレス解決

Business Process Manger 全体で、アクターは actorIds によって参照されます。これは、プロセスの参加者を識別するための文字列です。アドレスリゾルバー は、actorIds をメールアドレスに変換します。
アドレス解決を適用するには、actors 属性を使用します。これに対して、アドレスを直接追加する場合は、アドレス解決の適用を実行しない to 属性を使用します。
アドレスリゾルバーが次のインターフェイスを実装していることを確認してください。
public interface AddressResolver extends Serializable {
	Object resolveAddress(String actorId);
}
アドレスリゾルバーは、文字列、文字列のコレクション、または文字列の配列の 3 つの型のいずれかを返します。(文字列は、必ず特定の actorId のメールアドレスを表すものである必要があります。)
アドレスリゾルバーの実装が Bean であることを確認してください。この Bean は、次の例のように、jbpm.mail.address.resolver という名前の jbpm.cfg.xml ファイルで設定する必要があります。
<jbpm-configuration>
    <bean name='jbpm.mail.address.resolver' 
        class='org.jbpm.identity.mail.IdentityAddressResolver' 
        singleton='true' />
</jbpm-configuration>
Business Process Manger の アイデンティティー コンポーネントには、アドレスリゾルバーが含まれています。このアドレスリゾルバーは、特定の actorId のユーザーを探します。ユーザーが存在する場合、そのメールアドレスが返されます。そうでない場合は、null が返されます。
注記
アイデンティティーコンポーネントの詳細は、「 アイデンティティーコンポーネント 」 を参照してください。

12.4. メールテンプレート

processdefinition.xml ファイルを使用してメールを指定する代わりに、テンプレートを使用できます。この場合も、各フィールドを processdefinition.xml で上書きできます。次のようにテンプレートを指定します。
<mail-templates>
    <variable name="BaseTaskListURL"
        value="http://localhost:8080/jbpm/task?id=" />

    <mail-template name='task-assign'>
        <actors>#{taskInstance.actorId}</actors>
        <subject>Task '#{taskInstance.name}'</subject>
        <text><![CDATA[Hi,
Task '#{taskInstance.name}' has been assigned to you.
Go for it: #{BaseTaskListURL}#{taskInstance.id}
Thanks.
---powered by JBoss jBPM---]]></text>
    </mail-template>

    <mail-template name='task-reminder'>
        <actors>#{taskInstance.actorId}</actors>
        <subject>Task '#{taskInstance.name}' !</subject>
        <text><![CDATA[Hey,
Don't forget about #{BaseTaskListURL}#{taskInstance.id} 
Get going !
---powered by JBoss jBPM---]]></text>
    </mail-template>

</mail-templates>
メールテンプレートでは、追加の変数を定義できます。この変数は式で使用できます。
次のように、jbpm.cfg.xml でテンプレートを含むリソースを設定します。
<jbpm-configuration>
  <string name="resource.mail.templates" value="jbpm.mail.templates.xml" />
</jbpm-configuration>

12.5. メールサーバーの設定

次のコード例に従って、jbpm.cfg.xml ファイルの jbpm.mail.smtp.host プロパティーを設定して、メールサーバーを設定します。
<jbpm-configuration>
    <string name="jbpm.mail.smtp.host" value="localhost" />
</jbpm-configuration>
または、さらにプロパティーを指定する必要がある場合は、次の方法でプロパティーファイルへのリソース参照を指定します。
<jbpm-configuration>
   <string name='resource.mail.properties' value='jbpm.mail.properties' />
</jbpm-configuration>

12.6. メール認証

12.6.1. メール認証の設定

次の設定を使用して、メールを送信するときに (SMTP) 認証を有効にすることができます。

表12.1 jBPM のメール認証プロパティー

プロパティー タイプ 説明
jbpm.mail.user
string
ユーザーのメールアドレス
jbpm.mail.password
string
そのメールアドレスのパスワード
jbpm.mail.smtp.starttls
boolean
SMTP サーバーで STARTTLS プロトコルを使用するかどうか
jbpm.mail.smtp.auth
boolean
SMTP 認証プロトコルを使用するかどうか
jbpm.mail.debug
boolean
javax.mail.Session インスタンスをデバッグモードに設定するかどうか

12.6.2. メール認証のロジック

上記のプロパティーに関しては、次のロジックが適用されます。
jbpm.mail.user プロパティーも jbpm.mail.password プロパティーも設定されていない場合、他の設定に関係なく認証は使用されません。
jbpm.mail.user プロパティーが設定されている場合、次の処理が行われます。
  • mail.smtp.submitter プロパティーが jbpm.mail.user プロパティーの値で設定されます。
  • jbpm エンジンが、メールの送信時に smtp サーバーへのログインを試みます。
jbpm.mail.user プロパティーと jbpm.mail.password プロパティーが設定されている場合、次の処理が行われます。
  • この場合にも、少なくとも jbpm.mail.user を設定した場合の処理が行われます。
  • jbpm.mail.smtp.auth プロパティーの値に関係なく、mail.smtp.auth プロパティーは true に設定されます。

12.7. "From" アドレスの設定

From アドレスフィールドのデフォルト値は、jbpm@noreply です。次のように、キー jbpm.mail.from.address を使用して jbpm.xfg.xml ファイルで設定します。
<jbpm-configuration>
    <string name='jbpm.mail.from.address' value='jbpm@yourcompany.com' />
</jbpm-configuration>

12.8. メールサポートのカスタマイズ

Business Process Manager のすべてのメールサポートは、1 つのクラス、つまり org.jbpm.mail.Mail に一元化されています。このクラスは ActionHandler の実装です。process XML でメールが指定されるたびに、mail クラスへの委譲が行われます。特定のニーズに合わせて、mail クラスから継承し、特定の動作をカスタマイズすることができます。メール委譲に使用するクラスを設定するには、次のように jbpm.cfg.xmljbpm.mail.class.name 設定文字列を指定します。
<jbpm-configuration>
  <string name='jbpm.mail.class.name'
  value='com.your.specific.CustomMail' />
</jbpm-configuration>
カスタマイズされたメールクラスは、解析時に読み取られます。アクションは、設定済み (またはデフォルト) のメールクラス名を参照するプロセス内で設定されます。したがって、プロパティーが変更された場合、すでにデプロイされているすべてのプロセスは、引き続き古いメールクラス名を参照します。これは、jBPM データベースに対して update ステートメントを送信するだけで変更できます。
この章では、さまざまなメール設定を行う方法について詳しく説明しました。お使いの環境で設定を練習することをお勧めします。

第13章 ロギング

この章では、Business Process Manager のロギング機能と、その機能を利用するさまざまな方法について説明します。
ロギングの目的は、プロセス実行の履歴を記録することです。各プロセス実行のランタイムデータが変更されると、変更がログに保存されます。
注記
この章で説明する プロセスロギング は、ソフトウェアロギング と混同しないよう注意してください。ソフトウェアロギングは、ソフトウェアプログラムの実行を (通常はデバッグ目的で) トレースします。プロセスロギングは、対照的に、プロセスインスタンスの実行を追跡します。
プロセスのログ情報が役立つ場面は数多くあります。これらの中で最も明白なのは、プロセス実行参加者によるプロセス履歴の参照です。
もう 1 つのユースケースは、ビジネスアクティビティーモニタリング (BAM) のユースケースです。これを使用すると、プロセス実行のログを照会または分析し、ビジネスプロセスに関する有用な統計情報を見つけることができます。この情報は、組織で "実際" のビジネスプロセス管理を実装する際の鍵となります。(実際のビジネスプロセス管理とは、組織がそのプロセスを管理する方法、情報技術によるプロセスのサポート方法、および反復的なプロセスでこれら 2 つを使用して相互に改善する方法に関係します。)
プロセスログは、"元に戻す" 操作の実装にも使用できます。ログには、ランタイムのすべての情報変更に関するレコードが含まれているため、変更を逆の順序で "再生" することで、プロセスを前の状態に戻すことができます。

13.1. ログの作成

Business Process Manager モジュールは、プロセスの実行時にログを生成します。ただし、ユーザーはプロセスログを挿入することもできます。(ログエントリーは、org.jbpm.logging.log.ProcessLog から継承する Java オブジェクトです。) プロセスログのエントリーは、ProcessInstance のオプションの拡張である LoggingInstance に追加されます。
Business Process Manager は、グラフ実行ログ、コンテキストログ、タスク管理ログなど、さまざまな種類のログを生成します。org.jbpm.logging.log.ProcessLog から始めることをお勧めします。これを使用して、継承ツリー の下層に移動することができるためです。
LoggingInstance は、すべてのログエントリーを収集します。ProcessInstance が保存されると、ログエントリーはここからデータベースにフラッシュされます。(ProcessInstancelogs フィールドは Hibernate にマッピングされていません。これは、各トランザクションでデータベースから取得されるこれらのログを回避するためです。)
ProcessInstance は実行パスのコンテキストで作成されるため、ProcessLog はそのトークンを参照します。トークンは、インデックスシーケンスジェネレーター としても機能します。(これにより後続のトランザクションで生成されるログに連続したシーケンス番号が付けられるため、これはログの取得にとって重要です。)
次の API メソッドを使用して、プロセスログを追加します。
public class LoggingInstance extends ModuleInstance {
  ...
  public void addLog(ProcessLog processLog) {...}
  ...
}
以下は、情報ログの UML 図です。

図13.1 jBPM のログ情報のクラス図

jBPM のログ情報のクラス図
CompositeLog は特殊なケースです。これは、多数の子の親ログとして機能するため、階層構造を適用する手段を提供します。ログの挿入には、次のアプリケーションプログラミングインターフェイスを使用します。
public class LoggingInstance extends ModuleInstance {
  ...
  public void startCompositeLog(CompositeLog compositeLog) {...}
  public void endCompositeLog() {...}
  ...
}
階層構造が一貫していることを確認するために、CompositeLog は常に try-finally-block で呼び出す必要があります。以下はその例です。
startCompositeLog(new MyCompositeLog());
try {
  ...
} finally {
  endCompositeLog();
}

13.2. ログ設定

特定のデプロイメントにとってログが重要でない場合は、jbpm.cfg.xml 設定ファイルのセクション jbpm-context からロギングの行を削除します。
<service name='logging'
    factory='org.jbpm.logging.db.DbLoggingServiceFactory' />
ログをフィルタリングするには、LoggingService のカスタム実装を記述します (これは DbLoggingService のサブクラスです)。その後、ロギング用のカスタム ServiceFactory を作成し、それを factory 属性で指定します。

13.3. ログの取得

プロセスインスタンスのログは、常にデータベースクエリーを介して取得する必要があります。LoggingSession を介してこれを実現するには、2 つの方法があります。
最初の方法は、プロセスインスタンスのすべてのログを取得する方法です。これらのログは、マップ内のトークンごとにグループ化されます。このマップは、ProcessLog のリストをプロセスインスタンス内のすべてのトークンに関連付けます。リストには、作成された順序と同じ順序で ProcessLogs が含まれます。
public class LoggingSession {
  ...
  public Map findLogsByProcessInstance(long processInstanceId) {...}
  ...
}
2 番目の方法は、特定のトークンのログを取得する方法です。リストには、作成された順序と同じ順序で ProcessLogs が含まれます。
public class LoggingSession {
  public List findLogsByToken(long tokenId) {...}
  ...
}
この章では、jBPM のロギングの仕組みとロギングのさまざまな用途の概要を説明しました。

第14章 jBPM プロセス定義言語

jBPM プロセス定義言語 (jPDL) は、jBPM フレームワークによって認識され、XML スキーマとして表現されるビジネスプロセスを定義するための表記法です。多くの場合、プロセス定義には、jPDL ドキュメントに加えてサポートファイルが必要です。これらのファイルはすべて、デプロイメントの プロセスアーカイブ にパッケージ化されています。

14.1. プロセスアーカイブ

プロセスアーカイブは、特定の内容レイアウトを持つ単なる ZIP アーカイブです。プロセスアーカイブの中心となるファイルは、processdefinition.xml と呼ばれます。このファイルは、ビジネスプロセスを jPDL 表記で定義し、自動化されたアクションとヒューマンタスクに関する情報を提供します。プロセスアーカイブには、アクションハンドラークラスやユーザーインターフェイスタスクフォームなど、プロセスに関連する他のファイルも含まれます。

14.1.1. プロセスアーカイブのデプロイ

プロセスアーカイブ は、次のいずれかの方法でデプロイできます。
  • Process Designer Tool
  • ant タスク
  • プログラム
Process Designer Tool を使用してプロセスアーカイブをデプロイするには、プロセスアーカイブフォルダーを右クリックし、Deploy process archive オプションを選択します。
jBPM アプリケーションサーバー統合モジュールには、gpd-deployer Web アプリケーションが含まれています。このアプリケーションは、GPD Deployer Servlet と呼ばれる、プロセスアーカイブをアップロードするためのサーブレットを備えています。このサーブレットは、プロセスアーカイブを受信し、設定されたデータベースにデプロイできます。
ant タスクを使用してプロセスアーカイブをデプロイするには、次のようにタスクを定義して呼び出します。
<target name="deploy-process">
  <taskdef name="deployproc" classname="org.jbpm.ant.DeployProcessTask">
    <classpath location="jbpm-jpdl.jar" />
  </taskdef> 
  <deployproc process="build/myprocess.par" />
</target>
より多くのプロセスアーカイブを一度にデプロイするには、ネストされた fileset 要素を使用します。DeployProcessTask の属性は次のとおりです。

表14.1 DeployProcessTask の属性

属性 説明 必須かどうか
process
プロセスアーカイブへのパス。
はい。ネストされたリソースコレクション要素が使用されている場合を除きます。
jbpmcfg
デプロイメント時にロードする jBPM 設定リソース。
いいえ。デフォルトは jbpm.cfg.xml です。
failonerror
false の場合、プロセス定義がデプロイに失敗したときに、警告メッセージをログに記録しますが、ビルドは停止しません。
いいえ。デフォルトは true です。
プロセスアーカイブをプログラムでデプロイするには、org.jbpm.graph.def.ProcessDefinition クラスのいずれかの parseXXX メソッドを使用します。

14.1.2. プロセスのバージョン管理

プロセスインスタンスは常に、それが起動されたプロセス定義と同じプロセス定義で実行されます。ただし、jBPM では、同じ名前の複数のプロセス定義がデータベースに共存できます。通常、プロセスインスタンスは、その時点で利用可能な最新バージョンで起動し、その存続期間全体にわたって同じプロセス定義で実行され続けます。新しいバージョンがデプロイされると、新しく作成されたインスタンスは最新バージョンで起動しますが、古いプロセスインスタンスは古いプロセス定義で実行され続けます。
プロセスに Java クラスへの参照が含まれている場合、これら Java クラスは次の 2 つの方法のいずれかで jBPM ランタイム環境で使用できるようになります。
  • jBPM クラスローダーがこれらのクラスを認識できるようにする
    そのためには、委譲クラスを jbpm-jpdl.jar の "隣" の .jar ファイルに入れて、すべてのプロセス定義がそのクラスファイルを参照できるようにします。Java クラスもプロセスアーカイブに含めることができます。委譲クラスをプロセスアーカイブに含めた場合 (そしてそれらが jbpm クラスローダーによって認識されない場合)、jBPM はこれらのクラスをプロセス定義内でバージョン管理します。
    注記
    プロセスクラスローディングの詳細は、「委譲」 を参照してください。
プロセスアーカイブがデプロイされると、jBPM データベースに プロセス定義 が作成されます。プロセス定義は、名前に基づいてバージョン管理します。名前付きのプロセスアーカイブがデプロイされると、デプロイヤーがそれにバージョン番号を割り当てます。これは、同じ名前のプロセス定義に割り当てられている最大の番号を検索し、その値に 1 を追加することによって行われます。(名前のないプロセス定義は、常に -1 という形でバージョン付けされます。)

14.1.3. デプロイされたプロセス定義の変更

警告
デプロイ後にプロセス定義を変更することは推奨できません。プロセスインスタンスを新しいバージョンのプロセス定義に移行することを推奨します。
このプロセスを行う前に、次の要因を考慮してください。
  • org.jbpm.db.GraphSession のメソッド loadProcessDefinitionfindProcessDefinition を介してロードされたプロセス定義、またはアソシエーショントラバーサルを介して到達されたプロセス定義の更新に制限はありません。ただし、setStartState(null) などのいくつかの呼び出しにより、プロセスが台無しになることが 非常によくある ため、注意してください。
  • プロセス定義は変更されることが想定されていないため、出荷時の Hibernate 設定では、定義クラスとコレクションの nonstrict-read-write キャッシュストラテジーが指定されています。このストラテジーにより、コミットされていない更新を他のトランザクションから認識できるようになります。

14.1.4. プロセスインスタンスの移行

プロセス定義を変更する別の方法は、各プロセスインスタンスを新しいバージョンに移行することです。ビジネスプロセスは長期にわたって存続するため、移行は簡単ではないことを考慮してください。
注記
これは実験的な機能です。
定義データ、実行データ、ロギングデータは明確に区別されます。この区別が存在するため、まずプロセスの新しいバージョンをまずデプロイする必要があります。その後で、プロセスインスタンスを新しいバージョンに移行します。トークンまたはタスクインスタンスが、ターゲットプロセス定義で削除されたノードまたはタスク定義を参照している場合は、移行時に変換が必要でます。ロギングデータは 2 つのプロセス定義に分散されることに注意してください。これにより、ツールを開発して統計情報の計算を行う際に問題が発生することがあります。
プロセスインスタンスを新しいバージョンに移行するには、以下に示すように ChangeProcessInstanceVersionCommand を実行します。
new ChangeProcessInstanceVersionCommand()
  .processName("commute")
  .nodeNameMappingAdd("drive to destination", "ride bike to destination")
  .execute(jbpmContext);

14.2. 委譲

委譲 メカニズムを使用して、プロセス実行にカスタムコードを含めます。

14.2.1. jBPM クラスローダー

jBPM クラスローダーは、jBPM クラスをロードするローダーです。クラスを jBPM クラスローダーから認識可能にするには、クラスを JAR ファイルにパックし、JAR を jbpm-jpdl.jar と同じ場所に配置します。Web アプリケーションの場合、カスタム JAR ファイルを jbpm-jpdl.jar と一緒に WEB-INF/lib に配置します。

14.2.2. プロセスクラスローダー

委譲クラスは、それぞれの プロセスクラスローダー を介してロードされます。プロセスクラスローダーは、親として jBPM クラスローダーを持ちます。これは、1 つの特定のプロセス定義でデプロイされたクラスを追加します。クラスをプロセス定義に追加するには、それらをプロセスアーカイブの classes ディレクトリーに配置します。これは、プロセス定義に追加されたクラスをバージョン管理する場合にのみ役立つことに注意してください。バージョン管理が不要な場合は、代わりにクラスを jBPM クラスローダーで使用できるようにします。
リソース名がスラッシュで始まらない場合、リソースはプロセスアーカイブの classes ディレクトリーからも読み込まれます。このディレクトリーの外にあるリソースをロードするには、パスの先頭に二重スラッシュ (//) を付けます。たとえば、プロセスアーカイブのルートにある data.xml をロードするには、class.getResource("//data.xml") を呼び出します。

14.2.3. 委譲の設定

委譲クラスには、プロセス実行内から呼び出されるユーザーコードが含まれます。最も一般的な例は アクション です。アクションの場合、プロセス内のイベントで ActionHandler インターフェイスの実装を呼び出すことができます。委譲は、processdefinition.xml ファイルで指定します。委譲を指定するときに、次の 3 つのデータを指定できます。
  1. クラス名 (必須): これは委譲クラスの完全修飾名です。
  2. 設定タイプ (オプション): これは、委譲オブジェクトをインスタンス化して設定する方法を指定します。デフォルトでは、コンストラクターが使用され、設定情報は無視されます。
  3. 設定 (オプション): これは委譲オブジェクトの設定であり、設定タイプに必要な形式である必要があります。
設定の各タイプを以下に説明します。

14.2.3.1. config-type フィールド

これはデフォルトの設定タイプです。config-type フィールドは、まず委譲クラスのオブジェクトをインスタンス化してから、設定で指定されたオブジェクトフィールドに値を設定します。設定は XML ファイルに保存されます。このファイルでは、要素名がクラスのフィールド名に対応している必要があります。要素の内容テキストは、対応するフィールドに配置されます。必要かつ可能な場合は、要素の内容テキストがフィールド型に変換されます。
サポートされている型変換は次のとおりです。
  • 文字列はトリミングされますが、変換されません。
  • int、long、float、double などのプリミティブ型。
  • プリミティブ型の基本的なラッパークラス。
  • リスト、セット、およびコレクション。このような場合、xml-content の各要素はコレクションの要素と見なされ、変換を適用して再帰的に解析されます。要素の型が java.lang.String と異なる場合は、完全修飾型名で type 属性を指定することにより、その型を指定します。たとえば、このコードは文字列の ArrayListnumbers フィールドに挿入します。
    <numbers>
      <element>one</element>
      <element>two</element>
      <element>three</element>
    </numbers>
    要素内のテキストは、文字列コンストラクターを持つ任意のオブジェクトに変換できます。文字列以外の型を使用するには、フィールド (この場合は numbers) で element-type を指定します。
    マップの別の例を次に示します。
    <numbers>
      <entry><key>one</key><value>1</value></entry>
      <entry><key>two</key><value>2</value></entry>
      <entry><key>three</key><value>3</value></entry>
    </numbers>
  • この場合、フィールド要素のそれぞれに、1 つの key と 1 つの value サブ要素がある必要があります。変換規則を再帰的に使用して、これらの両方を解析します。コレクションと同様に、type 属性を指定しなかった場合は、java.lang.String への変換が意図されていると見なされます。
  • org.dom4j.Element
  • その他の型の場合は、文字列コンストラクターが使用されます。
次のクラスを参照してください。
public class MyAction implements ActionHandler {
  // access specifiers can be private, default, protected or public
  private String city;
  Integer rounds;
  ...
}
これは、そのクラスの有効な設定です。
...
<action class="org.test.MyAction">
  <city>Atlanta</city>
  <rounds>5</rounds>
</action>
...

14.2.3.2. config-type Bean

これは config-type フィールドと同じですが、その場合、プロパティーは "setter" メソッドを介して設定されます。ここでは、プロパティーはフィールドに直接設定されます。同じ変換が適用されます。

14.2.3.3. config-type コンストラクター

このメソッドは、委譲 XML 要素のすべての内容を取得し、それらをテキストとして委譲クラスコンストラクターに渡します。

14.2.3.4. config-type configuration-property

デフォルトのコンストラクターを使用する場合、このメソッドは委譲 XML 要素のすべての内容を取得し、それをテキストとして void configure(String); メソッドに渡します。

14.3. 式

JSP/JSF EL ライクな式言語のサポートは限定されています。アクション、割り当て、決定条件では、expression="#{myVar.handler[assignments].assign}" のような式を記述できます。
注記
この式言語の詳細は、次のチュートリアルを参照してください。http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html
jPDL と JSF の式言語は似ています。jPDL EL は JSF EL をベースとしていますが、後者とは対照的に、jPDL EL は #{...} 表記を採用し、メソッドバインディングのサポートを備えています。
コンテキストに応じて、次の暗黙的オブジェクトと同様に、プロセスおよびタスクのインスタンス変数を開始変数として使用できます。
  • taskInstance (org.jbpm.taskmgmt.exe.TaskInstance)
  • processInstance (org.jbpm.graph.exe.ProcessInstance)
  • processDefinition (org.jbpm.graph.def.ProcessDefinition)
  • token (org.jbpm.graph.exe.Token)
  • taskMgmtInstance (org.jbpm.taskmgmt.exe.TaskMgmtInstance)
  • contextInstance (org.jbpm.context.exe.ContextInstance)
この機能は、JBoss SEAM 環境 (http://www.jboss.com/products/seam) で使用した場合に効果的です。JBPM と SEAM は統合されているため、すべての backed Bean や Enterprise Java Bean などにプロセス定義内からアクセスできるようになります。

14.4. jPDL XML スキーマ

jPDL スキーマは、プロセスアーカイブの processdefinition.xml ファイルで使用されるスキーマです。

14.4.1. 検証

jPDL XML ドキュメントを解析するとき、jBPM は次の 2 つの条件が満たされると、それをスキーマに対して検証します。
  1. スキーマは XML ドキュメントで参照されます。
    <process-definition xmlns="urn:jbpm.org:jpdl-3.2">
      ...
    </process-definition>
  2. Xerces パーサーはクラスパスにあります。
注記
jPDL スキーマは ${jbpm.home}/src/java.jbpm/org/jbpm/jpdl/xml/jpdl-3.2.xsd または http://jbpm.org/jpdl-3.2.xsd で確認してください。

14.4.2. process-definition

表14.2 プロセス定義スキーマ

名前 タイプ 多重度 説明
name 属性 任意 これはプロセスの名前です。
swimlane 要素 [0..*] これはプロセスで使用される swim-lane です。swim-lane はプロセスの役割を表し、タスクの割り当てに使用されます。
start-state 要素 [0..1] これはプロセスの開始状態です。start-state のないプロセスは有効ですが、実行できないことに注意してください。
{end-state|state|node|task-node|process-state|super-state|fork|join|decision} 要素 [0..*] これらはプロセス定義のノードです。ノードのないプロセスは有効ですが、実行できないことに注意してください。
event 要素 [0..*] これはアクションのコンテナーとして機能します。
{action|script|create-timer|cancel-timer} 要素 [0..*] これらは、イベントおよび遷移から参照できるグローバルに定義されたアクションです。これらのアクションを参照するには、名前を指定する必要があることに注意してください。
task 要素 [0..*] これは、アクションなどで使用できるグローバルに定義されたタスクです。
exception-handler 要素 [0..*] これは、このプロセス定義の委譲クラスによって出力されたすべてのエラーに適用される例外ハンドラーのリストです。

14.4.3. node

表14.3 ノードスキーマ

名前 タイプ 多重度 説明
{action|script|create-timer|cancel-timer} 要素 1 これは、このノードの動作を表すカスタムアクションです。
共通のノード要素 「共通のノード要素」

14.4.4. 共通のノード要素

表14.4 共通のノードスキーマ

名前 タイプ 多重度 説明
name 属性 required これはノードの名前です。
async 属性 { true | false }、デフォルトは false true に設定すると、このノードは非同期で実行されます。10章 非同期継続 も併せて参照してください。
transition 要素 [0..*] これは退出遷移です。ノードから退出する各遷移には、個別の名前が *必要* です。名前を持たない遷移は、最大 1 つまで許可されます。指定された最初の遷移は、デフォルト遷移と呼ばれます。遷移を指定せずにノードから退出すると、デフォルトの遷移が取得されます。
event 要素 [0..*] サポートされているイベントタイプは、{node-enter|node-leave} の 2 つです。
exception-handler 要素 [0..*] これは、このプロセスノード内から委譲クラスによって出力されたすべてのバグに適用される例外ハンドラーのリストです。
timer 要素 [0..*] これは、このノードでの実行時間をモニタリングするタイマーを指定します。

14.4.5. start-state

表14.5 開始状態スキーマ

名前 タイプ 多重度 説明
name 属性 任意 これはノードの名前です。
task 要素 [0..1] これは、このプロセスの新しいインスタンスを開始するため、またはプロセスのイニシエーターをキャプチャーするために使用されるタスクです。「 開始タスクのスイムレーン 」 を参照
event 要素 [0..*] サポートされているイベントタイプは {node-leave} です。
transition 要素 [0..*] これは退出遷移です。ノードから退出する各遷移には、個別の名前が必要です。
exception-handler 要素 [0..*] これは、このプロセスノード内から委譲クラスによって出力されたすべてのバグに適用される例外ハンドラーのリストです。

14.4.6. end-state

表14.6 終了状態スキーマ

名前 タイプ 多重度 説明
name 属性 required これは end-state の名前です。
end-complete-process 属性 任意 end-complete-processfalse に設定されている場合、この end-state を完了するトークンのみが完了します。このトークンが最後に終了した子だった場合、親トークンが再帰的に終了します。プロセスインスタンス全体を確実に終了するには、このプロパティーを true に設定します。
event 要素 [0..*] サポートされているイベントタイプは {node-enter} です。
exception-handler 要素 [0..*] これは、このプロセスノード内から委譲クラスによって出力されたすべてのバグに適用される例外ハンドラーのリストです。

14.4.7. state

表14.7 状態スキーマ

名前 タイプ 多重度 説明
共通のノード要素 「共通のノード要素」 を参照

14.4.8. task-node

表14.8 タスクノードスキーマ

名前 タイプ 多重度 説明
signal 属性 任意 これは {unsynchronized|never|first|first-wait|last|last-wait} で、デフォルトは last です。タスクの完了が プロセスの実行継続 に影響を与える方法を指定します。
create-tasks 属性 任意 これは {yes|no|true|false} で、デフォルトは true です。ランタイム時の計算で、どのタスクを作成する必要があるかを決定する必要がある場合は、false に設定します。その場合、node-enter にアクションを追加し、そのアクションにタスクを作成して create-tasksfalse に設定します。
end-tasks 属性 任意 これは {yes|no|true|false} で、デフォルトは false です。node-leaveremove-taskstrue に設定されている場合、開いているすべてのタスクが終了します。
task 要素 [0..*] これは、実行がこのタスクノードに到達したときに作成されるタスクです。
共通のノード要素 「共通のノード要素」 を参照

14.4.9. process-state

表14.9 プロセス状態スキーマ

名前 タイプ 多重度 説明
sub-process 要素 1 これは、このノードに関連付けられているサブプロセスです。
variable 要素 [0..*] これは、開始時にスーパープロセスからサブプロセスにデータをコピーする方法と、サブプロセスの完了時にサブプロセスからスーパープロセスにデータをコピーする方法を指定します。
共通のノード要素 「共通のノード要素」 を参照

14.4.10. super-state

表14.10 スーパーステートスキーマ

名前 タイプ 多重度 説明
{end-state|state|node|task-node|process-state|super-state|fork|join|decision} 要素 [0..*] これらはスーパーステートのノードです。スーパーステートはネストできます。
共通のノード要素 「共通のノード要素」 を参照

14.4.11. fork

表14.11 フォークスキーマ

名前 タイプ 多重度 説明
共通のノード要素 「共通のノード要素」 を参照

14.4.12. join

表14.12 結合スキーマ

名前 タイプ 多重度 説明
共通のノード要素 「共通のノード要素」 を参照

14.4.13. decision

表14.13 決定スキーマ

名前 タイプ 多重度 説明
handler 要素 'handler' 要素または遷移の条件のいずれかを指定する必要があります。 org.jbpm.jpdl.Def.DecisionHandler 実装の名前
transition conditions 決定から退出する遷移の属性または要素のテキスト
すべての遷移には保護条件が含まれる場合があります。決定ノードは、条件を持つ退出遷移を調べ、条件が真である最初の遷移を選択します。
条件が満たされない場合は、デフォルト の遷移が取得されます。デフォルト遷移は、存在する場合は最初の無条件遷移であり、存在しない場合は最初の条件付き遷移です。遷移はドキュメント順に考慮されます。
条件付き ("保護された") 遷移のみが使用可能で、遷移の条件がいずれも true と評価されない場合、例外が出力されます。
共通のノード要素 「共通のノード要素」 を参照

14.4.14. event

表14.14 イベントスキーマ

名前 タイプ 多重度 説明
type 属性 required これは、イベントが配置される要素に対して相対的に表現されるイベントタイプです。
{action|script|create-timer|cancel-timer} 要素 [0..*] これは、このイベントで実行する必要があるアクションのリストです。

14.4.15. transition

表14.15 遷移スキーマ

名前 タイプ 多重度 説明
name 属性 任意 これは遷移の名前です。ノードから退出する各遷移には、個別の名前が *必要* であることに注意してください。
to 属性 required これは宛先ノードの階層名です。階層名の詳細は、「 階層名 」 を参照してください。
condition 属性または要素のテキスト 任意 これは 保護条件 式です。この条件属性 (または子要素) を決定ノードで使用するか、ランタイム時にトークンで使用可能な遷移を計算します。条件は、決定ノードから退出する遷移でのみ許可されます。
{action|script|create-timer|cancel-timer} 要素 [0..*] これらは、この遷移が発生したときに実行されるアクションです。遷移のアクションはイベントに入れる必要がないことに注意してください (イベントは 1 つしかないため)。
exception-handler 要素 [0..*] これは、このプロセスノード内から委譲クラスによって出力されたすべてのバグに適用される例外ハンドラーのリストです。

14.4.16. action

表14.16 アクションスキーマ

名前 タイプ 多重度 説明
name 属性 任意 これはアクションの名前です。アクションに名前を付けると、プロセス定義から検索できます。これは、ランタイムアクションやアクションの宣言を 1 回だけ行う場合に役立ちます。
class 属性 ref-name または expression のいずれか これは、org.jbpm.graph.def.ActionHandler インターフェイスを実装するクラスの完全修飾クラス名です。
ref-name 属性 ref-name または class のいずれか これは参照されるアクションの名前です。参照されるアクションが指定されている場合、このアクションの内容はそれ以上処理されません。
expression 属性 expression、class 、または ref-name のいずれか これは、メソッドに解決される jPDL 式です。「 式 」 も併せて参照してください。
accept-propagated-events 属性 任意 オプションは {yes|no|true|false} です。デフォルトは yes|true です。false に設定した場合、アクションは、このアクションの要素で発生したイベントでのみ実行されます。詳細は、「 イベントの受け渡し 」 を参照してください。
config-type 属性 任意 オプションは {field|bean|constructor|configuration-property} です。これは、action-object を構築する方法と、この要素の内容をその action-object の設定情報として使用する方法を指定します。
async 属性 {true|false} 'async="true" は、イベントでトリガーされた場合にのみ、アクション でサポートされます。デフォルト値は false です。これは、アクション が実行のスレッドで実行されることを意味します。true に設定した場合、メッセージがコマンドエグゼキューターに送信され、そのコンポーネントがアクションを別のトランザクションで非同期に実行します。
{content} 任意 アクションの内容は、カスタムアクション実装の設定情報として使用できます。これにより、再利用可能な委譲クラスを作成できます。

14.4.17. script

表14.17 スクリプトスキーマ

名前 タイプ 多重度 説明
name 属性 任意 これはスクリプトアクションの名前です。アクションに名前を付けると、プロセス定義から検索できます。これは、ランタイムアクションやアクションの宣言を 1 回だけ行う場合に役立ちます。
accept-propagated-events 属性 任意 [0..*] {yes|no|true|false}。デフォルトは yes|true です。false に設定すると、アクションは、このアクションの要素で発生したイベントでのみ実行されます。詳細は、「 イベントの受け渡し 」 を参照してください。
expression 要素 [0..1] Beanshell スクリプト。variable 要素を指定しない場合は、式を script 要素の内容として記述できます (expression 要素タグは省略します)。
variable 要素 [0..*] スクリプトの in 変数。in 変数が指定されていない場合、現在のトークンのすべての変数がスクリプト評価にロードされます。スクリプト評価にロードする変数の数を制限する場合は、in 変数を使用します。

14.4.18. expression

表14.18 式スキーマ

名前 タイプ 多重度 説明
{content} Bean シェルスクリプト。

14.4.19. variable

表14.19 変数スキーマ

名前 タイプ 多重度 説明
name 属性 required プロセスの変数名
access 属性 任意 デフォルトは read,write です。これは、アクセス指定子のコンマ区切りリストです。これまでに使用したアクセス指定子は、readwrite、および required のみです。"required" は、タスク変数をプロセス変数に送信する場合にのみ関係します。
mapped-name 属性 任意 デフォルト設定は変数名です。これは変数名がマップされる名前を指定します。mapped-name の意味は、この要素が使用されるコンテキストに依存します。スクリプトの場合、これは script-variable-name になります。タスクコントローラーの場合、これはタスクフォームパラメーターのラベルになります。プロセス状態の場合、これはサブプロセスで使用される変数名になります。

14.4.20. handler

表14.20 ハンドラースキーマ

名前 タイプ 多重度 説明
expression 属性 expression または class のいずれか jPDL 式。返された結果は toString () メソッドで文字列に変換されます。結果の文字列が退出遷移の 1 つと一致する必要があります。「 式 」も併せて参照してください。
class 属性 class または ref-name のいずれか org.jbpm.graph.node.DecisionHandler インターフェイスを実装するクラスの完全修飾クラス名。
config-type 属性 任意 {field|bean|constructor|configuration-property}。action-object を構築する方法と、この要素の内容をその action-object の設定情報として使用する方法を指定します。
{content} 任意 ハンドラーの内容は、カスタムハンドラー実装の設定情報として使用できます。これにより、再利用可能な委譲クラスを作成できます。

14.4.21. timer

表14.21 タイマースキーマ

名前 タイプ 多重度 説明
name 属性 任意 タイマーの名前。名前が指定されていない場合は、包含するノードの名前が使用されます。すべてのタイマーには一意の名前が必要であることに注意してください。
duedate 属性 required タイマーの作成からタイマーの実行までの期間を指定する時間幅 (必要に応じて営業時間で表します)。この構文については、「 期間 」 を参照してください。
repeat 属性 任意 {duration | 'yes' | 'true'}。duedate にタイマーを実行した後、'repeat' により、必要に応じてノードから退出するまでタイマー実行を繰り返す期間を指定します。yes または true を指定した場合、duedate と同じ期間が繰り返されます。この構文については、「 期間 」 を参照してください。
transition 属性 任意 タイマーイベントが発生し、アクション (存在する場合) が実行された後、タイマーが実行されるときに取得される遷移の名前。
cancel-event 属性 任意 この属性は、タスクのタイマーでのみ使用されます。タイマーをキャンセルするイベントを指定します。デフォルトでは、これは task-end イベントですが、たとえば task-assigntask-start にも設定できます。属性のコンマ区切りリストで指定することで、cancel-event タイプを組み合わせることができます。
{action|script|create-timer|cancel-timer} 要素 [0..1] このタイマーが起動したときに実行する必要があるアクション。

14.4.22. create-timer

表14.22 タイマー作成スキーマ

名前 タイプ 多重度 説明
name 属性 任意 タイマーの名前。この名前は、cancel-timer アクションでタイマーをキャンセルするために使用できます。
duedate 属性 required タイマーの作成からタイマーの実行までの期間を指定する時間幅 (必要に応じて営業時間で表します)。この構文については、「 期間 」 を参照してください。
repeat 属性 任意 {duration | 'yes' | 'true'}。duedate にタイマーを実行した後、'repeat' により、必要に応じてノードから退出するまでタイマー実行を繰り返す期間を指定します。yes または true を指定した場合、duedate と同じ期間が繰り返されます。この構文については、「 期間 」 を参照してください。
transition 属性 任意 タイマーイベントが発生し、アクション (存在する場合) が実行された後、タイマーが実行されるときに取得される遷移の名前。

14.4.23. cancel-timer

表14.23 キャンセルタイマースキーマ

名前 タイプ 多重度 説明
name 属性 任意 キャンセルするタイマーの名前。

14.4.24. task

表14.24 タスクスキーマ

名前 タイプ 多重度 説明
name 属性 任意 タスクの名前。名前付きのタスクは、TaskMgmtDefinition を介して参照および検索できます。
blocking 属性 任意 {yes|no|true|false}。デフォルトは false です。blocking を true に設定した場合、タスクが完了していないときにノードから退出することはできません。false (デフォルト) に設定すると、トークンのシグナルは実行を継続してノードから退出することができます。blocking は通常、ユーザーインターフェイスによって強制されるため、デフォルトは false に設定されています。
signalling 属性 任意 {yes|no|true|false}。デフォルトは true です。signalling を false に設定した場合、このタスクがトークンの継続をトリガーする機能を持つことはありません。
duedate 属性 任意 11章 ビジネスカレンダー で説明されているように、絶対時間または営業時間で表される期間です。
swimlane 属性 任意 スイムレーンへの参照。タスクにスイムレーンが指定されている場合、割り当ては無視されます。
priority 属性 任意 {highest, high, normal, low, lowest} のいずれか。または、優先順位として任意の整数を指定できます。参考: (最高 = 1、最低 = 5)
assignment 要素 任意 タスクの作成時にアクターにタスクを割り当てる委譲を記述します。
event 要素 [0..*] サポートされているイベントタイプは、{task-create|task-start|task-assign|task-end} です。task-assign については、TaskInstance に非永続プロパティー previousActorId が追加されています。
exception-handler 要素 [0..*] このプロセスノードでスローされる委譲クラスによってスローされるすべての例外に適用される例外ハンドラーのリスト。
timer 要素 [0..*] このタスクの実行時間をモニタリングするタイマーを指定します。タスクタイマーに特化した cancel-event を指定できます。デフォルトでは cancel-eventtask-end ですが、task-assigntask-start などにカスタマイズできます。
controller 要素 [0..1] プロセス変数をタスクフォームパラメーターに変換する方法を指定します。タスクフォームパラメーターは、タスクフォームをユーザーに表示するためにユーザーインターフェイスによって使用されます。

14.4.25. スイムレーン

表14.25 スイムレーンスキーマ

名前 タイプ 多重度 説明
name 属性 required スイムレーンの名前。スイムレーンは、TaskMgmtDefinition を介して参照および検索できます。
assignment 要素 [1..1] このスイムレーンの割り当てを指定します。割り当ては、このスイムレーンで最初のタスクインスタンスが作成されたときに実行されます。

14.4.26. 割り当て

表14.26 割り当てスキーマ

名前 タイプ 多重度 説明
expression 属性 任意 歴史的な理由から、この属性式は、jPDL 式を 参照するものではなく、jBPM アイデンティティーコンポーネントの割り当て式です。jBPM アイデンティティーコンポーネント式の記述方法の詳細は、「割り当て式」 を参照してください。この実装は、jbpm アイデンティティーコンポーネントに依存していることに注意してください。
actor-id 属性 任意 actorId。pooled-actors と組み合わせて使用できます。actor-id は式として解決されます。したがって、actor-id="bobthebuilder" のように、固定の actorId を参照できます。または、actor-id="myVar.actorId" のように、文字列を返すプロパティーまたはメソッドを参照できます。これは、タスクインスタンス変数 "myVar" で getActorId メソッドを呼び出します。
pooled-actors 属性 任意 actorId のコンマ区切りリスト。actor-id と組み合わせて使用できます。プールされたアクターの固定セットは、pooled-actors="chicagobulls, pointersisters" のように指定できます。pooled-actors は式として解決されます。そのため、返すべきプロパティーまたはメソッド、String[]、コレクション、またはプールされたアクターのコンマ区切りリストを参照することもできます。
class 属性 任意 org.jbpm.taskmgmt.def.AssignmentHandler の実装の完全修飾クラス名。
config-type 属性 任意 {field|bean|constructor|configuration-property}。assignment-handler-object を構築する方法と、この要素の内容をその assignment-handler-object の設定情報として使用する方法を指定します。
{content} 任意 assignment-element の内容は、AssignmentHandler 実装の設定情報として使用できます。これにより、再利用可能な委譲クラスを作成できます。

14.4.27. Controller

表14.27 コントローラースキーマ

名前 タイプ 多重度 説明
class 属性 任意 org.jbpm.taskmgmt.def.TaskControllerHandler の実装の完全修飾クラス名。
config-type 属性 任意 {field|bean|constructor|configuration-property}。これは、assignment-handler-object を構築する方法と、この要素の内容をその assignment-handler-object の設定情報として使用する方法を指定します。
{content} これは、コントローラーの内容か、指定されたタスクコントローラーハンドラーの設定です (class 属性が指定されている場合。タスクコントローラーハンドラーが指定されていない場合、内容は variable 要素のリストである必要があります)。
variable 要素 [0..*] class 属性でタスクコントローラーハンドラーが指定されていない場合、controller 要素の内容は変数のリストである必要があります。

14.4.28. sub-process

表14.28 サブプロセススキーマ

名前 タイプ 多重度 説明
name 属性 required 呼び出すサブプロセスの名前。String として評価される必要がある EL 式を指定できます。
version 属性 任意 呼び出すサブプロセスのバージョン。version が指定されていない場合、process-state は特定のプロセスの最新バージョン使用します。
binding 属性 任意 サブプロセスが解決される瞬間を定義します。オプションは {early|late} です。デフォルトでは early、つまりデプロイメント時に解決されます。bindinglate として定義されている場合、process-state は、実行ごとに特定のプロセスの最新バージョンを解決します。late binding は、固定バージョンと組み合わせると意味がありません。したがって、version 属性は binding="late" の場合、無視されます。

14.4.29. condition

表14.29 条件スキーマ

名前 タイプ 多重度 説明
オプションは {content} です。後方互換性のために、expression 属性で条件を入力することもできますが、この属性はバージョン 3.2 以降非推奨となりました。 required condition 要素の内容は、ブール値として評価される必要がある jPDL 式です。決定は、(processdefinition.xml ファイルの順序に従って) 式が true に解決される最初の遷移を取得します。どの条件も true に解決されない場合、デフォルトの退出遷移 (最初の遷移) が使用されます。条件は、決定ノードから退出する遷移でのみ許可されます。

14.4.30. exception-handler

表14.30 例外ハンドラースキーマ

名前 タイプ 多重度 説明
exception-class 属性 任意 これは、この例外ハンドラーと一致する Java の "スロー可能" なクラスの完全修飾名を指定します。この属性が指定されていない場合、すべての例外に一致します (java.lang.Throwable)。
action 要素 [1..*] これは、この例外ハンドラーによってエラーが処理されているときに実行されるアクションのリストです。

第15章 ワークフローのテスト駆動開発

15.1. ワークフローのテスト駆動開発の概要

この章では、拡張機能なしで JUnit を使用してカスタムプロセス定義のユニットテストを実行する方法を説明します。
開発サイクルはできるだけ短期間にします。ソフトウェアソースコードへの変更は (可能であれば、中間のビルド手順なしで) すべて即座に検証します。 次の例は、このような方法で jBPM プロセスを開発およびテストする方法を示しています。
ほとんどのプロセス定義ユニットテストは実行ベースです。各シナリオは 1 つの JUnit テストメソッドで実行します。テストメソッドは、外部トリガー (シグナル) をプロセス実行に送信します。続いて各シグナルの後に検証を行い、プロセスが期待される状態にあることを確認します。
以下は、このようなテストのグラフィカルな表現の例です。ここでは、シンプルなオークションプロセスを使用します。

図15.1 オークションのテストプロセス

オークションのテストプロセス
次に、メインシナリオを実行するテストを記述します。
public class AuctionTest extends TestCase {

  // parse the process definition
  static ProcessDefinition auctionProcess = 
      ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par");

  // get the nodes for easy asserting
  static StartState start = auctionProcess.getStartState();
  static State auction = (State) auctionProcess.getNode("auction");
  static EndState end = (EndState) auctionProcess.getNode("end");

  // the process instance
  ProcessInstance processInstance;

  // the main path of execution
  Token token;

  public void setUp() {
    // create a new process instance for the given process definition
    processInstance = new ProcessInstance(auctionProcess);

    // the main path of execution is the root token
    token = processInstance.getRootToken();
  }
  
  public void testMainScenario() {
    // after process instance creation, the main path of 
    // execution is positioned in the start state.
    assertSame(start, token.getNode());
    
    token.signal();
    
    // after the signal, the main path of execution has 
    // moved to the auction state
    assertSame(auction, token.getNode());
    
    token.signal();
    
    // after the signal, the main path of execution has 
    // moved to the end state and the process has ended
    assertSame(end, token.getNode());
    assertTrue(processInstance.hasEnded());
  }
}

15.2. XML ソース

実行シナリオを記述する前に、ProcessDefinition を構成する必要があります。ProcessDefinition オブジェクトを取得する最も簡単な方法は、XML を解析することです。コード補完をオンにして、ProcessDefinition.parse を入力します。さまざまな解析方法が表示されます。ProcessDefinition オブジェクトに解析できる XML を記述するには、次の 3 つの方法があります。

15.2.1. プロセスアーカイブの解析

プロセスアーカイブ は、プロセスの XML ファイル、つまり processdefinition.xml を含む ZIP ファイルです。jBPM Process Designer プラグインは、プロセスアーカイブの読み取りと書き込みを行います。
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par");

15.2.2. XML ファイルの解析

processdefinition.xml ファイルを手動で記述するには、JpdlXmlReader を使用します。ant スクリプトを使用して、結果の ZIP ファイルをパッケージ化します。
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseXmlResource("org/jbpm/tdd/auction.xml");

15.2.3. XML 文字列の解析

プレーン文字列からインラインでユニットテストの XML を解析します。
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseXmlString(
  "<process-definition>" + 
  "  <start-state name='start'>" + 
  "    <transition to='auction'/>" + 
  "  </start-state>" + 
  "  <state name='auction'>" + 
  "    <transition to='end'/>" + 
  "  </state>" + 
  "  <end-state name='end'/>" + 
  "</process-definition>");

付録A GNU Lesser General Public License 2.1

                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library 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 library 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 library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!

付録B 更新履歴

改訂履歴
改訂 5.3.1-0.402Fri Oct 25 2013Rüdiger Landmann
Publican 4.0.0 用に再構築
改訂 5.3.1-0Thu Jan 10 2013Suzanne Dorfield
SOA 5.3.1 用に更新
改訂 5.3.0-0Thu Mar 29 2012Suzanne Dorfield
SOA 5.3 用に更新
改訂 5.2.0-0Wed Jun 29 2011David Le Sage
SOA 5.2 用に更新
改訂 5.1.0-0Fri Feb 18 2011David Le Sage
SOA 5.1 用に更新
改訂 5.0.2-0Wed May 26 2010David Le Sage
SOA 5.0.2 用に更新
改訂 5.0.1-0Tue Apr 20 2010David Le Sage
SOA 5.0.1 用に更新
改訂 5.0.0-0Sat Jan 30 2010David Le Sage
作成

法律上の通知

Copyright © 2013 Red Hat, Inc.
このドキュメントのテキストと図は、GNU Lesser General Public License (LGPL) バージョン 2.1 に基づいて Red Hat によってライセンスされています。このライセンスのコピーは、付録A GNU Lesser General Public License 2.1 にあります。
このマニュアルは、JBPM 3.2 ユーザーガイドから派生したものです。JBPM の詳細は、プロジェクトの Web サイト (http://www.jboss.org/jbpm) を参照してください。
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
All other trademarks are the property of their respective owners.