第13章 カスタムアクションの開発

JBoss ESB は、カスタムのアクションを様々な方法で開発することができます。アクションごとに利点があり、以下のように分類できます。
  • ライフサイクルアクション、org.jboss.soa.esb.actions.ActionLifecycle または org.jboss.soa.esb.actions.ActionPipelineProcessor を実装
  • Java bean アクション、org.jboss.soa.esb.actions.BeanConfiguredAction を実装
  • 注釈付きのアクション
  • レガシーアクション
実装ごとの違いを理解するには、以下を把握する必要があります。
  • アクションがどのように設定されるか
  • いつアクションがインスタンス化されるか、実装のスレッドセーフティについて
  • ライフサイクルイベントの視覚性があるかどうか
  • アクションメソッドが直接呼び出されるか、リフレクションで呼び出されるか

13.1. プロパティを使用したアクションの設定

通常、アクションはタスクを実行するために外部の設定が必要なテンプレートとして動作します。たとえば、PrintMessage アクションは出力内容を示す message という名前のプロパティや出力回数を示す repeatCount とうプロパティを受け取ることができます。多くの場合、jboss-esb.xml ファイル内のアクション設定は以下のようになります。
<action name="PrintAMessage" class="test.PrintMessage">
 <property name="information" value="Hello World!" />
 <property name="repeatCount" value="5" />
</action>
このマッピングがどのように行われるかを変更する場合、その方法はゲートウェイの種類によって異なります。

13.2. ライフサイクルアクション

ライフサイクルアクションは、ライフサイクルインターフェース (org.jboss.soa.esb.actions.ActionLifecycleorg.jboss.soa.esb.actions.ActionPipelineProcessor) からきています。ActionLifecycle は、パイプラインライフサイクルメソッド (initialisedestroy) を実装し、ActionPipelineProcessor で processprocessSuccessprocessException などのメッセージ処理メソッドを含むようにこのインターフェースを拡張します。
このインターフェースは管理されたライフサイクルを持つステートレスアクションの実装をサポートします。これらのインターフェースのいずれかを実装するクラスの単一インスタンスがパイプラインごとにインスタンス化され (アクション毎の設定)、スレッドセーフでなければなりません。initialisedestroy パイプラインのライフタイムの間、アクションがリソース管理を行えるように、メソッドをオーバーライドすることができます。たとえば、initialise メソッドで必要なリソースをキャッシュし、destroy メソッドで消去するなどです。
これらのアクションは、パラメーターとして ConfigTree インスタンスを 1 つとるコンストラクターを定義する必要があります。これは、パイプライン内の特愛知のアクションの設定を表記します。
アクションが適切なインターフェースを実装し、メソッド名が設定でオーバーライドされないという前提で、パイプラインはメソッドを直接呼び出します。インターフェースで実装されていないメソッドや、アクション設定でオーバーライドされたメソッドは、リフレクションを使用して呼び出されます。
開発を簡素化するには、コードベースに抽象ベースクラスが 2 つ提供されています。それぞれ、適切なインターフェースを実装し、process メソッド以外の空のスタブメソッドを提供します。この 2 つのクラスは、org.jboss.soa.esb.actions.AbstractActionPipelineProcessororg.jboss.soa.esb.actions.AbstractActionLifecycle で、以下のように使用できます。
public class ActionXXXProcessor extends AbstractActionPipelineProcessor {
  public ActionXXXProcessor(final ConfigTree config) {
    // extract configuration
  }
  
  public void initialise() throws ActionLifecycleException {
    // Initialize resources...
  }
  
  public Message process(final Message message) throws ActionProcessingException {
    // Process messages in a stateless fashion...
  }

  public void destroy() throws ActionLifecycleException {
    // Cleanup resources...
  }
}

13.3. Java Bean アクション

アクションプロパティを設定する他の方法はプロパティ名に対応するアクションにセッターを追加し、フレームワークがそれらにデータを自動的に入力するよう許可することです。アクション Bean にデータを自動的に入力するために、アクションクラスは org.jboss.soa.esb.actions.BeanConfiguredAction マーカーインターフェースを実装する必要があります。たとえば、以下のクラスの動作は上記のものと同じです。
public class PrintMessage implements BeanConfiguredAction {
  private String information;
  private Integer repeatCount;
  public setInformation(String information) {
    this.information = information;
  }
  public setRepeatCount(Integer repeatCount) {
    this.repeatCount = repeatCount;
  }
  public Message process(Message message) {
    for (int i=0; i < repeatCount; i++) {
      System.out.println(information);
    }
  }
}

注記

setRepeatCount()Integer パラメーターは XML で指定された String 形式から自動的に変換されます。
ロードプロパティの BeanConfiguredAction メソッドは単純な引数を取るアクションに適しており、ConfigTree メソッドは XML 形式のデータを直接扱う必要がある場合に適しています
これらのアクションは、ライフサイクルメソッドをサポートせず、パイプラインを通過するメッセージについてはリフレクション経由で呼び出します。

13.4. 注釈付きアクションクラス

JBoss Enterprise Service Bus には、クリーンな action 実装をより簡単に構築できるアクションアノテーションがあります。これにより、インターフェースや抽象クラスの実装、ConfigTree タイプ (jboss-esb.xml ファイルの設定情報) の処理などの複雑な部分がなくなります。注釈付きアクションのインスタンスは、パイプラインごとにインスタンス化され (アクション毎の設定)、スレッドセーフでなければなりません。このパイプラインは常に、リフレクションを使用してアクションメソッドを使用して呼び出します。
以下がそのアノテーションです。
  • @Process
  • @ConfigProperty
  • @Initialize
  • @Destroy
  • @BodyParam
  • @PropertyParam
  • @AttachmentParam
  • @OnSuccess
  • @OnException

13.4.1. @Process

最も簡単な実装は、@Process のアノテーション付きの単一メソッドと基本的なplain old Java object (POJO) でアクションを作成します。
public class MyLogAction {

                @Process
 		    public void log(Message message) {
		        // log the message...
		    }
		}
@Process アノテーションは、有効な ESB action としてクラスを特定します。クラス内に複数のメソッドがある場合、メッセージインスタンスを処理するのに使用するメソッドを特定します (または、メッセージの一部。これについては、@BodyParam、@PropertyParam and @AttachmentParam アノテーションの説明時に詳しくみていきます)。
この action インスタンスを pipeline に設定するには、ロー/ベースレベルの action 実装と同じプロセスを使用します (AbstractActionPipelineProcessor を継承する、または ActionLifecycle を実装するもの、あるいはその他のサブタイプや抽象実装):
<service .....>
		    <actions>
		        <action name="logger" class="com.acme.actions.MyLogAction" />
		    </actions>
		</service>
@Process のアノテーションがついた複数のメソッドに action 実装が関連付けられている場合、process 属性を使用して、メッセージインスタンスの処理に使用するものはどれか指定します。
<service .....>
		    <actions>
		        <action name="logger" class="com.acme.actions.MyLogAction" 
                                           process="log" />
		    </actions>
		</service>

13.4.2. @Process メソッドの戻り値

以下を返すように @Process メソッドを実装することができます。
  • void: これは、上記の Logger アクション実装にあるように、戻り値がないという意味です。
  • message: これは ESB メッセージインスタンスです。action pipeline 上でアクティブな/現在のインスタンスになります。
  • その他のタイプ。メソッドが ESB メッセージインスタンスを返さない場合、返されたオブジェクトインスタンスは、action pipeline にある現在の ESB メッセージインスタンスに設定されます。メッセージがどこに設定されるかは、set-payload-location <action> 設定プロパティにより変わります。これは、通常の MessagePayloadProxy ルールに従いデフォルト設定します。

13.4.3. @Process メソッドパラメーター

@Process メソッドを使用して、様々な方法でパラメーターを指定します。
  1. メソッドパラメーターとして ESB メッセージインスタンスを指定
  2. 任意のパラメータータイプを 1 つ以上指定。Enterprise Service Bus フレームワークは、アクティブ/現在のパイプラインメッセージインスタンス内でそのタイプのデータを検索します。まず、メッセージボディ、次にプロパティ、最後に添付を検索し、そのパラメーターの値としてこのデータを渡します (該当するものが見つからない場合は null)。
最初のオプションに関する例は、Logger アクションの上記の例に記述されています。2 つ目のオプションの例を以下に示しています。
public class OrderPersister {

		    @Process
		    public OrderAck storeOrder(OrderHeader orderHeader,
								OrderItems orderItems) {
		        // process the order parameters and return an ack...
		    }
		}
この例では、@Process メソッドは、OrderHeaderOrderItem オブジェクトインスタンスの作成や、現在のメッセージへの添付について pipeline の 1 つ前のアクションに左右されます (より現実的な実装では、XML または EDI ペイロードを注文インスタンスにデコーディングする一般的なアクション実装を持ちます。OrderPersister は、単独のパラメーターとして注文インスタンスを取ります)。以下に例を示します。
public class OrderDecoder {

		    @Process
		    public Order decodeOrder(String orderXML) {
		        // decode the order XML to an ORder instance... 
		    }
		}

		public class OrderPersister {

		    @Process
		    public OrderAck storeOrder(Order order) {
		        // persist the order and return an ack...
		    }
		}

サービス設定で 2 つのアクションをつなぎます。
<actions>
    <action name="decode" class="com.acme.orders.OrderDecoder" />
    <action name="persist" class="com.acme.orders.OrderPersister" />
</actions>

アノテーションが少ないため、オプション 2 のほうが読み込みが簡単ですが、実行時に適切なパラメーターをメッセージ内から検索する処理が決定的でないため、リスクがあります。このため、Red Hat は@BodyParam、@PropertyParam、@AttachmentParam アノテーションをサポートします。
これらの @Process メソッドパラメーターのアノテーションを使用して、明示的にメッセージ内のどこから @Process メソッドの個別のパラメーター値がリトリーブされているか定義します。名前の通り、これらのアノテーションはそれぞれ、パラメーターごとに名前付きのロケーションを指定することができます。
public class OrderPersister {

  		    @Process
		    public OrderAck storeOrder(
		                   @BodyParam("order-header") OrderHeader orderHeader,
		                   @BodyParam("order-items") OrderItems orderItems) {
			
		        // process the order parameters and return an ack...
		    }
		}
指定のメッセージロケーションに値が含まれていない場合、このパラメーターについて null が渡されます (@Process メソッドインスタンスはこの処理方法を決定)。反対に指定のロケーションに誤ったタイプの値が含まれている場合、MessageDeliverException がスローされます。

13.4.4. @ConfigProperty

アクションは、カスタム設定がある程度必要になるものがほとんどです。ESB アクション設定では、プロパティは <action> 要素の <property> サブ要素として提供されます。
<action name="logger" class="com.acme.actions.MyLogAction">
    <property name="logFile" value="logs/my-log.log" />
    <property name="logLevel" value="DEBUG" />
</action>
これらのプロパティを活用するため、低/基本レベルのアクション実装を使用する必要があります (AbstractActionPipelineProcessor の継承または、ActionLifecycle の実装)。これは、ConfigTree クラスと連携します (コンストラクター経由でアクションに提供します) 。アクションを実装するには、以下の手順に従います。
  1. ConfigTree インスタンスを提供するアクションクラスへコンストラクターを定義します。
  2. ConfigTree インスタンスから該当のアクション設定プロパティをすべて取得します。
  3. 必須のアクションプロパティを確認して、<action> 設定に指定されていない場所で例外をあげます。
  4. プロパティの全値を文字列 (ConfigTree で提供) から、アクション実装で使用される適切なタイプにデコーディングします。例えば、java.lang.String から java.io.File に、java.lang.String から Boolean に、java.lang.String から long などにデコーディングします。
  5. 設定値が対象のプロパティタイプにデコーディングできない場合、例外を出します。
  6. 考えられる各種設定すべてに単体テストを実装して、さきほど表示したタスクが正しく完了するようにします。
上記のタスクは一般的に実行するのは難しくありませんが、作業が増え、エラーが発生しやすく、アクションごとに設定の間違いを処理する方法も異なってきます。結果として、比較すると全体的に明確さが欠けますが、多くのコードを追加する必要がある場合もあります。
アノテーション付きのアクションは、@ConfigProperty を使用してこのような問題に対処します。2 種の必須設定プロパティ (logFile and logLevel) を持つ、MyLogActions 実装を拡張します。
public class MyLogAction {

		    @ConfigProperty
		    private File logFile;

		    @ConfigProperty
		    private LogLevel logLevel;
 
		    public static enum LogLevel {
		        DEBUG, 
		        INFO, 
		        WARN
		    }

		    @Process
		    public void log(Message message) {
		        // log the message at the configured log level...
		    }
		}

注記

(フィールドではなく) "setter" メソッド上で @ConfigProperty アノテーションを定義することも可能です。
必要事項は以上です。Enterprise Service Bus がアクションをデプロイすると、デコーディングされた値にある実装とマップの両方を検証します (enum のサポートと上記の LogLevel enum)。 @ConfigProperty アノテーションを処理するアクションフィールドを検索します。開発者は、ConfigTree クラスを処理したり、余分のコードを開発したりする必要がありません。
デフォルトでは、@ConfigProperty アノテーションを処理するクラスフィールドはすべて必須です。必須ではないフィールドは、以下の 2 種のいずれかの方法で処理されます。
  1. フィールドの @ConfigProperty アノテーションで use = Use.OPTIONAL を指定
  2. フィールドの @ConfigProperty アノテーションで defaultVal を指定 (任意)
ログアクションのプロパティをオプションのみにするには、以下のようなアクションを実装します。
public class MyLogAction {
		
		    @ConfigProperty(defaultVal = "logs/my-log.log")
		    private File logFile;

		    @ConfigProperty(use = Use.OPTIONAL)
		    private LogLevel logLevel;

		    public static enum LogLevel {
		        DEBUG, 
		        INFO, 
		        WARN
		    }

		    @Process
		    public void log(Message message) {
		        // log the message...
		    }
		}
@ConfigProperty アノテーションは、2 つの追加フィールドをサポートします。
  1. name: これを使用して、アクションインスタンスで指定の名前のフィールドを生成する際に使用するアクション設定プロパティ名を明示的に指定します。
  2. choice: このフィールドを使用して、許容の設定値を制限します。これは、列挙型を使用することで可能です (LogLevel)
この名前フィールドは、古いアクション (低/基本レベルの実装タイプを使用) を新しいアノテーションベースの実装に以降する際などに使用できます。プロパティの古い設定名 (後方互換との関係で変更不可) は有効な Java フィールド名へのマッピングは行いません。ログアクションを例にとります。以下がログアクションの古いアクションだと仮定します。
<action ...>
		    <property name="log-file" value="logs/my-log.log" />
		    <property name="log-level" value="DEBUG" />
		</action>
ここのプロパティ名は、有効な Java フィールド名にマッピングを行わないため、@ConfigProperty アノテーションで名前を指定します。
public class MyLogAction {
		
		    @ConfigProperty(name = "log-file")
		    private File logFile;

		    @ConfigProperty(name = "log-level")
		    private LogLevel logLevel;

		    public static enum LogLevel {
		        DEBUG, 
		        INFO, 
		        WARN
		    }

		    @Process
		    public void log(Message message) {
		        // log the message...
		    }
		}

13.4.5. プロパティの値のデコード

Bean 設定のプロパティ値は、文字列の値からデコーディングされます。適切な POJO bean プロパティタイプと一致させるには、以下のシンプルなルールを使用します。
  1. プロパティタイプに単一引数の文字列コンストラクターがある場合、これを使用します。
  2. Primitive な場合、オブジェクトタイプの単一引数文字列コンストラクターを使用します。例えば、int の場合、整数のオブジェクトを使用します。
  3. 列挙型の場合 Enum.valueOf を使用して設定の文字列を列挙型の値に変換します。

13.4.6. @Initialize と @Destroy

ときに、アクション実装はデプロイメント時に初期化タスクを実行する必要があります。また、アンデプロイ時にはクリーンアップも行う必要があります。このような理由から、@Initialize および @Destroy メソッドアノテーションがあります。
これについて例を挙げます。デプロイメント時に、ロギングアクションはチェックをいくつか行います (例えばファイルやディレクトリが存在するかなど)。アンデプロイされると、アクションはクリーンアップタスクを実行する必要があります (ファイルを閉じるなど)。以下がこれらのタスクを実行するためのコードです。
public class MyLogAction {

		    @ConfigProperty
		    private File logFile;

		    @ConfigProperty
		    private LogLevel logLevel;

		    public static enum LogLevel {
		        DEBUG, 
		        INFO, 
		        WARN
		    }

		    @Initialize
		    public void initializeLogger() {
			// Check if file already exists… check if parent folder 
			// exists etc...
			// Open the file for writing...
		    }
 
		    @Destroy
		    public void cleanupLogger() {
		        // Close the file...
		    }

		    @Process
		    public void log(Message message) {
		        // log the message...
		    }
		}

注記

@ConfigProperty アノテーションはすべて、ESB デプロイヤーが @Initialize メソッドを呼び出す前に処理されます。そのため、@Initialize メソッドは、カスタマイズの初期化を行う前に準備が整ったこれらのフィールドに依存します。

注記

これらのアノテーションを両方使用して、メソッドを指定する必要はありません。必要がある場合のみ指定します。つまり、メソッドが初期化のみ必要な場合、@Initialize アノテーションのみを使用します (@Destroy アノテーションで注釈をつけた matching メソッドを提供する必要はありません)。

注記

単一のメソッドを指定して、@Initialize と @Destroy 両方でアノテーションをつけることができます。

注記

@Initialize メソッドに ConfigTree パラメーターをオプションで指定可能です。これを行うと ConfigTree インスタンスを基本とするアクションにアクセスできるようになります。

13.4.7. @OnSuccess と @OnException

アクションが設定されている pipeline の中で、実行が成功した際、または失敗した際にこれらのメソッドを実行すべきか指定するため、これらのアノテーションを使用します。
public class OrderPersister {

		    @Process
		    public OrderAck storeOrder(Order order) {
		        // persist the order and return an ack...
		    }

		    @OnSuccess
		    public void logOrderPersisted(Message message) {
		        // log it...
		    }

		    @OnException
		    public void manualRollback(Message message, 
                                           Throwable theError) {
		        // manually rollback...
		    }
		}
これらのアノテーションの場合、パラメーターはパラメーターに渡すかはオプションです。上記のパラメーターの一部、すべてを提供、何も提供しないといった選択ができます。Enterprise Service Bus フレームワークは、それぞれの場合で該当のパラメーターを解決します。

13.5. レガシーアクション

上記のカテゴリに属さないアクションはすべて、レガシーとしてパイプラインに処理されます。これは、以前の Rosetta コードベースの動作を継承します。実際には、以下のようになります。
  • ConfigTree パラメーターを 1 つとコンストラクターを提供することで、このアクションの設定を行います。
  • このパイプラインを経由するすべてのメッセージに対してインスタンス化されます。
  • このアクションには、ライフサイクルメソッドの情報はありません。
  • process メソッドの呼び出しは、常にリフレクションで行われます。

このページには機械翻訳が使用されている場合があります (詳細はこちら)。