Invoking PCS process as soon as storing files or folders to DoCS (2)

前回はDoCSとPCSの標準機能でDoCS着信後にPCSのプロセスを動作させるユースケースを考えました。

Invoking PCS process as soon as storing files or folders to DoCS (1)

今回は、APIを使ってDoCSのフォルダをチェックし、ファイルやフォルダが存在すればPCSを起動する、という流れを考えます。

DoCS、PCSともREST APIが提供されており、しかも呼び出し回数で課金されることは(現在のところ)ないので、使わずにおくのは非常に勿体ないと考えます。今回作成したのは、単なるコマンドライン・アプリケーションであって、Java EEアプリケーションサーバ上での動作を意図したものではありませんが、同じ考え方で実装できるはずです(スレッド管理はコンテナに任せなければなりませんが)。

流れは、次のような感じです。

  1. DoCSの監視対象フォルダを定期的に監視する
    1. ファイルやフォルダがなければ、次回の監視タイミングまで待機
    2. ファイルやフォルダがあれば、呼び出したいPCSのプロセスを起動する
      1. プロセス起動時の戻り値としてインスタンスIDが返るので、これを使って、プロセスインスタンス固有のフォルダを探索する
      2. 探索したフォルダに、検知したファイルもしくはフォルダを移動する

今回は、DoCSフォルダを監視するスレッドを一つ、PCSプロセスを起動し、フォルダの内容を移動するスレッドを複数作成しました。もちろん、フォルダの内容を移動するスレッドを独立させても構わないのですが、今回は簡単のため分割しませんでした。

DoCSのフォルダ監視

DoCS REST API Referenceは以下の場所からアクセスできます。

Oracle Documents Cloud Service REST API Reference
https://docs.oracle.com/cloud/latest/documentcs_welcome/WCCCD/GUID-205EBBB4-F8D5-4FD0-B82F-210960F5C6F7.htm#WCCCD3724

まずはフォルダの内容を確認するAPIを使い、ファイルやフォルダの着信を検知します。

WebTarget myResource = client.target(DOCS_TARGET_URL).path("/api/1.1/folders/{id}/items");
Response response = myResource.resolveTemplate("id", _id).request(MediaType.APPLICATION_JSON).get(Response.class);

// Check HTTP Status
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
    response.close();
}
else {
    // Get object Tree for returning a JsonObject
    try (JsonReader reader = Json.createReader(new StringReader(response.readEntity(String.class)))) {
    response.close();
    docsJSONobj = reader.readObject();
}

この docsJSONobj には、フォルダ中のコンテンツの個数、コンテンツの属性(フォルダなのかファイルなのか)、その他の情報が含まれています。監視対象のフォルダにファイルもしくはフォルダがあれば、PCSプロセス起動スレッドに通知するため、Dequeにデータを入れました。今回はアプリケーションを安全に停止することを考慮し、タイムアウト設定が可能なLinkedBlockingDequeを使っています。入側(監視スレッド)からはタイムアウト無し、出側(PCSプロセス起動スレッド)はタイムアウト有りで取得するようにしています。

ただこれだけでは、PCSプロセス起動~コンテンツ移動完了までに同じコンテンツに対する操作が複数回走る可能性があるので、docsJSONobjに含まれるitemIdをキーとし、オブジェクトをConcurrentHashMapに格納し、これをインターロックのために利用することにしました(本当はHashSetに格納するだけで済むのですが、マルチスレッド環境で利用するためのConcurrentHashSetは存在しないので…)。処理の大きな流れは次のような構成です。

  1. Mapに値を入れる
  2. Dequeに値を入れる
  3. Dequeから取り出す
  4. PCSプロセスを起動し、コンテンツを移動する
  5. Mapから値を取り除く

PCSプロセスの起動

これもREST APIから呼び出します。インターフェース要素として、様々なメッセージを取ることができますが、今回は前回のエントリと同様、コンテンツのitemId、コンテンツ名、ユーザIDを渡すことにしました。

PCS REST API Referenceは以下の場所からアクセスできます。

REST API for Oracle Process Cloud Service
https://docs.oracle.com/cloud/latest/process_gs/CPRRA/

また、プロセス起動時には、前回のエントリに書いたプロセス定義IDとオペレーション名が必要です。DoCSに標準で備わっている機能であれば、 “operation” が “start” に固定されていますが、今回はオペレーション名の制限はありませんので、以下のコードのように “ProcessStart” としても動作します。

// timeout setting is enabled at delivery side
MovingItemParam param = queue.pollFirst(1L, TimeUnit.SECONDS);
// create a message for PCS REST API in JSON format
JsonObject postObj = Json.createObjectBuilder()
                         .add("processDefId", PROCESS_DEFINITION_ID)
                         .add("operation", "ProcessStart")
                         .add("params", Json.createObjectBuilder()
                             .add("docId", param.getItemId())
                             .add("docName", param.getItemName())
                             .add("userId", param.getUserId())
                         )
                         .build();

WebTarget myResource = client.target(PCS_TARGET_URL).path("/api/3.0/processes");
response = myResource.request()
                    .buildPost(Entity.entity(postObj.toString(), MediaType.APPLICATION_JSON))
                    .invoke(Response.class);

if (response.getStatus() == Response.Status.OK.getStatusCode()) {

    // Parsing Response Message
    try ( JsonReader reader = Json.createReader( new StringReader( response.readEntity( String.class ) ) ) ) {
        response.close();
        JsonObject responseJSONobj = reader.readObject();
        // Confirm whether process state is "OPEN"
        if (responseJSONobj.getString("state").equalsIgnoreCase("OPEN")) {
            String instanceId = responseJSONobj.getString("processId");

            // Moving a content in monitored folder
            moveItem(instanceId, param.getItemType(), param.getItemId());

            // When completed, remove the itemid from Map
            map.remove(param.getItemId());
        }
    }
}

監視対象フォルダからインスタンス固有フォルダへのコンテンツの移動

上記コードの最後のあたりにある、moveItem()メソッドで、コンテンツをPCSプロセスインスタンス固有のフォルダに移動しています。このメソッドでは、

  • プロセスインスタンス固有のフォルダが存在することを確認(_filterNameを使って、PCSプロセスで利用しているDoCSフォルダ名を抽出)
  • フォルダがあれば、そのフォルダ内に監視対象フォルダ内のコンテンツを移動

という流れで処理をしています。
コンテンツがファイルの場合とフォルダの場合でリソース名が変わるので、注意が必要です。

WebTarget myResource = client.target(DOCS_TARGET_URL).path("/api/1.1/folders/{id}/items");
String _filterName = "PCSProcess_Instance_" + _instanceId;
Response response = myResource.resolveTemplate("id", _id)
        .queryParam("filterName", _filterName)
        .request(MediaType.APPLICATION_JSON)
        .get(Response.class);
// Check HTTP Status
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
    response.close();
}
else {
    // Get object Tree for returning a JsonObject
    try (JsonReader reader = Json.createReader(new StringReader(response.readEntity(String.class)))) {
    response.close();
    docsJSONobj = reader.readObject();
}

...
/**
 * Moving contents from monitored folder to instance-specific folder
 **/
// Folder or File
switch (itemType) {
case "file":
    myResource = client.target(DOCS_TARGET_URL).path("/api/1.1/files/{id}/move");
    break;
case "folder":
    myResource = client.target(DOCS_TARGET_URL).path("/api/1.1/folders/{id}/move");
    break;
default:
    return false;
}

// Destination (JSON format)
JsonObject putObj = Json.createObjectBuilder()
                        .add("destinationID", _destinationId)
                        .build();

Response response =
    myResource.resolveTemplate("id", _itemId)
              .request()
              .buildPost(
                  Entity.entity(
                      putObj.toString(),
                      MediaType.APPLICATION_JSON
                  )
              )
              .invoke(Response.class);

// Check HTTP response
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
    response.close();
}
...

なお、PCSプロセス起動スレッドの個数、DoCS、PCSへの認証情報は外部に切り出すようにしています。所々大文字の変数名があるのはそのためです。

anishi1222 について

とあるキャラクターの中の人です。
カテゴリー: Java, Middleware タグ: , , パーマリンク

Invoking PCS process as soon as storing files or folders to DoCS (2) への1件のフィードバック

  1. ピンバック: Invoking PCS process as soon as storing files or folders to DoCS (3) | Troubadour

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中