ASTERIA3用のコンポーネント/マッパー関数のASTERIA WARPへの移行について

  1. 概要
  2. 定義ファイルの変更
    1. 概要
    2. 修正方法
  3. コネクションの変更
    1. 修正が必要な対象
    2. 概要
    3. 修正方法
  4. トランザクションの変更
    1. 修正が必要な対象
    2. 概要
    3. トランザクションを処理するコンポーネントの作成方法
    4. 修正方法
      1. コネクション(RDBConnection)を使用しているコンポーネントの場合
      2. 独自にcommit/rollbackメソッドを実装したコンポーネントの場合
    5. その他の留意事項

1 概要

この文書ではASTERIA3用に作成されたコンポーネント/マッパー関数をASTERIA WARPで動作させるために必要な移行手順について説明します。

ほとんどの場合、ASTERIA3用に作成されたコンポーネント/マッパー関数のソースコードはそのままASTERIA WARPのSDKでコンパイルすることができ、 そのまま動作しますが一部フレームワーク側の処理の変更によりソースコードの修正が必要となる場合があります。
また定義ファイルに関してはASTERIA3用のものから大きく内容が変更になったのでコンバージョンが必要です。

必要な修正は次の3つです。

以下でそれぞれの修正についての概要と必要な移行手順について説明していきます。

2 定義ファイルの変更

2.1 概要

ASTERIA WARPでは今までは作成不可能だった次のようなコンポーネント/マッパー関数を作成することができます。

これらの定義はASTERIA3までの定義ファイルではおこうなことができません。
またこれまでの定義ファイルはASTERIA R2以前の古いバージョンから引き継がれてきたものであり、一部現在の枠組みとは整合しない部分があったため 今回全面的に改稿することとなりました。
定義ファイルの主な変更点は以下の通りです。

新しい定義ファイルの詳細については定義ファイル書式ガイドを参照してください。

2.2 修正方法

このSDKのconvertフォルダにコンポーネント、マッパー関数の定義ファイルを新形式に変更するためのXSLTファイルがバンドルされています。

また変換を実行するためのフロー(convert.xfp)もありますので、convertフォルダを任意のフローサービスユーザーのホームディレクトリにコピーすれば デザイナーから変換を実行することもできます。

定義ファイルの変換は旧形式の通常使われるような基本的な内容については新形式へ移行することができますが、一部の内容については変換することができません。
例えば

これらは新しいデザイナーではPropertyListenerはコンポーネント/マッパー関数の生成時には動作しない仕様となったために起こります。
その他にも新形式では使える機能自体が増えているため、旧形式からの自動変換で生成されたものよりも適切な表現がある場合があるので、 変換されたファイルはできるだけ定義ファイル書式ガイドを参照して意味を確認するようにしてください。

3 コネクションの変更

3.1 修正が必要な対象

ASTERIAのコネクションを使用しているコンポーネントのみ修正が必要になります。
具体的にはExecuteConext#getConnectionメソッドを用いてASTERIAのコネクションを取得し、それに対して何らかの操作を行っているコンポーネントです。
それ以外のコンポーネントでは以下の修正は必要ありません。

3.2 概要

ASTERIA3ではRDBなどのコネクションクラスで実装するインターフェースとして次の2つがありました。

このことは新たなコネクションの追加を難しくし、事実上サードベンダーによる新しいコネクションの追加は不可能となっていました。
ASTERIA WARPではこれら二つのインターフェースは統合され、FlowConnectionインターフェースは削除されました。

3.3 修正方法

この変更でSDKユーザーに直接関係するのはExecuteContext#getConnectionメソッドの返り値がFlowConnectionからConnectionに変更された点です。
ただしこのメソッドの返り値は通常次のように具象クラスにキャストされて使用されているはずです。

import com.infoteria.asteria.connection.RDBConnectionEntry;
import com.infoteria.asteria.flowengine2.connection.RDBConnection;

private ConnectionProperty _conProp = new ConnectionProperty(RDBConnectionEntry.TYPE, "Connection");
...

public boolean execute(ExecuteContext context) throws FlowException {
	RDBConnection con = (RDBConnection)context.getConnection(_conProp);
	...
}

この場合SDKユーザーで必要な修正はimportされているコネクションの具象クラス名を変更することだけです。
(わかりやすく言えばConnectionのimport節の「flowengine2.」を削除するだけでOKです。)


import com.infoteria.asteria.flowengine2.connection.RDBConnection;

import com.infoteria.asteria.connection.RDBConnection;

4 トランザクションの変更

4.1 修正が必要な対象

トランザクションをサポートしているコンポーネントのみ修正が必要になります。
具体的にはComponentクラスのcommitメソッド、rollbackメソッドをオーバーライドしているコンポーネントです。
それ以外のコンポーネントでは以下の修正は必要ありません。

4.2 概要

ASTERIA WARPでフローのトランザクションの枠組みが変更になりました。
トランザクションはこれまでのように直接Componentのcommitメソッドやrollbackメソッドが実行されるのではなく、 新たに追加されたTransactionManagerクラスとTransactionインターフェースを介して行われることになります。

Transactionインターフェースは以下に示すようにcommitメソッドとrollbackメソッドを持つインターフェースです。
このメソッドシグネチャはASTERIA3までのComponentに実装されていた、commitメソッド、rollbackメソッドと同じなので Componentのサブクラスは「implements Transaction」を宣言するだけでTransactionインターフェースを実装することができます。

package com.infoteria.asteria.flowengine2.execute;

import com.infoteria.asteria.flowlibrary2.FlowException;

public interface Transaction {
	
	public void commit(ExecuteContext context) throws FlowException;
	public void rollback(ExecuteContext context) throws FlowException;
}

新しいトランザクションフレームワークではTransactionManagerにTransaction処理を積み上げていき、それをcommitなりrollback なりするという形式を取ります。

BeginTransaction=Falseの場合はコンポーネントをひとつ実行する度にTransactionはcommitされ、
BeginTransaction=Trueの場合はトランザクション化されたフローの終了時にそこまでに積まれたTransactionがまとめてcommitされます。

親フローのBeginTransaction=Falseの場合はサブフローだけをトランザクション化してその単位でcommitまたはrollbackできる のはこれまでと同じです。
また、BeginTransaction=Falseの場合はトランザクションがロールバックされてからExceptionフローがスタートします。

つまり、フローの実行に関してはトランザクションの処理はほとんど変わりありません。(細かい違いについては後述します。)
ただしSDKを用いてTransaction処理を行うコンポーネントを作成している場合は新フレームワークでトランザクション処理を行うために コンポーネントのコードを修正する必要があります。

4.3 トランザクションを処理するコンポーネントの作成方法

executeまたはexecuteLoopメソッド内でExecuteContext#addTransactionメソッドを実行することで、 必要なTransactionをTransactionManagerに追加することができます。

このメソッドはexecute処理内で複数回実行することができますが、同じTransactionインスタンスを複数回addしても同一インスタンスは1度だけしかTransactionManagerに追加されません。
この仕様はフローでループが発生する場合に大きな意味を持ちます。
トランザクションの仕組みを理解するためにトランザクション処理の内容としてログ出力を行う次のような2つのコンポーネントを考えてみます。

/**
 * Transactionとして追加されるのはComponent自身。
 * つまりループの中で複数回コンポーネントが実行される場合は毎回同一インスタンスが
 * ExecuteContext#addTransactionに渡される
 */
class ComponentA extends SimpleComponent implements Transaction {
	
	public String getComponentName() { return "ComponentA";}
	
	public boolean execute(ExecuteContext context) throws FlowException {
		context.addTransaction(this);
		passStream();
		return;
	}
	
	public void commit(ExecuteContext context) throws FlowException {
		//ExecuteContext#infoはログに情報を出力するメソッド
		context.info("ComponentA commit");
	}
	
	public void commit(ExecuteContext context) throws FlowException {
		context.info("ComponentA rollback");
	}
}

/**
 * 追加するTransactionを毎回 new しているので常に異なるインスタンスとなる
 */
class ComponentB extends SimpleComponent {
	
	public String getComponentName() { return "ComponentB";}
	
	public boolean execute(ExecuteContext context) throws FlowException {
		context.addTransaction(new MyTransaction());
		passStream();
		return;
	}
	
	private static class MyTransaction implements Transaction {
		public void commit(ExecuteContext context) throws FlowException {
			context.info("ComponentB commit");
		}
		
		public void commit(ExecuteContext context) throws FlowException {
			context.info("ComponentB rollback");
		}
	}
}

この2つのコンポーネントを用いて次のようなフローを書いたとします。

  Start(BeginTransaction=True)
    ↓
  LoopStart(LoopCount=3)
    ↓
  ComponentA
    ↓
  ComponentB
    ↓
  End

するとログへの出力結果は次のようになります。

  情報(End1) : ComponentA commit
  情報(End1) : ComponentB commit
  情報(End1) : ComponentB commit
  情報(End1) : ComponentB commit

トランザクションのコミットはフローの終了時(Endコンポーネント実行後)に一度だけ行われています。
ComponentA、ComponentBともにループの中で3回実行されていますが、ComponentAが積んだTransactionはひとつだけであり、 ComponentBは3つのTransactionを積み上げていることがわかります。

ちなみにこのフローをStart(BeginTransaction=False)として実行すると、結果は以下のようになります。

  情報(ComponentA1) : ComponentA commit
  情報(ComponentB1) : ComponentB commit
  情報(ComponentA1) : ComponentA commit
  情報(ComponentB1) : ComponentB commit
  情報(ComponentA1) : ComponentA commit
  情報(ComponentB1) : ComponentB commit

BeginTransaction=Falseの場合はコンポーネント実行直後にTransactionがcommitされるので、 ComponentAが2回目に実行される際には以前に追加したTransactionは残っておらず、 再度ComponentA自身がTransactionとして追加されているのです。

4.4 修正方法

ASTERIA3用に作成されたコンポーネントの新フレームワークへの対応方法を、 コネクション(RDBConnection)を使用するコンポーネントと、独自にcommit/rollbackメソッドを実装したコンポーネントの2つに分けて説明します。
どちらの場合も形式化された手順を踏むだけで新フレームワークに対応させることができます。

4.4.1 コネクション(RDBConnection)を使用しているコンポーネントの場合

RDBConnectionを使用するコンポーネントは、「RDBGet」「RDBPut」「SQLCall」など複数存在します。
それらのコンポーネントはひとつのフロー内に複数配置されることがありえるわけですが、 BeginTransaction=Trueのフローではそれらのコンポーネント群のトランザクション処理はRDBConnectionに対して1度だけ行われれば良いことになります。

コンポーネント側の処理としては使用しているコネクションに対応する単一のTransactionインスタンスを取得し、それをTransactionManagerに追加すればOKです。
別のRDBコンポーネントが同じコネクションを使用していてもTransactionインスタンスが重複するためTransactionManagerによって破棄されます。

	private ConnectionProperty _connectionProp = new ConnectionProperty(RDBConnectionEntry.TYPE, "Connection");
	
	/**
	 * executeメソッドの先頭で使用しているConnectionに対応するTransactionを追加
	 */
	public boolean execute(ExecuteContext context) throws FlowException {
		context.addTransaction(context.getConnectionTransaction(_connectionProp));
		...
	}
	
	/*
	//executeLoopメソッドが実装されている場合は同様にTransactionの追加をメソッドの先頭で行う
	public int executeLoop(ExecuteContext context) throws FlowException {
		context.addTransaction(context.getConnectionTransaction(_connectionProp));
		...
	}
	*/

それまでコンポーネントに実装されていたcommitメソッド、rollbackメソッドは削除してしまって構いません。
(これまでに使用されていたExecuteContext#commitConnectionメソッドとrollbackConnectionメソッドは新しいフレームワークでは適切に機能しません。)

4.4.2 独自にcommit/rollbackメソッドを実装したコンポーネントの場合

独自にcommit/rollbackを実装したコンポーネントではComponent自身にTransactionインターフェースを実装して、 それをexecuteメソッド(必要な場合はexecuteLoopメソッドも)の先頭でTransactionManagerに追加すればOKです。

class ComponentA extends SimpleComponent implements Transaction
{
	/**
	 * executeメソッドの先頭で使用しているConnectionに対応するTransactionを追加
	 */
	public boolean execute(ExecuteContext context) throws FlowException {
		context.addTransaction(this);
		...
	}
	
	/*
	//executeLoopメソッドが実装されている場合は同様にTransactionの追加をメソッドの先頭で行う
	public int executeLoop(ExecuteContext context) throws FlowException {
		context.addTransaction(this);
		...
	}
	*/
	
	//commitメソッド、rollbackメソッドの内容は変更する必要がない
	public void commit(ExecuteContext context) throws FlowException {
		...
	}
	
	public void commit(ExecuteContext context) throws FlowException {
		...
	}
}

ここでの説明ではASTERIA3までのトランザクション処理と同等の処理を実現するためにexecute(executeLoop)メソッドの先頭で addTransactionするようにガイドしていますが、実際には必要な場合にだけaddTransactionする方がパフォーマンス的に有利です。

例えばFileGetコンポーネントでは「FileDeleteプロパティがTrueの場合だけaddTransactionする」とすれば、 余計なトランザクション処理が発生しなくなります。

4.5 その他の留意事項

トランザクションフレームワークの修正はASTERIA3までの動作と変わらないように修正されています。
唯一NextFlowがある場合のトランザクションの動作が変わっていますが、これはどちらかと言えば仕様バグが修正されたといえます。

トランザクションの実行順序

原則としてTransactionManagerはcomitまたはrollbackの際にはTransactionをそれがaddされた順番に処理します。
ただしConnectionに対するTransaction(ExecuteContext#getConnectionTransactionで取得されたTransaction)は他のTransactionよりも先に処理します。

例えば、

FileGet(DeleteFile=True) → RDBPut

のようなフローでは、

RDBコミット → ファイル削除

の順でトランザクション処理が行われます。
この動作はASTERIA3までのトランザクションの動作と同じです。

Connectionを使用しないトランザクションでもNextFlowをまたげるようになった

NextFlow指定時に、「Transaction=DoNothing」とした場合、これまではコンポーネントのcommit処理は実行されませんでしたが、 全てのトランザクション処理がNextFlowに引き継げるようになりました。

(注)コンポーネント自身がTransactionインターフェースを実装している場合は注意が必要です。 この場合、コンポーネントのtermメソッドが実行された後でTransactionインターフェースのcommit(または)メソッドが実行される ことになるので、termメソッドでTransactionに関わる内容をクリーンナップしてはいけません。