業務フローの書き方あれこれ

このコンテンツでは、jp.ossc.nimbus.service.beancontrol.BeanFlowInvokerAccessImpl2での、業務フローの書き方のあれこれを説明します。
業務フローXMLのスキーマ定義は、beanflow_1_0.dtdを参照してください。
以下に、このコンテンツの一覧を示します。

基本編


DTDの宣言

業務フローのXMLが、XMLスキーマレベルで、正しくかけているのかを検証するために、DTDの宣言を行います。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE flows PUBLIC
  3. "-//Nimbus//DTD Nimbus Bean Flow 1.0//JA"
  4. "http://nimbus.sourceforge.jp/dtd/beanflow_1_0.dtd">
  5. <flows>
  6. </flows>

業務フローの宣言

ルート要素であるflows要素の配下にflow要素を宣言する事で、業務フローが宣言できます。 業務フローは、一意な名前で宣言する必要があり、name属性で名前を指定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. </flow>
  4. </flows>
1つの業務フローXML内に、複数のフローが宣言できます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. </flow>
  4. <flow name="SampleFlow2">
  5. </flow>
  6. </flows>

ステップの宣言

業務フロー内の1処理単位をステップと呼びます。
ステップでは、処理の対象となるオブジェクトを指定し、そのオブジェクトに対する複数の操作を行い、結果を返します。
ステップの処理対象をtarget要素で指定します。staticメソッドを呼ぶだけのステップなど、対象のオブジェクトが存在しない場合は、指定する必要はありません。
ステップの結果をresult要素で指定します。結果がない場合は、指定する必要はありません。このステップの結果を別のステップから参照したい場合は、step要素のname属性で、ステップ名を宣言する事で、参照可能となります。
処理対象に対する操作は、attribute要素や、invoke要素など、様々な要素で、記述します。
1つの業務フローXML内に、複数のステップが宣言できます。また、暗黙的に最後のステップの結果は、業務フローの戻り値となります。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.lang.StringBuilder buf = new java.lang.StringBuilder();
  5. buf.append("これは");
  6. buf.append("テストです。");
  7. String message = buf.toString();
  8. -->
  9. <step name="message">
  10. <target><object code="java.lang.StringBuilder"/></target>
  11. <attribute name="append">これは</attribute>
  12. <attribute name="append">テストです。</attribute>
  13. <result><invoke name="toString">/</result>
  14. </step>
  15. </flow>
  16. </flows>

ステップの参照

ステップに名前を付ける事で、ステップの対象や結果を参照する事ができます。
step-ref要素の内容に、ステップ名を指定する事で、ステップの結果を参照できます。 また、ステップ名に続いて、"ステップ名.target"や"ステップ名.result"と指定する事で、明示的にステップの対象や結果を参照する事もできます。 また、それに続いて、プロパティ表現を指定する事で、ステップの対象や結果のプロパティを参照する事もできます。
プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。
ステップのresultなどで、ステップの対象自体を返したい場合は、this要素を指定する事で、自分自身のステップの対象を参照する事ができます。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.lang.StringBuilder buf = new java.lang.StringBuilder();
  5. buf.append("これは");
  6. buf.append("テストです。");
  7. String message = buf.toString();
  8. -->
  9. <step name="message">
  10. <target><object code="java.lang.StringBuilder"/></target>
  11. <attribute name="append">これは</attribute>
  12. <attribute name="append">テストです。</attribute>
  13. <result><invoke name="toString">/</result>
  14. </step>
  15. <!-- ステップの処理内容のイメージ
  16. System.out.println(message);
  17. System.out.println(message.length());
  18. -->
  19. <step>
  20. <target><static-field-ref code="java.lang.System" name="out"/></target>
  21. <invoke name="println"><argument><step-ref>message</step-ref></argument></invoke>
  22. <invoke name="println"><argument><step-ref>message.length</step-ref></argument></invoke>
  23. </step>
  24. <!-- ステップの処理内容のイメージ
  25. buf.setLength(0);
  26. -->
  27. <step>
  28. <target><step-ref>message.target<step-ref></target>
  29. <attribute name="Length">0</attribute>
  30. </step>
  31. </flow>
  32. </flows>

業務フローの戻り値

業務フローは、戻り値を返す事ができます。
戻り値を返すには、return要素を使用します。return要素の内容には、様々な要素や、即値を記述する事ができます。 また、return要素を記述しない場合は、暗黙的に最後のステップの結果が戻り値となります。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <return>終わり</return>
  4. </flow>
  5. </flows>

引数の参照

業務フローには、引数を渡す事ができます。
引数は、input要素で参照可能で、引数長が1の場合は、以下のようにinput要素を指定する事で参照します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <step>
  4. <target><input/></target>
  5. </step>
  6. </flow>
  7. </flows>
引数長が1より大きい場合は、引数は配列として渡ってきており、以下のようにinput要素の内容に配列インデックスを指定する事で参照します。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <step>
  4. <target><input>[0]</input></target>
  5. </step>
  6. </flow>
  7. </flows>
引数のオブジェクトのプロパティを参照したい場合は、以下のようにinput要素の内容にプロパティ表現を指定する事で参照します。
プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <step>
  4. <target><input>Name</input></target>
  5. </step>
  6. </flow>
  7. </flows>

引数の宣言

引数は、引数の参照で説明されているようにinput要素で参照可能ですが、引数の実体がどのようなものなのかが、曖昧です。引数が複数ある場合は、配列となり配列インデックスで参照するため、なおさら曖昧です。そこで、引数を明示的に宣言する事で、可読性を向上させる事が必要になります。
input-def要素を宣言し、name属性で引数に明示的に名前を付ける事で、引数の実体を明確にできます。名前宣言された引数を参照するには、input要素のname属性で指定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <input-def name="userId"/>
  4. <step>
  5. <target><input name="userId"/></target>
  6. </step>
  7. </flow>
  8. </flows>
引数が複数ある場合は、以下のように宣言します。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <input-def name="userId">[0]</input>
  4. <input-def name="password">[1]</input>
  5. <step>
  6. <target><input name="userId"/></target>
  7. </step>
  8. </flow>
  9. </flows>
引数のプロパティを引っ張り出して、宣言することもできます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <input-def name="userId">UserId</input>
  4. <input-def name="password">Password</input>
  5. <step>
  6. <target><input name="userId"/></target>
  7. </step>
  8. </flow>
  9. </flows>

オブジェクトの生成

object要素のcode属性で、生成するオブジェクトのクラス名を指定する事で、デフォルトコンストラクタ(引数なしのコンストラクタ)を使ってオブジェクトを生成します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.HashMap map = new java.util.HashMap();
  5. -->
  6. <step name="map">
  7. <result><object code="java.util.HashMap"/></result>
  8. </step>
  9. </flow>
  10. </flows>

フィールドの設定

field要素のname属性でフィールド名を指定する事で、オブジェクトのpublicなフィールドに値を設定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. public class User{
  5. public String name;
  6. public int age;
  7. }
  8. User user = new User();
  9. user.name="山田太郎";
  10. user.age=20;
  11. -->
  12. <step>
  13. <target><object code="sample.User"/></target>
  14. <field name="name">山田太郎</field>
  15. <field name="age">20</field>
  16. </step>
  17. </flow>
  18. </flows>
field要素の内容に記述された文字列を、フィールドの型に合わせて編集する処理は、プロパティ編集を参照して下さい。
フィールドの型と、設定する値の型が、代入可能ではあるが異なる場合は、field要素のtype属性で、設定する値の型を指定します。
フィールドにnullを設定する場合は、field要素のnullValue属性にtrueを指定します。

フィールドの取得

field要素のname属性でフィールド名を指定する事で、オブジェクトのpublicなフィールドの値を取得します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. public class User{
  5. public String name;
  6. public int age;
  7. }
  8. User user = new User();
  9. user.name="山田太郎";
  10. user.age=20;
  11. int age = user.age;
  12. -->
  13. <step name="age">
  14. <target><object code="sample.User"/></target>
  15. <field name="name">山田太郎</field>
  16. <field name="age">20</field>
  17. <result><field name="age"/></result>
  18. </step>
  19. </flow>
  20. </flows>

属性の設定

attribute要素のname属性で属性名を指定する事で、オブジェクトのpublicなsetterメソッド(setXxx(引数が1つ)のメソッド。Xxxの部分を属性名とみなします。)を呼び出して値を設定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. public class User{
  5. private String name;
  6. private int age;
  7. public void setName(String name){
  8. this.name = name;
  9. }
  10. public String getName(){
  11. return name;
  12. }
  13. public void setAge(int age){
  14. this.age = age;
  15. }
  16. public int getAge(){
  17. return age;
  18. }
  19. }
  20. User user = new User();
  21. user.setName("山田太郎");
  22. user.setAge(20);
  23. -->
  24. <step>
  25. <target><object code="sample.User"/></target>
  26. <attribute name="Name">山田太郎</attribute>
  27. <attribute name="Age">20</attribute>
  28. </step>
  29. </flow>
  30. </flows>
attribute要素の内容に記述された文字列を、属性の型に合わせて編集する処理は、プロパティ編集を参照して下さい。
属性の型と、設定する値の型が、代入可能ではあるが異なる場合は、attribute要素のtype属性で、設定する値の型を指定します。
属性にnullを設定する場合は、attribute要素のnullValue属性にtrueを指定します。
また、name属性には、属性名に続いて、プロパティ表現を指定できます。プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。

属性の取得

attribute要素のname属性で属性名を指定する事で、オブジェクトのpublicなgetterメソッド(voidでない戻り値 getXxx()のメソッド。Xxxの部分を属性名とみなします。)を呼び出して値を取得します。 また、name属性には、属性名に続いて、プロパティ表現を指定できます。プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. public class User{
  5. private String name;
  6. private int age;
  7. public void setName(String name){
  8. this.name = name;
  9. }
  10. public String getName(){
  11. return name;
  12. }
  13. public void setAge(int age){
  14. this.age = age;
  15. }
  16. public int getAge(){
  17. return age;
  18. }
  19. }
  20. User user = new User();
  21. user.setName("山田太郎");
  22. user.setAge(20);
  23. int age = user.getAge();
  24. -->
  25. <step name="age">
  26. <target><object code="sample.User"/></target>
  27. <attribute name="Name">山田太郎</attribute>
  28. <attribute name="Age">20</attribute>
  29. <result><attribute name="Age"/></result>
  30. </step>
  31. </flow>
  32. </flows>

メソッドの呼び出し

invoke要素の、name属性でメソッド名を指定し、配下にargument要素を引数の数だけ宣言します。argument要素には、type属性で引数の型を指定し、その内容に引数の値を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.io.PrintStream out = java.lang.System.out;
  5. out.println("テスト");
  6. -->
  7. <step>
  8. <target><static-field-ref code="java.lang.System" name="out"/></target>
  9. <invoke name="println"><argument type="java.lang.String">テスト</argument></invoke>
  10. </step>
  11. </flow>
  12. </flows>
argument要素の内容に記述された文字列を、引数の型に合わせて編集する処理は、プロパティ編集を参照して下さい。
引数にnullを渡す場合は、argument要素のnullValue属性にtrueを指定します。
argument要素のtype属性は、引数の型を特定するために、基本的には必須ですが、省略した場合は、argument要素に内容を指定した場合はjava.lang.String型、配下にobject型など型を表す要素を指定した場合は、その型として、暗黙的に推測します。

staticフィールドの参照

static-field要素の、code属性でクラス名を、name属性でstaticなフィールド名を指定して、staticフィールドの値を参照します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. int MAX_VALUE = java.lang.Integer.MAX_VALUE;
  5. -->
  6. <step name="MAX_VALUE">
  7. <result><static-field-ref code="java.lang.Integer" name="MAX_VALUE"/></result>
  8. </step>
  9. </flow>
  10. </flows>

staticメソッドの呼び出し

static-invoke要素の、code属性でクラス名を、name属性でstaticなメソッド名を指定し、配下にargument要素を引数の数だけ宣言します。argument要素には、type属性で引数の型を指定し、その内容に引数の値を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("[0-9]+");
  5. -->
  6. <step name="pattern">
  7. <result>
  8. <static-invoke code="java.util.regex.Pattern" name="compile">
  9. <argument type="java.lang.String">[0-9]+</argument>
  10. </static-invoke>
  11. </result>
  12. </step>
  13. </flow>
  14. </flows>
argument要素の内容に記述された文字列を、引数の型に合わせて編集する処理は、プロパティ編集を参照して下さい。
引数にnullを渡す場合は、argument要素のnullValue属性にtrueを指定します。
argument要素のtype属性は、引数の型を特定するために、基本的には必須ですが、省略した場合は、argument要素に内容を指定した場合はjava.lang.String型、配下にobject型など型を表す要素を指定した場合は、その型として、暗黙的に推測します。

リソース宣言によるトランザクション制御

コミットやロールバック、リソース開放といったトランザクション制御が可能なリソースを使用する場合、その制御を暗黙的に行ってくれます。
resource要素で宣言されたリソースは、業務フローが正常終了した時にはコミット、異常終了した時(例外発生時)にはロールバックが行われ、必ずリソースの解放が自動的に行われます。
コミット、ロールバックの制御を行うかどうかは、resource要素のtrancontrol属性で指定できます。デフォルトは、falseで自動制御されません。別途説明されているJTAによるトランザクション制御と、この機能によるトランザクション制御は、別の機能であるため、排他的に利用すべきです。
リソース解放の制御を行うかどうかは、resource要素のtranclose属性で指定できます。デフォルトは、trueで自動制御されます。
宣言されたリソースを参照するには、resource-ref要素を使用し、name属性でresource要素で宣言したリソース名を指定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <resource name="Connection" service="Nimbus#JDBCConnectionResourceFactory" trancontrol="true" tranclose="true"/>
  4. <step name="Statement">
  5. <target><resource-ref>Connection</resource-ref><target>
  6. <result><invoke name="createStatement"/><result>
  7. </step>
  8. </flow>
  9. </flows>

条件指定

条件に合致する場合のみ処理を実行したい場合、条件指定を使用します。
if要素は、test属性または、その配下にtest要素を記述して、そこに条件式を記述します。その条件に合致する場合のみ、配下のステップが実行されます。条件に合致する場合と合致しない場合の双方において処理が必要な場合は、条件分岐を使用します。
条件式の言語は、業務フローサービスの設定に依存しますが、デフォルトでは、Apache commons Jexlです。業務フローサービスの設定で、インタープリターを指定する事で、JavaやJavaScriptを条件式の言語として、利用する事もできます。
また、条件式の中で、変数名を@で囲む事で、変数のプロパティを取得するプロパティ表現を記述する事ができます。 プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。
条件式の中で参照可能な変数は、参照可能変数を参照。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. if(input >= 100) System.out.println("入力が100以上です");
  5. -->
  6. <if test="input ge 100">
  7. <step>
  8. <target><static-field-ref code="java.lang.System" name="out"/></target>
  9. <invoke name="println"><argument type="java.lang.String">入力が100以上です</argument></invoke>
  10. </step>
  11. </if>
  12. </flow>
  13. </flows>
test属性では、改行ができないため、複数条件などを改行して表現したい場合は、test要素を記述します。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. if(input >= 100 && input <= 200) System.out.println("入力が100以上かつ200以下です");
  5. -->
  6. <if>
  7. <test>
  8. input ge 100
  9. and input le 200
  10. </test>
  11. <step>
  12. <target><static-field-ref code="java.lang.System" name="out"/></target>
  13. <invoke name="println"><argument type="java.lang.String">入力が100以上かつ200以下です</argument></invoke>
  14. </step>
  15. </if>
  16. </flow>
  17. </flows>
また、XMLにはエスケープ対象文字があります。エスケープ文字を記述すると、本来の文字ではないため、可読性が下がります。条件式にエスケープ対象文字をエスケープせずに記述したい場合は、test要素内にXMLコメントで条件式を記述します。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. if(input >= 100 && input <= 200) System.out.println("入力が100以上かつ200以下です");
  5. -->
  6. <if>
  7. <test>
  8. <!--
  9. input >= 100
  10. and input <= 200
  11. -->
  12. </test>
  13. <step>
  14. <target><static-field-ref code="java.lang.System" name="out"/></target>
  15. <invoke name="println"><argument type="java.lang.String">入力が100以上かつ200以下です</argument></invoke>
  16. </step>
  17. </if>
  18. </flow>
  19. </flows>

条件分岐

複数の条件があり、それぞれの条件に合致する場合に行いたい処理がある場合に、条件分岐を使用します。
条件分岐は、switch要素を記述します。switch要素の配下に、各条件をcase要素で記述し、その配下に処理すべきステップを記述します。全ての条件に合致しないという条件は、default要素で記述し、その配下に処理すべきステップを記述します。
case要素には、test属性または、その配下にtest要素を記述して、そこに条件式を記述します。その条件に合致する場合のみ、配下のステップが実行されます。Javaのswitchのように、breakしないと次のcaseに処理が進むような機能はありません。Javaのif-elseif-elseに相当する機能です。
条件式の言語は、業務フローサービスの設定に依存しますが、デフォルトでは、Apache commons Jexlです。業務フローサービスの設定で、インタープリターを指定する事で、JavaやJavaScriptを条件式の言語として、利用する事もできます。
条件式の中で参照可能な変数は、参照可能変数を参照。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. if(input < 100){
  5. System.out.println("入力が100より小さいです");
  6. }else if(input >= 100 && input < 200){
  7. System.out.println("入力が100以上かつ200より小さいです");
  8. }else{
  9. System.out.println("入力が200以上です");
  10. }
  11. -->
  12. <switch>
  13. <case test="input lt 100">
  14. <step>
  15. <target><static-field-ref code="java.lang.System" name="out"/></target>
  16. <invoke name="println"><argument type="java.lang.String">入力が100より小さいです</argument></invoke>
  17. </step>
  18. </case>
  19. <case test="input ge 100 and input lt 200">
  20. <step>
  21. <target><static-field-ref code="java.lang.System" name="out"/></target>
  22. <invoke name="println"><argument type="java.lang.String">入力が100以上かつ200より小さいです</argument></invoke>
  23. </step>
  24. </case>
  25. <default>
  26. <step>
  27. <target><static-field-ref code="java.lang.System" name="out"/></target>
  28. <invoke name="println"><argument type="java.lang.String">入力が200以上です</argument></invoke>
  29. </step>
  30. </default>
  31. </switch>
  32. </flow>
  33. </flows>
test属性は、test要素で代替できます。詳細は、条件指定を参照してください。

繰り返し

一定回数または繰り返し可能なオブジェクト(配列やリストなど)に対して繰り返し処理を行いたい場合は、for要素を記述し、その配下に繰り返す処理のステップを記述します。
一定回数の繰り返しを行う場合は、for要素のbegin属性とend属性を指定して、繰り返し処理インデックスの開始インデックスと終了インデックスを指定します。繰り返し処理インデックスは、beginから始まり、1ずつ増えていき、end - 1まで繰り返されます。
begin属性とend属性には、数値を返す式を記述する事ができます。記述できる式は、の仕様に準じます。
また、繰り返し処理インデックスの値を参照したい場合は、index属性で繰り返し処理インデックスの変数名を定義し、var要素で参照します。
何らかの条件を満たした場合に、繰り返し処理を途中で抜ける場合にはbreak要素やreturn要素、後続の処理を行わずに次の繰り返し処理インデックスに進みたい場合にはcontinue要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. for(int i = 0; i < 3; i++){
  5. System.out.print("繰り返しインデックスは、");
  6. System.out.print(i);
  7. System.out.println("です。");
  8. }
  9. -->
  10. <for index="i" begin="0" end="3">
  11. <step>
  12. <target><static-field-ref code="java.lang.System" name="out"/></target>
  13. <invoke name="print"><argument type="java.lang.String">繰り返しインデックスは、</argument></invoke>
  14. <invoke name="print"><argument type="int"><var>i</var></argument></invoke>
  15. <invoke name="println"><argument type="java.lang.String">です。</argument></invoke>
  16. </step>
  17. </for>
  18. </flow>
  19. </flows>
繰り返し可能なオブジェクトの繰り返しを行う場合は、for要素の配下に、target要素を指定して、繰り返し可能なオブジェクトを指定します。繰り返し可能オブジェクトの要素を参照する場合は、var属性で変数名を指定して、var要素で参照します。begin属性とend属性を指定して、繰り返し可能なオブジェクトの一部のみを繰り返す事もできます。
繰り返し可能オブジェクトは、以下です。


  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.List list = (java.util.List)input;
  5. for(int i = 0; i < list.size(); i++){
  6. Object element = list.get(i);
  7. System.out.print("繰り返し要素[");
  8. System.out.print(i);
  9. System.out.print("]は、");
  10. System.out.print(element);
  11. System.out.println("です。");
  12. }
  13. -->
  14. <for var="element" index="i">
  15. <target><input/></target>
  16. <step>
  17. <target><static-field-ref code="java.lang.System" name="out"/></target>
  18. <invoke name="print"><argument type="java.lang.String">繰り返し要素[</argument></invoke>
  19. <invoke name="print"><argument type="int"><var>i</var></argument></invoke>
  20. <invoke name="print"><argument type="java.lang.String">]は、</argument></invoke>
  21. <invoke name="print"><argument type="java.lang.Object"><var>element</var></argument></invoke>
  22. <invoke name="println"><argument type="java.lang.String">です。</argument></invoke>
  23. </step>
  24. </for>
  25. </flow>
  26. </flows>
繰り返し処理は、回数が多い場合、ジャーナルの出力量が多くなり、システムに負荷を与える場合があるため、注意が必要です。ジャーナルの出力については、ジャーナル出力を参照。

条件指定繰り返し

条件を満たす間、繰り返し処理を行いたい場合は、while要素を記述し、その配下に繰り返す処理のステップを記述します。
while要素のtest属性に条件式を記述します。また、条件を評価してから繰り返し処理を行うか、繰り返し処理を行ってから条件を評価するかを、do属性で記述します。do属性は、デフォルトではfalseで、条件を評価してから繰り返し処理を行います。
条件式の言語は、業務フローサービスの設定に依存しますが、デフォルトでは、Apache commons Jexlです。業務フローサービスの設定で、インタープリターを指定する事で、JavaやJavaScriptを条件式の言語として、利用する事もできます。
条件式の中で参照可能な変数は、参照可能変数を参照。
何らかの条件を満たした場合に、繰り返し処理を途中で抜ける場合にはbreak要素やreturn要素、後続の処理を行わずに次の繰り返し処理に進みたい場合にはcontinue要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- フローの処理内容のイメージ
  4. java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
  5. byte[] bytes = new byte[1024];
  6. int length = ((java.io.InputStream)input).read(bytes);
  7. while(length > 0){
  8. baos.write(bytes, 0, length);
  9. length = ((java.io.InputStream)input).read(bytes);
  10. }
  11. return baos.toByteArray();
  12. -->
  13. <step name="baos">
  14. <result><object code="java.io.ByteArrayOutputStream"/></result>
  15. </step>
  16. <step name="bytes">
  17. <result><static-invoke code="java.lang.reflect.Array" name="newInstance"><argument type="java.lang.Class">byte</argument><argument type="int">1024</argument></static-invoke></result>
  18. </step>
  19. <step name="length">
  20. <target><input/></target>
  21. <result><invoke name="read"><argument type="byte[]"><step-ref>bytes</step-ref></argument></invoke></result>
  22. </step>
  23. <while test="length gt 0">
  24. <step>
  25. <target><step-ref>baos</step-ref></target>
  26. <invoke name="write">
  27. <argument type="byte[]"><step-ref>bytes</step-ref></argument>
  28. <argument type="int">0</argument>
  29. <argument type="int"><step-ref>length</step-ref></argument>
  30. </invoke>
  31. </step>
  32. <step name="length">
  33. <target><input/></target>
  34. <result><invoke name="read"><argument type="byte[]"><step-ref>bytes</step-ref></argument></invoke></result>
  35. </step>
  36. </while>
  37. <return><step-ref>baos.toByteArray</step-ref></return>
  38. </flow>
  39. </flows>
test属性は、test要素で代替できます。詳細は、条件指定を参照してください。

例外処理

基本的にアプリケーションの業務的な意味合いでのエラーは、例外で処理すべきではありません。但し、業務処理の共通化などで、小さな粒度の業務フローを設計した場合、呼び出し階層が深くなってしまう場合があります。そのような場合は、戻り値で業務エラーを伝搬するのは非生産的なロジックを生むため、業務的な意味合いでのエラーを例外処理で賄う場合があり、アプリケーション層である業務フローで、例外処理が必要になる場合があります。
業務フローにおける例外処理は、flow単位、及びstep単位で可能です。flow要素および、step要素の配下にcatch要素を記述し、その配下に必要なステップを記述します。
catch要素には、exception属性で、処理する例外のクラス名を指定します。また、var属性で、補足した例外の変数名を定義して、var要素で参照できます。 また、catch要素の配下では、throw要素を記述する事で、補足した例外や、新たに生成した例外をthrowする事ができます。
ステップ単位の例外処理は、

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. try{
  5. input.execute();
  6. }catch(java.lang.Exception e){
  7. e.printStackTrace();
  8. throw e;
  9. }
  10. -->
  11. <step>
  12. <target><input/></target>
  13. <invoke name="execute"/>
  14. <catch exception="java.lang.Exception" var="e">
  15. <step>
  16. <target><var>e</var></target>
  17. <invoke name="printStackTrace"/>
  18. </step>
  19. <throw var="e"/>
  20. </catch>
  21. </step>
  22. </flow>
  23. </flows>
フロー単位の例外処理は、
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- フローの処理内容のイメージ
  4. try{
  5. input.execute();
  6. return input.getResult();
  7. }catch(java.lang.Exception e){
  8. e.printStackTrace();
  9. throw e;
  10. }
  11. -->
  12. <step>
  13. <target><input/></target>
  14. <invoke name="execute"/>
  15. </step>
  16. <step>
  17. <target><input/></target>
  18. <result><attribute name="Result"/></result>
  19. </step>
  20. <catch exception="java.lang.Exception" var="e">
  21. <step>
  22. <target><var>e</var></target>
  23. <invoke name="printStackTrace"/>
  24. </step>
  25. <throw var="e"/>
  26. </catch>
  27. </flow>
  28. </flows>
業務フローでは、任意の区間の処理をtry節で括って、例外処理を記述する機能はありません。 任意の区間の例外処理を行いたい場合は、その区間そのものを、一つのフローにして、フロー単位の例外処理を行う必要があります。

終了処理

リソースの開放が必要なオブジェクトなどを扱う際、例外が発生しても必ずリソースの解放を行いたい場合があります。そのような処理を終了処理と呼びます。
業務フローにおける終了処理は、flow単位、及びstep単位で可能です。flow要素および、step要素の配下にfinally要素を記述し、その配下に必要なステップを記述します。
ステップ単位の終了処理は、

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. try{
  5. input.execute();
  6. }finally{
  7. input.close();
  8. }
  9. -->
  10. <step>
  11. <target><input/></target>
  12. <invoke name="execute"/>
  13. <finally>
  14. <step>
  15. <target><input/></target>
  16. <invoke name="close"/>
  17. </step>
  18. </finally>
  19. </step>
  20. </flow>
  21. </flows>
フロー単位の終了処理は、
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- フローの処理内容のイメージ
  4. try{
  5. input.execute();
  6. return input.getResult();
  7. }finally{
  8. input.close();
  9. }
  10. -->
  11. <step>
  12. <target><input/></target>
  13. <invoke name="execute"/>
  14. </step>
  15. <step>
  16. <target><input/></target>
  17. <result><attribute name="Result"/></result>
  18. </step>
  19. <finally>
  20. <step>
  21. <target><input/></target>
  22. <invoke name="close"/>
  23. </step>
  24. </finally>
  25. </flow>
  26. </flows>
業務フローでは、任意の区間の処理をtry節で括って、終了処理を記述する機能はありません。 任意の区間の終了処理を行いたい場合は、その区間そのものを、一つのフローにして、フロー単位の終了処理を行う必要があります。

一行で済むような簡易な計算処理などを記述する場合に、ステップを組むのは煩雑です。
そのような何らかの戻り値を持ち、一行で実行できる計算処理などを行うのが、expression要素で、その内容に戻り値を持つ式を記述します。 式の言語は、業務フローサービスの設定に依存しますが、デフォルトでは、Apache commons Jexlです。業務フローサービスの設定で、インタープリターを指定する事で、JavaやJavaScriptを条件式の言語として、利用する事もできます。
また、条件式の中で、変数名を@で囲む事で、変数のプロパティを取得するプロパティ表現を記述する事ができます。 プロパティ表現の表現方法は、プロパティファクトリを参照して下さい。
式の中で参照可能な変数は、参照可能変数を参照。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. input.execute(input.getValue() * 100);
  5. -->
  6. <step>
  7. <target><input/></target>
  8. <invoke name="execute">
  9. <argument type="int"><expression>@input.value@ * 100</expression></argument>
  10. </invoke>
  11. </step>
  12. </flow>
  13. </flows>

フロー呼び出し

業務フローを共通化して、開発の生産性や保守性を向上したい場合に、業務フローから業務フローを呼び出す必要性があります。また、トランザクション境界や、例外処理の単位など、業務フローの単位を分割する必要性もあります。
callflow要素のname属性で、呼び出すフロー名を指定し、その配下にargument要素を記述する事で引数を渡します。
フローの戻り値を参照する場合、name属性の値で、ステップ参照可能です。呼び出しフロー名とステップ名を別にしたい場合は、stepname属性で、ステップ名を指定できます。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow name="SampleFlow2" stepname="result">
  4. <argument><input/></argument>
  5. <argument type="int">100</argument>
  6. </callflow>
  7. <step>
  8. <target><static-field-ref code="java.lang.System" name="out"/></target>
  9. <invoke name="println"><argument type="java.lang.Object"><step-ref>result</step-ref></argument></invoke>
  10. </step>
  11. </flow>
  12. <flow name="SampleFlow2">
  13. <input-def name="bean">[0]</input-def>
  14. <input-def name="num">[1]</input-def>
  15. <step>
  16. <target><input name="bean"/></target>
  17. <result>
  18. <invoke name="execute">
  19. <argument type="int"><input name="num"/></argument>
  20. </invoke>
  21. <result>
  22. </step>
  23. </flow>
  24. </flows>

参照可能変数

変数名
input業務フローの入力
thisステップの対象。但し、ステップ内に限る
varvar属性等で指定された任意の変数。var(変数名)で参照
inputDefinput-def属性等で指定された任意の変数。inputDef(変数名)で参照
ステップ名ステップの結果



応用編


環境変数の参照

業務フローで、サービス定義のserver-property、manager-property、Javaのシステムプロパティ、スレッドコンテキストサービスのコンテキスト値を、${プロパティ名}で参照できます。

コンストラクタを使ったオブジェクトの生成

デフォルトコンストラクタ(引数なしのコンストラクタ)以外の引数が存在するコンストラクタを使って、オブジェクトを生成したい場合は、object要素の配下にconstructor要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.HashMap map = new java.util.HashMap(100, 0.9f);
  5. -->
  6. <step name="map">
  7. <result>
  8. <object code="java.util.HashMap">
  9. <constructor>
  10. <argument type="int">100</argument>
  11. <argument type="float">0.9</argument>
  12. </constructor>
  13. </object>
  14. </result>
  15. </step>
  16. </flow>
  17. </flows>
argument要素の内容に記述された文字列を、引数の型に合わせて編集する処理は、プロパティ編集を参照して下さい。
引数にnullを渡す場合は、argument要素のnullValue属性にtrueを指定します。
argument要素のtype属性は、引数の型を特定するために、基本的には必須ですが、省略した場合は、argument要素に内容を指定した場合はjava.lang.String型、配下にobject型など型を表す要素を指定した場合は、その型として、暗黙的に推測します。

配列オブジェクトの生成

配列オブジェクトを生成したい場合は、object要素の配下のconstructor要素の配下に、argument要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. int[] intArray = new int[]{1, 2, 3};
  5. -->
  6. <step name="intArray">
  7. <result>
  8. <object code="int[]">
  9. <constructor>
  10. <argument type="int">1</argument>
  11. <argument type="int">2</argument>
  12. <argument type="int">3</argument>
  13. </constructor>
  14. </object>
  15. </result>
  16. </step>
  17. </flow>
  18. </flows>
空の配列オブジェクトや多次元配列オブジェクトを生成したい場合などは、java.util.ArrayクラスのnewInstanceメソッドを利用します。staticメソッド呼び出しによるオブジェクトの生成を参照。

staticフィールド参照によるオブジェクトの生成

生成したいオブジェクトが、staticフィールドを参照する事で手に入る場合は、object要素の配下のconstructor要素の配下に、staticなフィールドを参照するstatic-field-ref要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. double nan = java.lang.Double.NaN;
  5. -->
  6. <step name="nan">
  7. <result>
  8. <object code="double">
  9. <constructor><static-field-ref code="java.lang.Double" name="NaN"/></constructor>
  10. </object>
  11. </result>
  12. </step>
  13. </flow>
  14. </flows>
また、target要素や、result要素など、その配下に、static-field-ref要素を直接記述できる要素もあります。その場合は、上記は、以下のように簡略に書くことができます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. double nan = java.lang.Double.NaN;
  5. -->
  6. <step name="nan">
  7. <result><static-field-ref code="java.lang.Double" name="NaN"/></result>
  8. </step>
  9. </flow>
  10. </flows>

staticメソッド呼び出しによるオブジェクトの生成

生成したいオブジェクトが、staticメソッドの呼び出しで手に入る場合は、object要素の配下のconstructor要素の配下に、staticなメソッドを呼び出すstatic-invoke要素を記述します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.Calendar cal = java.util.Calendar.getInstance();
  5. -->
  6. <step name="cal">
  7. <result>
  8. <object code="java.util.Calendar">
  9. <constructor><static-invoke code="java.util.Calendar" name="getInstance"/></constructor>
  10. </object>
  11. </result>
  12. </step>
  13. </flow>
  14. </flows>
また、target要素や、result要素など、その配下に、static-invoke要素を直接記述できる要素もあります。その場合は、上記は、以下のように簡略に書くことができます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <!-- ステップの処理内容のイメージ
  4. java.util.Calendar cal = java.util.Calendar.getInstance();
  5. -->
  6. <step name="cal">
  7. <result><static-invoke code="java.util.Calendar" name="getInstance"/></result>
  8. </step>
  9. </flow>
  10. </flows>

業務フローに別名をつける

業務フローは、一意な名前を持ちますが、その一意な名前とは別に、代替となる名前を別名として宣言することができます。
flow要素の配下に、alias要素を宣言し、そのname属性に別名を指定します。alias要素は、複数宣言する事が可能です。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <alias name="Flow1"/>
  4. </flow>
  5. </flows>

ジャーナル出力

業務フローのサービスにジャーナルサービスが設定されている場合、自動的にジャーナルが出力されます。
業務フローのジャーナルは、開発時のデバッグ用途や、障害時のトレース情報として非常に有用ですが、その出力量がシステムのリソース(CPUやメモリ、ディスク等)を圧迫する場合があります。 そのため、ジャーナルの出力量を必要最低限に制御する必要がある場合があります。
業務フローのほとんどの要素には、ジャーナルの出力を制御するために、journal属性があります。デフォルトは、trueになっているため、ジャーナルの出力を抑止する場合は、明示的にfalseに指定する必要があります。XMLの構造的に上位の要素に、journal="false"を指定すると、それより下位の要素の全てのジャーナル出力が抑止されます。

  1. <flows>
  2. <flow name="SampleFlow1" journal="false">
  3. </flow>
  4. </flows>
また、for、while要素などの繰り返し処理では、繰り返し回数が多いと、大量のジャーナルが出力されてしまう場合があります。しかし、単純にjournal="false"にしてしまうと、障害時などのトレース情報が不足してしまいます。
そのような場合は、journalOnlyLast属性にtrueと指定する事で、最後のループのみジャーナルを出力する事ができます。これにより、障害時のトレース情報としては、十分な情報を確保しつつ、ジャーナルの出力量を抑止する事ができます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <for index="i" begin="0" end="1000" journalOnlyLast="true">
  4. <step>
  5. <target><static-field-ref code="java.lang.System" name="out"/></target>
  6. <invoke name="print"><argument type="java.lang.String">繰り返しインデックスは、</argument></invoke>
  7. <invoke name="print"><argument type="int"><var>i</var></argument></invoke>
  8. <invoke name="println"><argument type="java.lang.String">です。</argument></invoke>
  9. </step>
  10. </for>
  11. </flow>
  12. </flows>

流量制御

flow要素のmaxRunThreads属性に、同時実行数の上限を指定することで、フロー単位で、同時実行数を制御できます。

  1. <flows>
  2. <flow name="SampleFlow1"
  3. maxRunThreads="3">
  4. </flow>
  5. </flows>
同時実行数が限界まで来ている時に、同時実行数に空きが出来るまで待機させられます。その際に、一定時間待っても空きがでない場合は、諦めたい場合があります。
flow要素のtimeout属性を指定する事で、 指定された時間[ms]だけ待機して諦めた時は、UnavailableFlowExceptionをthrowします。この属性を定義しない場合は、無限に待ち続けます。
  1. <flows>
  2. <flow name="SampleFlow1"
  3. maxRunThreads="3"
  4. timeout="10000">
  5. </flow>
  6. </flows>
同時実行数が限界まで来ている時に、同時実行数に空きが出来るまで待機させられます。その際に、待機している数が多い場合は、長い時間待たされる可能性があります。そのため、その待機数が多い場合は、待たずに諦めたいことがあります。
flow要素のmaxWaitThreads属性を指定する事で、その待機数の最大値を制御できます。待機せずに諦めた時は、UnavailableFlowExceptionをthrowします。この属性を定義しない場合は、同時実行待ち数の制限はありません。
  1. <flows>
  2. <flow name="SampleFlow1"
  3. maxRunThreads="3"
  4. maxWaitThreads="10">
  5. </flow>
  6. </flows>
同時実行数制御を行っている場合で、業務フローの処理が何らかの問題で、ハングしてしまい応答を返さない状態になると、その処理によって同時実行数の空きが食いつぶされてしまいます。そのような場合、同時実行数の空きを強制的に回復させたいことがあります。
flow要素のforceFreeTimeout属性を指定する事で、実行時間がこの時間[ms]を過ぎた場合には、そのスレッドが確保している同時実行数を開放することができます。
  1. <flows>
  2. <flow name="SampleFlow1"
  3. maxRunThreads="3"
  4. forceFreeTimeout="60000">
  5. </flow>
  6. </flows>
また、これらの流量制御は、step要素の属性でも指定でき、ステップ単位での流量制御も可能です。

JTAによるトランザクション制御

JavaEEサーバなど、JTA(Java Transaction Architecture)を持つJavaプロセス上で、業務フローを実行する場合、JTAによるトランザクション制御を利用することができます。
制御可能なトランザクションの種類は、以下になります。

トランザクション種別説明
Required既存トランザクションがなければ新しく開始する。あれば何もしない。
RequiresNew既存トランザクションがなければ新しく開始する。あれば中断して、新しく開始する。
Supports既存トランザクションがあっても、なくても何もしない。
Mandatory既存トランザクションがなければエラー。あれば何もしない。
Never既存トランザクションがなければ何もしない。あればエラー。
NotSupported既存トランザクションがなければ何もしない。あれば停止する。

業務フロー単位での、宣言的なトランザクション制御を行う方法。

  1. <flows>
  2. <flow name="SampleFlow1"
  3. transaction="Required">
  4. </flow>
  5. </flows>

業務フローを呼び出す側での、呼び出し時トランザクション制御を行う方法。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow name="SampleFlow2" transaction="Required"/>
  4. </flow>
  5. <flow name="SampleFlow2">
  6. </flow>
  7. </flows>
宣言的なトランザクション制御と、呼び出し時トランザクション制御の両方が指定されている場合は、呼び出し時トランザクション制御の方が優先されます。

また、JTAのトランザクション制御を利用する場合は、トランザクションタイムアウトを適用する事が可能です。
flow要素およびcallflow要素のtrantimeout属性に、トランザクションタイムアウト[s]を指定します。
  1. <flows>
  2. <flow name="SampleFlow1"
  3. transaction="Required"
  4. trantimeout="30">
  5. </flow>
  6. </flows>

非同期フロー呼び出し

業務フローを非同期で処理することができます。実現できる非同期要求に、3パターンあります。

  • 非同期要求を行い、応答は待たない。
  • 非同期要求を行い、応答を待つ。
  • 非同期要求を行い、応答は待たないが、応答をコールバックさせる。


非同期要求を行い、応答は待たない場合、callflow要素のasynch属性にtrueを指定します。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow name="SampleFlow2" asynch="true">
  4. <argument><input/></argument>
  5. <argument type="int">100</argument>
  6. </callflow>
  7. </flow>
  8. <flow name="SampleFlow2">
  9. <input-def name="bean">[0]</input-def>
  10. <input-def name="num">[1]</input-def>
  11. <step>
  12. <target><input name="bean"/></target>
  13. <result>
  14. <invoke name="execute">
  15. <argument type="int"><input name="num"/></argument>
  16. </invoke>
  17. <result>
  18. </step>
  19. </flow>
  20. </flows>
非同期要求を行い、応答を待つ場合は、callflow要素のasynch属性とreply属性にtrueを指定して、非同期要求を行います。
その後、応答を待ちたい場所で、reply要素を指定し、name属性で応答待ちしたいcallflow要素のステップ名を指定します。name属性の値が、応答待ちしたいcallflow要素の名前であり、reply要素自体のステップ名になりますが、reply要素のステップ名を別にしたい場合には、stepname属性で指定できます。また、応答を待つ際に、timeout属性で最大待ち時間[ms]を指定できます。timeout属性で指定した時間が経過しても、応答が得られない場合は、BeanFlowAsynchTimeoutExceptionがthrowされます。さらに、タイムアウトした場合に、非同期処理を取り消す場合は、cancel属性をtrueに指定します。但し、非同期処理の取り消しが必ずしも間に合うとは限りません。非同期処理で例外が発生していた場合は、reply要素を実行した際に、例外がthrowされます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow stepname="call1" name="SampleFlow2" asynch="true" reply="true">
  4. <argument><input/></argument>
  5. <argument type="int">100</argument>
  6. </callflow>
  7. <callflow stepname="call2" name="SampleFlow2" asynch="true" reply="true">
  8. <argument><input/></argument>
  9. <argument type="int">200</argument>
  10. </callflow>
  11. <reply name="call1"/>
  12. <step>
  13. <target><static-field-ref code="java.lang.System" name="out"/></target>
  14. <invoke name="println"><argument><step-ref>call1</step-ref></argument></invoke>
  15. </step>
  16. <reply name="call2"/>
  17. <step>
  18. <target><static-field-ref code="java.lang.System" name="out"/></target>
  19. <invoke name="println"><argument><step-ref>call2</step-ref></argument></invoke>
  20. </step>
  21. </flow>
  22. <flow name="SampleFlow2">
  23. <input-def name="bean">[0]</input-def>
  24. <input-def name="num">[1]</input-def>
  25. <step>
  26. <target><input name="bean"/></target>
  27. <result>
  28. <invoke name="execute">
  29. <argument type="int"><input name="num"/></argument>
  30. </invoke>
  31. <result>
  32. </step>
  33. </flow>
  34. </flows>
非同期要求を行い、応答は待たないが、応答をコールバックさせる場合は、callflow要素のasynch属性にtrueを指定し、その配下にcallback要素を記述します。callback要素には、name属性でコールバックする業務フロー名を指定します。
コールバックに、パラメータを渡したい場合は、callback要素の配下に、attribute要素を指定し、name属性でパラメータ名を、内容に値を指定します。コールバックされた業務フローには、引数としてAsynchCallbackContextが、渡されます。非同期処理で例外が発生していたかどうかは、AsynchCallbackContext#checkError()メソッドを呼び出す事で確認でき、例外が発生していた場合は、例外がthrowされます。また、例外をthrowせずに例外が発生していたか知りたい場合は、AsynchCallbackContext#getThrowable()メソッドの戻り値がnullでないかどうかで調べられます。非同期処理の戻り値を取得する場合は、AsynchCallbackContext#getOutput()メソッドで取得できます。callback要素のattribute要素で指定したパラメータは、AsynchCallbackContext#getContext(パラメータ名)メソッドで取得できます。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow name="SampleFlow2" asynch="true">
  4. <argument><input/></argument>
  5. <argument type="int">100</argument>
  6. <callback name="CallbackFlow">
  7. <attribute name="param1">テスト</attribute>
  8. </callback>
  9. </callflow>
  10. </flow>
  11. <flow name="SampleFlow2">
  12. <input-def name="bean">[0]</input-def>
  13. <input-def name="num">[1]</input-def>
  14. <step>
  15. <target><input name="bean"/></target>
  16. <result>
  17. <invoke name="execute">
  18. <argument type="int"><input name="num"/></argument>
  19. </invoke>
  20. <result>
  21. </step>
  22. </flow>
  23. <flow name="CallbackFlow">
  24. <step>
  25. <target><input/></target>
  26. <invoke name="checkError"/>
  27. </step>
  28. <step>
  29. <target><static-field-ref code="java.lang.System" name="out"/></target>
  30. <invoke name="print"><argument>戻り値:</argument></invoke>
  31. <invoke name="println"><argument><input>Output</input></argument></invoke>
  32. <invoke name="print"><argument>パラメータ:</argument></invoke>
  33. <invoke name="println"><argument><input>Context(param1)</input></argument></invoke>
  34. </step>
  35. </flow>
  36. </flows>

インタープリタを使ったスクリプト実行

業務フローは、Javaで表現できる処理は、ほぼ記述できますが、XMLで記述するため、基本的に冗長です。複雑で長大なアプリケーションロジックを表現するには、適切な言語とは言えません。
そのような場合、Javaでアプリケーションを開発し、業務フローから呼び出す事で、問題を解決できます。しかし、Javaソースファイルと業務フローのXMLにアプリケーションが分離してしまうため、可読性や保守性の観点では、懸念もあります。
そこで、業務フロー内に、スクリプト言語でアプリケーションロジックを記述できるようにしたのが、interpreter要素です。interpreter要素の内容にスクリプトを記述できます。また、スクリプト内には、XMLエスケープ文字を使用する場合もあるため、XMLコメントアウト内に、スクリプトを記述できます。 スクリプト言語は、業務フローサービスの設定に依存し、インタープリターを指定する事で、JavaやJavaScriptを言語として、利用する事ができます。
スクリプト内では、参照可能変数に加えて、以下の変数が参照可能です。

変数名
targetステップの対象
resourceresource宣言したリソースにアクセスするためのResourceManager
journalジャーナルを出力するためのジャーナルサービス
loggerログを出力するためのログサービス
beanFlowInvokerFactory他の業務フローを呼び出すための業務フローサービス

スクリプトの戻り値(戻り値の戻し方は、スクリプト言語に依存する)は、interpreter要素を内包するステップの結果として参照可能です。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <step>
  4. <interpreter>
  5. <!--
  6. for(var item in input){
  7. java.lang.System.out.println(item);
  8. }
  9. -->
  10. </interpreter>
  11. </step>
  12. </flow>
  13. </flows>

テンプレートエンジンを使った文字列編集

Javaで文字列編集を行う場合、通常、java.lang.StringBuilderを使用します。しかし、それを使った文字列編集のロジックから、編集後の文字列を想像するのは、可読性が高いとは言い難いものがあります。
そこで、template要素の内容にテンプレートエンジンサービスを使った文字列編集ロジックを記述することで、文字列編集の生産性、可読性を上げる事ができます。また、文字列編集ロジック内には、XMLエスケープ文字を使用する場合もあるため、XMLコメントアウト内に、文字列編集ロジックを記述できます。 文字列編集ロジック内では、参照可能変数に加えて、以下の変数が参照可能です。

変数名
targetステップの対象

文字列編集結果は、template要素を内包するステップの結果として参照可能です。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <step name="message">
  4. <template>
  5. <!--
  6. #for($item in $input)
  7. item
  8. #end
  9. -->
  10. </template>
  11. </step>
  12. <step>
  13. <target><static-field-ref code="java.lang.System" name="out"/></target>
  14. <invoke name="println"><argument><step-ref>message</step-ref></argument></invoke>
  15. </step>
  16. </flow>
  17. </flows>

業務フローのオーバーライド

状況に応じて業務フローを上書きたい場合に、業務フローのオーバーライドによって上書きすることができます。
オーバーライドする方法は、2つあり、フローの宣言で行う方法と、フロー呼び出し時に行う方法があります。 フローの宣言で行う方法は、flow要素の配下に、override要素を宣言し、name属性にオーバーライドするフロー名を指定します。オーバーライドするフロー名には、必ずスレッドコンテキストのキー名を参照する環境変数参照が必要です。オーバーライドされる可能性のある業務フローを呼び出す際には、事前にスレッドコンテキストに、そのキーを設定しておく必要があります。環境変数を参照した上で決まったオーバーライド名の業務フローが存在する場合には、呼び出したフローではなく、オーバライドしたフローが呼び出されます。

  1. <flows>
  2. <flow name="SampleFlow1">
  3. <override name="${userType}/SampleFlow1"/>
  4. <step>
  5. <target><static-field-ref code="java.lang.System" name="out"/></target>
  6. <invoke name="println"><argument>一般ユーザです</argument></invoke>
  7. </step>
  8. </flow>
  9. <flow name="admin/SampleFlow1">
  10. <step>
  11. <target><static-field-ref code="java.lang.System" name="out"/></target>
  12. <invoke name="println"><argument>管理者です</argument></invoke>
  13. </step>
  14. </flow>
  15. </flows>
フロー呼び出し時に行う方法は、callflow要素の配下に、override要素を宣言し、name属性にオーバーライドするフロー名を指定します。
  1. <flows>
  2. <flow name="SampleFlow1">
  3. <callflow name="SampleFlow2">
  4. <override name="${userType}/SampleFlow2"/>
  5. </callflow>
  6. </flow>
  7. <flow name="SampleFlow2">
  8. <step>
  9. <target><static-field-ref code="java.lang.System" name="out"/></target>
  10. <invoke name="println"><argument>一般ユーザです</argument></invoke>
  11. </step>
  12. </flow>
  13. <flow name="admin/SampleFlow2">
  14. <step>
  15. <target><static-field-ref code="java.lang.System" name="out"/></target>
  16. <invoke name="println"><argument>管理者です</argument></invoke>
  17. </step>
  18. </flow>
  19. </flows>



FAQ