5. データを受け取る側の実装¶
項目
用意する資材¶
受信側で用意する資材は、以下の通りです。図 受信側の処理表 用意する資材一覧
番号 資材名 準備必須 解説 1 送受信モデル(Generic)(図中の Generic B) 必須 送受信モデル(Generic)を作成する 2 受信するデータを格納するためのクラス(独自モデル)(図中の Data B) 必須 受信するデータを格納するためのクラスを作成する 3 データ変換クラス(図中の Decoder) 必須 受信側のデータ変換クラス(Decoder)を作成する 4 処理結果を格納するクラス(図中の Result B) 任意 処理結果を格納するクラスを作成する 5 データ処理クラス(図中の Procedure) 必須 受信側のデータ処理クラス(Procedure)を作成する 6 マッピング設定(図中の Mapping Table) 必須 マッピング設定を作成する また、受信したいデータが送信側から送信されるタイミングを示す以下のキー情報を、送信側のマッピング設定などから入手してください。下記の例では、「データを送る側の実装 」で作成したデータを受信するための元となる資材を記載しています。
送信する「独自モデル」の完全修飾子例) jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData データの操作種別例) DATA_CREATED 送信側が送る「送受信モデル(Generic)」のパラメータ名と型情報例) jp.co.intra_mart.module_name.function_name.propagation.generic.SampleGenericData
パラメータ名 型 resourceId String url String 送信側が受け取る「処理結果を格納するクラス」のパラメータ名と型情報送信側が処理結果を要求していない場合は不要です。例) jp.co.intra_mart.module_name.function_name.propagation.result.ProcResult
パラメータ名 型 success boolean
送受信モデル(Generic)を作成する¶
最初に、直列化したデータを受信するための「送受信モデル(Generic)」を作成します。ただし、以下に該当する場合は、このクラスを新規に作成する必要はありません。
送信されるデータが格納される、データの送信側が定義した「送受信モデル(Generic)」が IM-Propagation 内に定義されている「送受信モデル(Generic)」のいずれかのクラスで復元可能である場合、定義済みのクラスを流用できます。定義されている「送受信モデル(Generic)」は、「APIドキュメント - jp.co.intra_mart.foundation.propagation.model.generic 」を参照してください。復元可能かどうかを判定する場合は、「復元可能なクラスとは 」を参照してください。新しい「送受信モデル(Generic)」を用意する場合は、AbstractGeneric クラスを継承して新しいクラスを作成してください。送信側が公開している「送受信モデル(Generic)」から受信したいデータが全て格納でき、かつ、「送受信モデル(Generic)」の制約を満たすクラスを作成してください。「送受信モデル(Generic)」の制約についての詳細は、「IM-Propagation 仕様書 」を参照してください。package jp.co.intra_mart.module_name.function_name.propagation.generic; import jp.co.intra_mart.foundation.propagation.model.generic.AbstractGeneric; public class SampleGenericData extends AbstractGeneric { /** バージョン番号(新しく採番してください) */ private static final long serialVersionUID = 1234567890123456789L; /** リソースID */ private String resourceId; /** URL */ private String url; public String getResourceId() { return resourceId; } public String getUrl() { return url; } public void setResourceId(final String resourceId) { this.resourceId = resourceId; } public void setUrl(final String url) { this.url = url; } }注意
AbstractGeneric クラスは Serializable インタフェースを実装しているため、serialVersionUID が必要です。上記の値をそのまま使用せず、新しく採番してください。
受信するデータを格納するためのクラスを作成する¶
次に、後で作成する「受信側のデータ変換クラス(Decoder)」を通して、受信するデータを格納するためのクラス(以下、「独自モデル」)を用意します。例えば、「送受信モデル(Generic)」において String 型で定義されたプロパティを、「独自モデル」においては URL 型として扱いたい場合、String から URL に変換処理を行うために変換後のデータを格納するためのクラスが必要になります。package jp.co.intra_mart.module_name.function_name.propagation; import java.net.URL; public class ReceiverModuleData { /** リソースID */ private String resourceId; /** URL */ private URL url; public String getResourceId() { return resourceId; } public URL getUrl() { return url; } public void setResourceId(final String resourceId) { this.resourceId = resourceId; } public void setUrl(final URL url) { this.url = url; } }
受信側のデータ変換クラス(Decoder)を作成する¶
次に、「送受信モデル(Generic)」を「独自モデル」に変換する機能を提供するクラス(以下、「データ変換クラス」)を用意します。「データ変換クラス」を作成する際は、AbstractDecoder クラスを継承してください。AbstractDecoder クラスの総称型は、左から「送受信モデル(Generic)」、「独自モデル」の順にクラスタイプを指定してください。AbstractDecoder クラスを継承することで、decode、getGenericDataClass メソッドの実装が必須となります。package jp.co.intra_mart.module_name.function_name.propagation.decoder; import java.net.MalformedURLException; import java.net.URL; import jp.co.intra_mart.foundation.propagation.exception.ConvertException; import jp.co.intra_mart.foundation.propagation.receiver.AbstractDecoder; public class SampleDecoder extends AbstractDecoder<SampleGenericData, ReceiverModuleData> { /** * 「送受信モデル」から「独自モデル」に変換するメソッド * @param generic 送受信モデル * @return 独自モデル * @throws 変換に失敗した場合 */ @Override public ReceiverModuleData decode(final SampleGenericData generic) throws ConvertException { try { // 送受信モデルから独自モデルに変換 final ReceiverModuleData data = new ReceiverModuleData(); if (generic.getResourceId() != null) { data.setResourceId(generic.getResourceId()); } if (generic.getUrl() != null) { data.setUrl(new URL(generic.getUrl())); } // 独自モデルを返却 return data; } catch (final MalformedURLException e) { throw new ConvertException(e); } } /** * 「送受信モデル」のクラスを返却するメソッド * @return 送受信モデルのクラス */ @Override public Class<SampleGenericData> getGenericDataClass() { return SampleGenericData.class; } }実装が必要なメソッドとその説明は、以下の通りです。
decode メソッド
引数から渡された「送受信モデル(Generic)」を「独自モデル」に入れ替えて返却してください。「送受信モデル(Generic)」と「独自モデル」がまったく同じクラスの場合は、そのまま引数の値を返却することができます。データの入れ替えができない状況のときや、必要なデータが格納されていなかった場合は、ConvertException 、または、ConvertException を継承した例外クラスをスローしてください。getGenericDataClass メソッド
「送受信モデル(Generic)」のクラスを返却してください。
処理結果を格納するクラスを作成する¶
次に、受信側が処理した結果を送り返すための「処理結果を格納するクラス」を作成します。特に処理結果を返却しない場合はクラスを用意する必要はなく、IM-Propagation で用意されている jp.co.intra_mart.foundation.propagation.model.EmptyObject クラスで代用できます。新しい「処理結果を格納するクラス」を用意する場合は、送信側が公開している「処理結果を格納するクラス」から復元可能なクラスを作成してください。復元可能かどうかを判定する場合は、「復元可能なクラスとは 」を参照してください。package jp.co.intra_mart.module_name.function_name.propagation.result; import java.io.Serializable; public class ProcResult implements Serializable { /** バージョン番号(新しく採番してください) */ private static final long serialVersionUID = 1234567890123456789L; /** 成功フラグ */ private boolean success; public boolean isSuccess() { return success; } public void setSuccess(final boolean success) { this.success = success; } }注意
「処理結果を格納するクラス」は Serializable インタフェースを実装する必要があるため、serialVersionUID が必要です。上記の値をそのまま使用せず、新しく採番してください。
受信側のデータ処理クラス(Procedure)を作成する¶
次に、「送受信モデル(Generic)」または「独自モデル」を受け取りデータ処理を行うクラス(以下、「データ処理クラス」)を用意します。IM-Propagation は独自のセッション管理を行っており、データベースのトランザクション管理は IM-Propagation が自動的に行います。セッション管理についての詳細は、「IM-Propagation 仕様書 」を参照してください。「データ処理クラス」を作成する際は、AbstractProcedure クラスを継承してください。AbstractProcedure クラスの総称型は、左から「独自モデル」、「処理結果を格納するクラス」の順にクラスタイプを指定してください。AbstractProcedure クラスを継承することで、onReceive メソッドの実装が必須となります。package jp.co.intra_mart.module_name.function_name.propagation.procedure; import jp.co.intra_mart.foundation.propagation.code.EventStatus; import jp.co.intra_mart.foundation.propagation.exception.ProcedureException; import jp.co.intra_mart.foundation.propagation.model.ReceiveParameter; import jp.co.intra_mart.foundation.propagation.model.ReceiveResult; import jp.co.intra_mart.foundation.propagation.receiver.AbstractProcedure; public class SampleProcedure extends AbstractProcedure<ReceiverModuleData, ProcResult> { /** * データを受信した際の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @param data 独自モデル * @throws ProcedureException 処理に失敗した場合 */ @Override public ReceiveResult<ProcResult> onReceive(final ReceiveParameter parameter, final ReceiverModuleData data) throws ProcedureException { // データリクエストからデータ変換クラスを通してモデルを取得 System.out.print(data.getUrl()); System.out.print(data.getResourceId()); // 処理成功を返却 final ProcResult result = new ProcResult(); result.setSuccess(true); return new ReceiveResult<ProcResult>(EventStatus.SUCCEEDED, result); } }実装が必要なメソッドとその説明は、以下の通りです。
onReceive メソッド
受信したデータを処理する内容を実装してください。メソッドの戻り値には、ReceiveResult クラスのインスタンスを作成して返却します。総称型には「処理結果を格納するクラス」を指定してください。各メソッドで返却する戻り値のインスタンスを作成する際、以下のいずれかのステータス値をコンストラクタに設定してください。表 ステータス一覧
値 概要 使用例 SUCCEEDED 処理成功 処理が最後まで正常に終了したことを示す目的で使用します。 FAILED 処理失敗 処理が途中で失敗したことを示す目的で使用します。 例外発生時と同様です。 各メソッド内で処理に失敗し例外をスローする場合は ProcedureException、または、ProcedureException を継承した例外クラスをスローしてください。コラム
SUCCEEDED、FAILED 以外のステータス値は、IM-Propagation マネージャ内部で使用します。明示的には使用しません。 「処理結果を格納するクラス」を作成せず、特に処理結果を返さない場合は、最後の ReceiveResult インスタンスの返却を以下のように実装することができます。return new ReceiveResult<EmptyObject>(EventStatus.SUCCEEDED);注意
onReceive メソッド内でデータベース以外(ファイルやメモリなど)の更新・削除操作を行う場合は、データベースとは別のトランザクション管理を実装する必要があります。詳細は、「データベース以外の操作を行う「データ処理クラス」の実装 」を参照してください。
マッピング設定を作成する¶
次に、ここまで作成した「送受信モデル(Generic)」「データ変換クラス」「データ処理クラス」を紐付けるためのマッピング設定を作成します。ファイル名の最初にはモジュールIDを含めるなど、他のモジュールが提供している設定ファイルと衝突しないようにしてください。マッピング設定は、以下のような形式で記述します。「設定ファイルリファレンス - IM-Propagation 受信側設定 」も合わせて参照してください。
パス WEB-INF/conf/propagation-receivers-config/{任意のファイル名}.xml
<?xml version="1.0" encoding="UTF-8"?> <propagation-receivers-config xmlns="http://www.intra-mart.jp/propagation/receivers-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intra-mart.jp/propagation/receivers-config propagation-receivers-config.xsd"> <receiver source="jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData" operationType="DATA_CREATED"> <decoder class="jp.co.intra_mart.module_name.function_name.propagation.decoder.SampleDecoder" /> <procedure class="jp.co.intra_mart.module_name.function_name.propagation.procedure.SampleProcedure" /> </receiver> </propagation-receivers-config>注意
ファイル名は、他のモジュールが提供しているものと重複しないようにするために、モジュールIDを接頭子にするなどの対策を行ってください。例) receiver_module-sample_data.xml設定が必要なタグとその説明は、以下の通りです。
receiver タグ
source 属性には、データを送信する側が指定した「独自モデル」、または、「送受信モデル(Generic)」のパッケージ名を含むクラス名(完全修飾子)を指定します。受信側のクラス名ではありませんので注意してください。例えば、送信側が定義した SenderModuleData クラスのデータを受信したい場合は、SenderModuleData のクラス名を指定します。キーとして使用されるだけですので、この場合でも送信側モジュールとの依存関係は必要ありません。operationType 属性には、データを送信する側が指定した「データの操作種別」を指定します。例えば、送信側が定義した SenderModuleData クラスのデータが、DATA_CREATED の「データの操作種別」で送信される場合、受信側も同じ値 DATA_CREATED を指定します。decoder タグ
class 属性には、「データ変換クラス」のパッケージ名を含むクラス名(完全修飾子)を指定します。procedure タグ
class 属性には、「データ処理クラス」のパッケージ名を含むクラス名(完全修飾子)を指定します。コラム
「データ変換クラス」や「データ処理クラス」に渡すことができる独自のパラメータ文字列を指定することができます。指定は任意です。詳細は、「「データ変換クラス」「データ処理クラス」の初期パラメータを設定する 」を参照してください。
データを受信する¶
各資材をアプリケーションサーバ内に配置した後、受信対象のデータが送信されるよう操作を行ってください。正しく設定が行われていれば、「データ処理クラス」の onReceive メソッドが呼び出され、処理が行われます。例えばサンプル(SampleProcedure)の場合、受信したクラス内に含まれる url と resourceId の内容がコンソールに出力されます。送信操作を行っても正しく受信できない場合は、以下の点を確認してください。
- マッピング設定の記載が間違っていないか。
- 「送受信モデル(Generic)」が、送信側が定義するクラスから復元可能かどうか。
- 「データ変換クラス」が、AbstractDecoder クラスを継承しているか。
- 「データ処理クラス」が、AbstractProcedure クラスを継承しているか。
追加情報¶
「データ変換クラス」「データ処理クラス」の初期パラメータを設定する¶
「IM-Propagation 受信側設定」の decoder タグ内、および、procedure タグ内に params、param タグを記述することで、「データ変換クラス」や「データ処理クラス」に渡すことができる独自のパラメータ文字列を指定することができます。この設定により、同一の「データ変換クラス」や「データ処理クラス」で動的に変化させたいロジック・設定値がある場合などに、個別にクラスを分ける必要がなくなり、汎用性を向上させることができます。<?xml version="1.0" encoding="UTF-8"?> <propagation-receivers-config xmlns="http://www.intra-mart.jp/propagation/receivers-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intra-mart.jp/propagation/receivers-config propagation-receivers-config.xsd"> <receiver source="jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData" operationType="DATA_CREATED"> <decoder class="jp.co.intra_mart.module_name.function_name.propagation.decoder.SampleDecoder"> <params> <param key="no-resource-id">sampleValue1</param> <param key="no-url">sampleValue2</param> </params> </decoder> <procedure class="jp.co.intra_mart.module_name.function_name.propagation.procedure.SampleProcedure"> <params> <param key="update-keys">key1</param> <param key="update-keys">key2</param> </params> </procedure> </receiver> </propagation-receivers-config>設定が必要なタグとその説明は、以下の通りです。
params タグ
param タグの親タグです。このタグ内に複数の param タグを記述することで、複数のパラメータ値を引き渡すことができます。param タグ
key 属性にパラメータ文字列を取得するためのキー、タグ内にパラメータ文字列を記述してください。キーは重複可能で、取得時に同一のキーで指定された全てのパラメータ文字列を取得できます。キーやパラメータ文字列を取得する場合は、「データ変換クラス」と「データ処理クラス」内で super#getParamValue()、super#getParamValues()、super#getParamKeys() メソッドを使用してください。各メソッドについての詳細は、「APIドキュメント - AbstractDecoder 」「APIドキュメント - AbstractSessionableProcedure 」を参照してください。例えば、「データ変換クラス」上では以下のようにパラメータ値を取得して、処理を分岐できます。package jp.co.intra_mart.module_name.function_name.propagation.decoder; import java.net.MalformedURLException; import java.net.URL; import jp.co.intra_mart.foundation.propagation.exception.ConvertException; import jp.co.intra_mart.foundation.propagation.receiver.AbstractDecoder; public class SampleDecoder extends AbstractDecoder<SampleGenericData, ReceiverModuleData> { /** * 「送受信モデル」から「独自モデル」に変換するメソッド * @param generic 送受信モデル * @return 独自モデル * @throws 変換に失敗した場合 */ @Override public ReceiverModuleData decode(final SampleGenericData generic) throws ConvertException { try { // 送受信モデルから独自モデルに変換 final ReceiverModuleData data = new ReceiverModuleData(); if (generic.getResourceId() != null && !"true".equals(getParamValue("no-resource-id"))) { data.setResourceId(generic.getResourceId()); } if (generic.getUrl() != null && !"true".equals(getParamValue("no-url"))) { data.setUrl(new URL(generic.getUrl())); } // 独自モデルを返却 return data; } catch (final MalformedURLException e) { throw new ConvertException(e); } } /** * 「送受信モデル」のクラスを返却するメソッド * @return 送受信モデルのクラス */ @Override public Class<SampleGenericData> getGenericDataClass() { return SampleGenericData.class; } }「データ処理クラス」内でも同様に、すべてのメソッド内で使用できます。
データベース以外の操作を行う「データ処理クラス」の実装¶
データベースのトランザクション管理は IM-Propagation が自動的に行いますが、データベース以外のデータ操作を行う場合は、データの受信側が独自にトランザクション管理を行う必要があります。例えばファイル操作の場合、自身のモジュール側の処理が成功したとしても、他のモジュールで処理が失敗してしまった場合は、それまでの操作を戻す(ロールバック)処理が必要になります。このような独自のトランザクション処理を含む「データ処理クラス」を作成する際は、AbstractProcedure ではなく AbstractSessionableProcedure クラスを継承してください。AbstractSessionableProcedure クラスの総称型は、左から「独自モデル」、「処理結果を格納するクラス」の順にクラスタイプを指定してください。AbstractSessionableProcedure クラスを継承することで、onInitialize、onReceive、onPrepare、onDecide、および、onAbort メソッドの実装が必須となります。package jp.co.intra_mart.module_name.function_name.propagation.procedure; import jp.co.intra_mart.foundation.propagation.code.EventStatus; import jp.co.intra_mart.foundation.propagation.exception.ProcedureException; import jp.co.intra_mart.foundation.propagation.model.AbortParameter; import jp.co.intra_mart.foundation.propagation.model.AbortResult; import jp.co.intra_mart.foundation.propagation.model.DecideParameter; import jp.co.intra_mart.foundation.propagation.model.DecideResult; import jp.co.intra_mart.foundation.propagation.model.EmptyObject; import jp.co.intra_mart.foundation.propagation.model.InitializeParameter; import jp.co.intra_mart.foundation.propagation.model.InitializeResult; import jp.co.intra_mart.foundation.propagation.model.PrepareParameter; import jp.co.intra_mart.foundation.propagation.model.PrepareResult; import jp.co.intra_mart.foundation.propagation.model.ReceiveParameter; import jp.co.intra_mart.foundation.propagation.model.ReceiveResult; import jp.co.intra_mart.foundation.propagation.receiver.AbstractSessionableProcedure; public class SampleProcedure extends AbstractSessionableProcedure<ReceiverModuleData, EmptyObject> { /** * セッションが開始した際の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @throws ProcedureException 処理に失敗した場合 */ @Override public InitializeResult onInitialize(final InitializeParameter parameter) throws ProcedureException { // 一時データ格納領域の初期化 return new InitializeResult(EventStatus.NOT_AFFECTED); } /** * データを受信した際の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @param data 独自モデル * @throws ProcedureException 処理に失敗した場合 */ @Override public ReceiveResult<EmptyObject> onReceive(final ReceiveParameter parameter, final ReceiverModuleData data) throws ProcedureException { // onInitialize からの変更を一時データとして管理して、いつでもロールバックできるようにする return new ReceiveResult<EmptyObject>(EventStatus.NOT_AFFECTED); } /** * セッションが確定する前段階の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @throws ProcedureException 処理に失敗した場合 */ @Override public PrepareResult onPrepare(PrepareParameter parameter) throws ProcedureException { // トランザクションが確定してデータが更新できるかどうかを判定 // SUCCEEDED を返す場合は、次に onDecide または onAbort が呼び出されるまでロックをかけ、 // データの衝突が起こらないよう (onDecide で必ず成功するよう) 対処する return new PrepareResult(EventStatus.NOT_AFFECTED); } /** * セッションが確定した際の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @throws ProcedureException 処理に失敗した場合 */ @Override public DecideResult onDecide(final DecideParameter parameter) throws ProcedureException { // onPrepare が呼び出された時点での一時データを確定して本更新をかける return new DecideResult(EventStatus.NOT_AFFECTED); } /** * セッションが中断した際の処理を行うメソッド * @param parameter 受信時のパラメータ情報 * @throws ProcedureException 処理に失敗した場合 */ @Override public AbortResult onAbort(final AbortParameter parameter) throws ProcedureException { // onInitiaize から行われた一時データへの変更を破棄 return new AbortResult(EventStatus.NOT_AFFECTED); } }実装が必要なメソッドとその説明は、以下の通りです。
onInitialize メソッド
「データ処理クラス」の初期化処理を実装してください。メソッドの戻り値には、InitializeResult クラスのインスタンスを作成して返却します。onReceive メソッド
受信したデータを処理する内容を実装してください。メソッドの戻り値には、ReceiveResult クラスのインスタンスを作成して返却します。総称型には「処理結果を格納するクラス」を指定してください。onPrepare メソッド
「データ処理クラス」内でのデータ処理を確定する準備の処理を実装してください。メソッドの戻り値には、PrepareResult クラスのインスタンスを作成して返却します。このメソッドは、onDecide メソッドが呼び出される前に、コミットが可能かどうかの問い合わせに使用されます。onDecide メソッド
「データ処理クラス」内でのデータ処理を確定する(コミット)処理を実装してください。メソッドの戻り値には、DecideResult クラスのインスタンスを作成して返却します。onAbort メソッド
「データ処理クラス」内でのデータ処理を元に戻す(ロールバック)処理を実装してください。メソッドの戻り値には、AbortResult クラスのインスタンスを作成して返却します。例えば、ファイルに受信したデータを書き込む処理を実装する場合は、以下のような処理の流れになります。
onInitialize メソッドで、テンポラリファイルを書き込む準備を行います(ディレクトリの作成など)。 onReceive メソッドで、テンポラリファイルを作成し、受信したデータを書き込みます。 onPrepare メソッドで、他のスレッドから本番データファイルへの変更を阻止するためのロックを取得します。 onDecide メソッドで、テンポラリファイルの内容を本番データファイルへ書き込みます。その後、テンポラリファイルを破棄し、本番データファイルのロックを解除します。 onAbort メソッドで、テンポラリファイルを破棄します。本番データファイルのロックが行われている場合は、ロックを解除します。