intra-mart Accel Platform IM-Propagation プログラミングガイド 初版 2014-05-01

4. データを送る側の実装

用意する資材

送信側で用意する資材は、以下の通りです。
../../_images/sender_1.png
図 送信側の処理
表 用意する資材一覧
番号 資材名 準備必須 解説
1 送信するデータを格納するためのクラス(独自モデル)(図中の Data A 必須 送信するデータを格納するためのクラスを作成する
2 送受信モデル(Generic)(図中の Generic A 必須 送受信モデル(Generic)を作成する
3 データ変換クラス(図中の Encoder 必須 送信側のデータ変換クラス(Encoder)を作成する
4 マッピング設定(図中の Mapping Table 必須 マッピング設定を作成する
5 処理結果を格納するクラス(図中の Result A 任意 処理結果を格納するクラスを作成する

送信するデータを格納するためのクラスを作成する

最初に、送信するデータを格納するためのクラス(以下、「独自モデル」)を用意します。
package jp.co.intra_mart.module_name.function_name.propagation;

import java.net.URL;

public class SenderModuleData {

    /** リソース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;
    }

}
送信側から送ることができるデータを格納するクラスは1つだけです。
そのため、同時に送信したいデータのクラスが複数ある場合、1つのクラスにまとめてください。

注意

「独自モデル」のパッケージ名を含むクラス名(完全修飾子)が、データを伝搬する上でのキー情報の1つになります。
そのため、後から容易に変更できませんので、慎重に命名するようにしてください。
例えば、パッケージ名はモジュールや機能を示す名称、クラス名には送信するデータの内容を示す名称を指定してください。

送受信モデル(Generic)を作成する

次に、送信するデータを直列化するための「送受信モデル(Generic)」を作成します。
ただし、以下のいずれかに該当する場合は、このクラスを新規に作成する必要はありません。
  • 「独自モデル」が「送受信モデル(Generic)」の制約を満たすクラスの場合、「独自モデル」を流用できます。
    「送受信モデル(Generic)」の制約についての詳細は、「IM-Propagation 仕様書 」を参照してください。
  • 「独自モデル」に格納されているデータが IM-Propagation 内に定義されている「送受信モデル(Generic)」のいずれかのクラスに格納できる場合、定義済みのクラスを流用できます。
    定義されている「送受信モデル(Generic)」は、「APIドキュメント - jp.co.intra_mart.foundation.propagation.model.generic 」を参照してください。
新しい「送受信モデル(Generic)」を用意する場合は、AbstractGeneric クラスを継承して新しいクラスを作成してください。
「独自モデル」で定義されているプロパティのうち送信したいデータが全て格納でき、かつ、「送受信モデル(Generic)」の制約を満たすクラスを作成してください。
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 が必要です。
上記の値をそのまま使用せず、新しく採番してください。

送信側のデータ変換クラス(Encoder)を作成する

次に、「独自モデル」を「送受信モデル(Generic)」に変換する機能を提供するクラス(以下、「データ変換クラス」)を用意します。
「データ変換クラス」を作成する際は、AbstractEncoder クラスを継承してください。
AbstractEncoder クラスの総称型は、左から「独自モデル」、「送受信モデル(Generic)」の順にクラスタイプを指定してください。
AbstractEncoder クラスを継承することで、encodegetGenericDataClass メソッドの実装が必須となります。
package jp.co.intra_mart.module_name.function_name.propagation.encoder;

import jp.co.intra_mart.foundation.propagation.exception.ConvertException;
import jp.co.intra_mart.foundation.propagation.sender.AbstractEncoder;

public class SampleEncoder extends AbstractEncoder<SenderModuleData, SampleGenericData> {

    /**
     * 「独自モデル」から「送受信モデル」に変換するメソッド
     * @param data 独自モデル
     * @return 送受信モデル
     * @throws 変換に失敗した場合
     */
    @Override
    public SampleGenericData encode(final SenderModuleData data) throws ConvertException {
        // 入力チェック
        if (data.getResourceId() == null || data.getUrl() == null) {
            throw new ConvertException("Required parameters are null.");
        }

        // 独自モデルから送受信モデルに変換して返却
        final SampleGenericData generic = new SampleGenericData();
        generic.setResourceId(data.getResourceId());
        generic.setUrl(data.getUrl().toString());
        return generic;
    }

    /**
     * 「送受信モデル」のクラスを返却するメソッド
     * @return 送受信モデルのクラス
     */
    @Override
    public Class<SampleGenericData> getGenericDataClass() {
        return SampleGenericData.class;
    }

}
実装が必要なメソッドとその説明は、以下の通りです。
  • encode メソッド

    引数から渡された「独自モデル」を「送受信モデル(Generic)」に入れ替えて返却してください。
    「独自モデル」と「送受信モデル(Generic)」がまったく同じクラスの場合は、そのまま引数の値を返却することができます。
    データの入れ替えができない状況のときや、必要なデータが格納されていなかった場合は、ConvertException、または、ConvertException を継承した例外クラスをスローしてください。
  • getGenericDataClass メソッド

    「送受信モデル(Generic)」のクラスを返却してください。

マッピング設定を作成する

次に、ここまで作成した「独自モデル」「送受信モデル(Generic)」「データ変換クラス」を紐付けるためのマッピング設定を作成します。
ファイル名の最初にはモジュールIDを含めるなど、他のモジュールが提供している設定ファイルと衝突しないようにしてください。
マッピング設定は、以下のような形式で記述します。
設定ファイルリファレンス - IM-Propagation 送信側設定 」も合わせて参照してください。
パス WEB-INF/conf/propagation-senders-config/{任意のファイル名}.xml

<?xml version="1.0" encoding="UTF-8"?>
<propagation-senders-config xmlns="http://www.intra-mart.jp/propagation/senders-config"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.intra-mart.jp/propagation/senders-config propagation-senders-config.xsd">
  <sender source="jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData" operationType="DATA_CREATED">
    <encoder class="jp.co.intra_mart.module_name.function_name.propagation.encoder.SampleEncoder" />
  </sender>
</propagation-senders-config>

注意

ファイル名は、他のモジュールが提供しているものと重複しないようにするために、モジュールIDを接頭子にするなどの対策を行ってください。
例) sender_module-sample_data.xml
設定が必要なタグとその説明は、以下の通りです。
  • sender タグ

    source 属性には、「独自モデル」のパッケージ名を含むクラス名(完全修飾子)を指定します。
    operationType 属性には、データ変換対象の「データの操作種別」を指定します。
    指定可能な値の候補が IM-Propagation でいくつか用意されていますので、基本的には候補の中から使用してください。
    候補の一覧は「IM-Propagation 仕様書 」、および、「APIドキュメント - OperationType 」を参照してください。
    独自に「データの操作種別」を定義する場合は、必ずモジュールIDを接頭子とした文字列にしてください。
    例) "sender_module.DATA_UPLOADED"
  • encoder タグ

    class 属性には、「データ変換クラス」のパッケージ名を含むクラス名(完全修飾子)を指定します。

コラム

「データ変換クラス」に渡すことができる独自のパラメータ文字列を指定することができます。指定は任意です。
詳細は、「「データ変換クラス」の初期パラメータを設定する 」を参照してください。

処理結果を格納するクラスを作成する

最後に、受信側が処理した結果を受け取るための「処理結果を格納するクラス」を作成します。
「処理結果を格納するクラス」の指定は必須ですが、特に処理結果を受け取らない場合は、IM-Propagation で用意されている jp.co.intra_mart.foundation.propagation.model.EmptyObject クラスで代用できます。
新しい「処理結果を格納するクラス」を用意する場合は、「処理結果を格納するクラス」の制約を満たすクラスを作成してください。
「処理結果を格納するクラス」の制約についての詳細は、「IM-Propagation 仕様書 」を参照してください。
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 が必要です。
上記の値をそのまま使用せず、新しく採番してください。

データを送信する

データを送信するためには、IM-Propagation マネージャのインスタンスを取得します。
final PropagationManager manager = PropagationManagerFactory.getInstance().getPropagationManager();
PropagationManager についての詳細は、「APIドキュメント - PropagationManager 」を参照してください。
IM-Propagation は独自のセッション管理を行っており、データの送信が成功・失敗した場合は正しくセッションを終了させる必要があります。
セッション管理についての詳細は、「IM-Propagation 仕様書 」を参照してください。
操作手順は以下の通りです。
  1. begin メソッドを呼び出すことで、IM-Propagation のセッションが開始されます。
  2. send メソッドを呼び出すことで、データが送信されます。送信したいデータが複数ある場合は send メソッドをその都度呼び出します。
  3. データ送信後に decide メソッドを呼び出すことで、セッションを確定してコミット処理が行われます。
  4. 1 ~ 3 の処理中に何らかの例外が発生した場合は、例外処理を行います。
  5. 最終処理として abort メソッドを呼び出すことで、セッションが確実に終了されます。
    このとき decide メソッドがまだ呼び出されていない場合は、セッションが中断してロールバック処理が行われます。
final PropagationManager manager = PropagationManagerFactory.getInstance().getPropagationManager();
try {
    // セッションを開始
    manager.begin();

    // データを送信
    manager.send(OperationType.DATA_CREATED, SenderModuleData.class, data, EmptyObject.class);

    // セッションを確定
    manager.decide();
} catch (final BeginException e) {
    // manager.begin() に失敗した場合

} catch (final SendException e) {
    // manager.send() に失敗した場合

} catch (final DecideException e) {
    // manager.decide() に失敗した場合

} catch (final Exception e) {
    // その他例外が発生した場合

} finally {
    // セッションを中断。 manager.decide() が成功した場合は何もしない
    manager.abort();
}
特に例外処理を行わない場合は、クロージャ対応のメソッドを利用すると、実装を簡略化できます。
また、自身のモジュール内へデータを伝搬する目的で使用するなどの理由でセッション管理が不要な場合は、セッション関係の実装を省略できます。
詳細は、「セッション管理を使用せずにデータを送信する 」を参照してください。
受信側からの処理結果を取得する方法についての詳細は、「受信側から渡された処理結果を取得する 」を参照してください。

受信側へ情報を公開する

  • 「独自モデル」と「データの操作種別」を公開します。

    送信したデータを受信側が取得できるようにするため、送信する「独自モデル」の完全修飾子と「データの操作種別」の組み合わせ(マッピング設定)を公開してください。
  • 「送受信モデル(Generic)」と「処理結果を格納するクラス」を公開します。

    送信したデータを受信側が取得できるようにするため、「送受信モデル(Generic)」の仕様を公開してください。
    また、処理結果を受信側から受け取る場合は、受信側が処理結果を返却できるようにするため、「処理結果を格納するクラス」の仕様を公開してください。
    将来、送受信するデータの内容を変更するためにこれら2つのクラスの仕様を変更する場合は、既存のパラメータを削除したり、設定する値を変えたりすることは、できる限り避けてください。
    後からパラメータを追加することは、問題ありません。
公開例:
  • 「独自モデル」の完全修飾子

    jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData

  • データ受信のための送受信モデル

    jp.co.intra_mart.module_name.function_name.propagation.generic.SampleGenericData

  • 送信側に返却するための処理結果モデル

    jp.co.intra_mart.module_name.function_name.propagation.result.ProcResult

    パラメータ 設定値
    success
    リソースIDとURLのマッピングの登録を承認する場合は true
    否認する場合は false
  • データの操作種別とデータ送信のタイミング

    データの操作種別 データ送信のタイミング
    DATA_CREATED リソースIDとURLのマッピングが登録された。

追加情報

「データ変換クラス」の初期パラメータを設定する

「IM-Propagation 送信側設定」の encoder タグ内に paramsparam タグを記述することで、「データ変換クラス」に渡すことができる独自のパラメータ文字列を指定することができます。
この設定により、同一の「データ変換クラス」で動的に変化させたいロジック・設定値がある場合などに、個別にクラスを分ける必要がなくなり、汎用性を向上させることができます。
<?xml version="1.0" encoding="UTF-8"?>
<propagation-senders-config xmlns="http://www.intra-mart.jp/propagation/senders-config"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.intra-mart.jp/propagation/senders-config propagation-senders-config.xsd">
  <sender source="jp.co.intra_mart.module_name.function_name.propagation.SenderModuleData" operationType="DATA_CREATED">
    <encoder class="jp.co.intra_mart.module_name.function_name.propagation.encoder.SampleEncoder">
      <params>
        <param key="no-resource-id">false</param>
        <param key="no-url">true</param>
      </params>
    </encoder>
  </sender>
</propagation-senders-config>
設定が必要なタグとその説明は、以下の通りです。
  • params タグ

    param タグの親タグです。
    このタグ内に複数の param タグを記述することで、複数のパラメータ値を引き渡すことができます。
  • param タグ

    key 属性にパラメータ文字列を取得するためのキー、タグ内にパラメータ文字列を記述してください。
    キーは重複可能で、取得時に同一のキーで指定された全てのパラメータ文字列を取得できます。
    キーやパラメータ文字列を取得する場合は、「データ変換クラス」内で super#getParamValue()super#getParamValues()super#getParamKeys() メソッドを使用してください。
    各メソッドについての詳細は、「APIドキュメント - AbstractEncoder 」を参照してください。
例えば、「データ変換クラス」上では以下のようにパラメータ値を取得して、処理を分岐できます。
package jp.co.intra_mart.module_name.function_name.propagation.encoder;

import jp.co.intra_mart.foundation.propagation.exception.ConvertException;
import jp.co.intra_mart.foundation.propagation.sender.AbstractEncoder;

public class SampleEncoder extends AbstractEncoder<SenderModuleData, SampleGenericData> {

    /**
     * 「独自モデル」から「送受信モデル」に変換するメソッド
     * @param data 独自モデル
     * @return 送受信モデル
     * @throws 変換に失敗した場合
     */
    @Override
    public SampleGenericData encode(final SenderModuleData data) throws ConvertException {
        // 入力チェック
        if (data.getResourceId() == null || data.getUrl() == null) {
            throw new ConvertException("Required parameters are null.");
        }

        // 独自モデルから送受信モデルに変換
        // 属性値によってパラメータに設定するかどうかを決める
        final SampleGenericData generic = new SampleGenericData();
        if (!"true".equals(getParamValue("no-resource-id"))) {
            generic.setResourceId(data.getResourceId());
        }
        if (!"true".equals(getParamValue("no-url"))) {
            generic.setUrl(data.getUrl().toString());
        }

        // 送受信モデルを返却
        return generic;
    }

    /**
     * 「送受信モデル」のクラスを返却するメソッド
     * @return 送受信モデルのクラス
     */
    @Override
    public Class<SampleGenericData> getGenericDataClass() {
        return SampleGenericData.class;
    }

}

クロージャ対応のメソッドを使用してデータを送信する

クロージャ対応の execute メソッドを使用した場合は、セッション管理系のメソッド(send 以外)を呼び出さずに、セッション管理を自動化することができます。
IM-Propagation のセッション管理、および、データベースのトランザクション制御は、IM-Propagation 側が行います。
特に細かな例外処理が求められない場合、この方法が最も簡単です。
Callable#call() メソッドで任意のオブジェクトのインスタンスが返却された場合は、decide メソッドを呼び出して自動的にセッションを確定します。
null が返却された、または、例外が発生した場合は、abort メソッドを呼び出して自動的にセッションを中断します。
Callable#call() メソッドで返却された値が、このメソッドの戻り値としてそのまま返却されます。
final PropagationManager manager = PropagationManagerFactory.getInstance().getPropagationManager();
try {
    // データを送信
    manager.execute(new Callable<SendResult<EmptyObject>>() {
        @Override
        public SendResult<EmptyObject> call() throws Exception {
            return manager.send(OperationType.DATA_CREATED, SenderModuleData.class, data, EmptyObject.class);
        }
    });
} catch (final PropagationManagerException e) {
    // |common_im_propagation| で失敗した場合

} catch (final Exception e) {
    // その他例外が発生した場合

}

注意

Callable#call() メソッドの戻り値に null を返却した場合は、セッションが中断されますので注意してください。
特に返却するものがない場合でも、何らかのオブジェクトのインスタンス(例えば、SendResult)を返却してください。

セッション管理を使用せずにデータを送信する

IM-Propagation のセッション管理が不要で単純にデータを伝播したい場合は、begindecideabort メソッドの呼び出しを省略することができます。
この場合、リソースを解放するために最終処理として close メソッドを呼び出してください。
final PropagationManager manager = PropagationManagerFactory.getInstance().getPropagationManager();
try {
    // データを送信
    manager.send(OperationType.DATA_CREATED, SenderModuleData.class, data, EmptyObject.class);
} catch (final SendException e) {
    // manager.send() に失敗した場合

} catch (final Exception e) {
    // その他例外が発生した場合

} finally {
    // 処理を終了
    manager.close();
}

注意

この実装が有効なケースは、以下のいずれかが保証されている場合に限られますので注意してください。
  • データの受信側がセッションの開始(データベースのトランザクション、または、独自のセッション管理)を要求していない場合。
  • 自分のモジュール内へデータを伝搬する目的で使用する場合(他のモジュールへデータを伝搬しない場合)。
  • この実装の外側で IM-Propagation のセッション管理が行われている場合。

受信側から渡された処理結果を取得する

受信側から渡された処理結果を取得する場合の操作手順は、以下の通りです。
  1. send メソッドの第4引数に、処理結果を格納するクラスを指定します。
  2. send メソッドの戻り値(SendResult クラスのインスタンス)を取得します。
  3. SendResult#getResponses() メソッドから、全ての受信側が返却した処理結果を取得します。
SendResult<ProcResult> result = manager.send(OperationType.DATA_CREATED, SenderModuleData.class, data, ProcResult.class);
for (ProcResult response : result.getResponses()) {
    System.out.print(response.isSuccess());
}
受信側が処理結果を返さなかった場合、または、明示的に null を返した場合、SendResult#getResponses() メソッドから取得された一覧には含まれません(一覧内に null は含まれません)。