日別アーカイブ: 2019年5月14日

Logic Appsの相関

これは2019/05/14現在の備忘録であり、今後機能変更や廃止される可能性があります。

相関ID

相関ID (correlation ID) はすべての呼び出しに付いています。Azure Portalで見ると、実行履歴の識別子が相関IDです。

一方のロジックアプリから他方のロジックアプリを呼び出す場合、ロジックアプリ呼び出しのコネクタを使うと、親ロジックアプリの相関IDでトラッキングできます。子ロジックアプリにはx-ms-client-tracking-idとして親ロジックアプリの相関IDが渡されます。以下は親ロジックアプリから子ロジックアプリを呼び出した際に出力結果として取得できるJSONです。

{
  "statusCode": 200,
  "headers": {
    "x-ms-workflow-run-id": "08586454326920836835265445460CU03",
    "x-ms-correlation-id": "426de7b3-f342-4d1c-b22b-2b5e70a9fa7f",
    "x-ms-client-tracking-id": "08586454326921754734898813794CU09",
    "x-ms-trigger-history-name": "08586454326920836835265445460CU03",
    "x-ms-execution-location": "japaneast",
    "x-ms-workflow-id": "f9ab65abfb5f451d96202204e9d57821",
    "x-ms-workflow-version": "08586454328613030885",
    "x-ms-workflow-name": "logicapp-child",
    "x-ms-workflow-system-id": "/locations/japaneast/scaleunits/prod-26/workflows/f9ab65abfb5f451d96202204e9d57821",
    "x-ms-tracking-id": "598bee1f-6153-46e1-b70f-c48bbb452dcd"
  }
}

子ロジックアプリの入力を確認すると、以下の内容がHeaderとして渡っていて、呼び出し元のx-ms-client-tracking-idの値が呼び出し先でも確認できます(x-ms-workflow-run-idとしても確認できます)。

{
  "headers": {
    "Accept-Language": "ja",
    "User-Agent": "azure-logic-apps/1.0,(workflow 6e1eef65db9b49fe9efd155c6b13330f; version 08586454327017350370)",
    "x-ms-execution-location": "japaneast",
    "x-ms-workflow-id": "6e1eef65db9b49fe9efd155c6b13330f",
    "x-ms-workflow-version": "08586454327017350370",
    "x-ms-workflow-name": "logicapp-test",
    "x-ms-workflow-system-id": "/locations/japaneast/scaleunits/prod-25/workflows/6e1eef65db9b49fe9efd155c6b13330f",
    "x-ms-workflow-run-id": "08586454326921754734898813794CU09",
    "x-ms-workflow-run-tracking-id": "c1fa76ed-bc77-4924-8fad-ce5f3b092b53",
    "x-ms-workflow-operation-name": "logicapp-child",
    "x-ms-workflow-subscription-id": "8b45cdb2-bc55-4605-9e2d-09ef038fcbf3",
    "x-ms-workflow-resourcegroup-name": "LogicApps",
    "x-ms-tracking-id": "8b07200c-3b54-4e44-b22b-e8b34ae9331b",
    "x-ms-correlation-id": "8b07200c-3b54-4e44-b22b-e8b34ae9331b",
    "x-ms-client-request-id": "8b07200c-3b54-4e44-b22b-e8b34ae9331b",
    "x-ms-client-tracking-id": "08586454326921754734898813794CU09",
    "x-ms-action-tracking-id": "c1430f0e-c14c-49c5-85ea-80d6ed2004f1",
    "x-ms-activity-vector": "IN.08"
  }
}

呼び出し時にx-ms-tracking-client-idをHTTP Headerに指定すると、その値を使ってインスタンスを一意に識別できることがわかります。例えばx-ms-client-tracking-idにlogicapp-20190425を指定した場合、以下のように関連付けID(英語ではCorrelation ID)で確認できます。

カスタム値を相関IDとして利用する

Logic Appのインスタンスを確認する際、通常はシステム固有のIDでトラッキングしますが、システムでユニークな値を使いEnd-to-Endに確認するような仕組みを既に有している場合、その仕組みで生成されるIDを使ってインスタンス実行状況の確認をしたいというニーズがあります。また、業務上ユニークになり得る値を使って実行状況を確認したいというニーズもあります。そうしたニーズに対応するための仕組みがLogic Appsにはあります。

相関IDにカスタムヘッダー値を使う

現時点ではCode Viewを使ってのみ設定可能です(Designerでもカスタムの追跡IDで確認できますが、設定そのものはCode Viewを使うしか手がありません)。

Designerでは入力できるけれど…

Code Viewでtriggers.manual.correlationオブジェクトを作成し、その中でclientTrackingIdを設定します。以下の例では、x-my-custom-correlation-idをカスタムヘッダーとして設定し、その値を使っています。

"triggers": {
  "manual": {
    "correlation": {
    "clientTrackingId": "@{coalesce(triggerOutputs().headers?['x-my-custom-correlation-id'], guid())}"
  },
  "inputs": {
    "method": "POST",
    "relativePath": "/v1/test",
    "schema": {}
  },
  "kind": "Http",
  "type": "Request"
}

相関IDにメッセージ本体の値を使う

triggerbody関数を使います。triggerbodyのスキーマは以下の通りです。

{
  "properties": {
    "message": {
      "type": "string"
    },
    "txid": {
      "type": "string"
    }
  },
  "type": "object"
}

以下のようなメッセージを例にします。

{
    "txid": "123456abcdef",
    "message": "Hello World!"
}

以下の例では、txidをcorrelation内のclientTrackingIdに設定し、x-ms-client-tracking-idとして利用できるようにしています。

"triggers": {
  "manual": {
    "correlation": {
      "clientTrackingId": "@{triggerBody()?['txid']}"
    },
    "inputs": {
      "method": "POST",
      "relativePath": "/v1/test",
      "schema": {
        "properties": {
          "message": {
            "type": "string"
          },
          "txid": {
            "type": "string"
          }
        },
        "type": "object"
      }
    },
    "kind": "Http",
    "type": "Request"
  }
}

Jakarta EE 8に関するFAQ

このエントリは以下のエントリをベースにしています。
This entry is based on the following one written by Mike Milinkovich.
Frequently Asked Questions About Jakarta EE 8
https://eclipse-foundation.blog/2019/05/08/jakarta-ee-8-faq/

Mike Milinkovichは先週の自身のエントリに対する反応に対して、再度 Jakarta EE Steering Committeeの一致した見解を表すとともに、Jakarta EEの最初のリリースであるJakarta EE 8(Java EE 8完全互換、javaxパッケージ名前空間を使用)に関する質問に答えることを目的として、上記エントリを発表しました。

Update on Jakarta EE Rights to Java Trademarks
https://eclipse-foundation.blog/2019/05/03/jakarta-ee-java-trademarks/

なお、このエントリに記載している内容は、名前空間が変わる将来のJakarta EEのリリースに関するものではありません。現在jakarta-platform-devメーリングリストで活発な議論が行われていますので、興味がある方はご参加ください。現時点では、Jakarta EE Spec CommitteeがJakarta EEロードマップの次のステップを決定するまでに約1ヶ月かかると見込んでいます。

Mailing list: jakartaee-platform-dev
https://accounts.eclipse.org/mailing-list/jakartaee-platform-dev
アーカイブ
https://www.eclipse.org/lists/jakartaee-platform-dev/

Will Jakarta EE 8 break existing Java EE applications that rely upon javax APIs? (Jakarta EE 8はjavax APIを使う既存のJava EEアプリケーションを壊そうとしている?)

いいえ、Jakarta EE 8は、javaxパッケージ名前空間のAPIに依拠している既存のJava EEアプリケーションを壊すことはありません。Jakarta EE 8はJava EE 8の完全互換を目指してします。Jakarta EE 8はJava EE8で指定されているのと同じjavax名前空間、同じjavax API、同じ動作が指定されるはずです。Java EE 8のTCKをパスした実装は、Jakarta EE 8のTCKにもパスするでしょう。なぜならJakarta EE 8のTCKはJava EE 8のTCKと同じソースを基にしているからです。Jakarta EE 8はJava EE 8アプリケーションやjavax APIの利用に対して変更を加える必要はありません。

What will Jakarta EE 8 consist of? (Jakarta EE 8の構成要素)

Jakarta EE 8仕様は以下のようなもので構成されるでしょう。

  • Java EE 8仕様完全互換
  • 同じjavaxパッケージ名前空間を使う、同じAPIそしてJavadocを含む
  • オープンソースのライセンス下でJakarta EE 8 TCKを提供。このTCKはJava EE 8 TCKをベースにしており、完全な互換性がある
  • Jakarta EE 8 Platform仕様を含む。これにはJava EE 8 Platformと同じプラットフォーム統合要件が記載されている。
  • Jakarta EE 8仕様がリリースされたタイミングで、Jakarta EE 8 Platformの複数の互換実装(Compatible Implementations) に言及
  • 実装がJakarta EE 8互換であることを説明するための互換性ならびにブランディングプロセスを提供

Will there be Jakarta EE 8 compatible implementations? (Jakarta EE 8互換実装はあるか?)

はい。Jakarta EE 8仕様がリリースされるタイミングで、Jakarta EE 8 Platformの複数の互換実装が利用可能になる予定です。すべてのJava EE 8互換実装もまたJakarta EE 8互換実装であり、Jakarta EE 8 Working Groupに属するベンダーは自社のJava EE 8互換実装をJakarta EE 8互換として認定する予定です。さらに、Jakarta EE TCKはオープンソースのライセンス下で入手できるので、他のテクノロジーベンダーが自身の実装をJakarta EE互換と実証するためのハードルを下げる予定です。より低コストかつより自由なJakarta EEの商標ライセンスにより、 多くのテクノロジープロバイダーがEnterprise JavaコミュニティにおけるJakarta EEブランドを活用し強化できます。Jakarta EE 8はオープンでベンダーニュートラルなコミュニティ主導のプロセスの下、Jakarta EEテクノロジーの進化のための新たなベースラインを提供します。

What is the process for delivery of Jakarta EE 8 (Jakarta EE 8のデリバリに関するプロセス)

Jakarta EE 8仕様のデリバリプロセスは完全に透明性があり、Jakarta EE Specification Processに完全に従います。 Java EE8コンポーネント仕様に対応した最初のJakarta EE8コンポーネント仕様のドラフトが、数週間以内に提供される予定で、これらには、関連するAPIを定義するJavadocと、互換性テスト用のTCKが含まれます。仕様文書を公開するには、この文書の著作権ライセンスを取得する必要がありますが、Eclipse Foundationでは、OracleとIBMからの貢献により、両社の著作権ライセンスを取得しています。できるだけ多くのコンポーネント仕様を公開するため、Jakarta EE 8 Platform仕様文書と、必要な残りの著作権ライセンスも取得するつもりです。過去にJCPでJava EE仕様に貢献したことがあれば、今後Jakarta EEでその貢献を利用するためのライセンスを提供をお願いするためにEclipse Foundationからご連絡することになるでしょう。このようなライセンス提供は、新しい仕様プロセスとJakarta EEコミュニティをサポートする上で重要なステップとなるでしょう。これらのドラフト仕様は、オープン・コミュニティ・プロセスにて最終仕様に発展します。ぜひ仕様プロジェクトに参加してください。

When will Jakarta EE 8 be delivered? (Jakarta EE 8のリリース時期)

Jakarta EE Working Groupは2019年秋までにJakarta EE 8仕様の最終版をリリースしたいと考えています。これはオープンなコミュニティ主導の取り組みであるため、Jakarta EE仕様の推進、Jakarta EE 8 TCKのデリバリ、Jakarta EE 8互換実装というプロセスには透明性があります。

Cosmos DB Binding for Azure Functions

このエントリは2019/05/13現在の備忘録であり、以下のドキュメントの内容に対するメモです。将来この機能や設定方法が変わる可能性はあります。対象はFunction 2.xです。また、以下のコード例はいずれもJavaで記述しています。

Azure Functions 2.x の Azure Cosmos DB バインド
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-cosmosdb-v2

Functionsをローカル環境でテストする場合(http://localhost:7071/…)、以下の前提条件を満たしておく必要があります。

Azure Functions バインド拡張機能を登録する
ローカル開発の Azure Functions Core Tools
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-register#local-development-azure-functions-core-tools

このドキュメントには記載がありませんが、実際にはService Fabric SDKも必要です。必ずダウンロードしてインストールしておく必要があります。

Windows で開発環境を準備する(OS X、Linuxでの開発の場合は、下記URLでドロップダウンボックスでLinux、OS Xを選択します)
https://docs.microsoft.com/ja-jp/azure/service-fabric/service-fabric-get-started

あと、Cosmos DBへの接続のための接続文字列は、ローカルテスト環境とAzure上では設定箇所が違うのでご注意を。

  • ローカルテスト環境:local.settings.json
  • 本番環境:Application settings > Connection strings

Trigger

Azure Cosmos DB のトリガーは Azure Cosmos DB 変更フィードを使用して、パーティション間の挿入と更新をリッスンします。 変更フィードは、削除ではなく挿入と更新を発行します。

Azure Functions 2.x の Azure Cosmos DB バインド – トリガー
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-cosmosdb-v2#trigger

変更されたドキュメントは変更順にリストとして出力されます。@CosmosDBTriggerをアノテーションとして付加したFunctionが定期的に変更リストをポーリングしており、リストに追加があれば、そのFunctionsのロジックが動作します。

package cosmos2functions;

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.annotation.CosmosDBTrigger;
import com.microsoft.azure.functions.annotation.FunctionName;

import java.util.List;

public class Function {
    @FunctionName("cosmosDBMonitor")
    public void cosmosDbLog(
            @CosmosDBTrigger(
                    name = "CosmosDB2Functions",
                    databaseName = "database",
                    collectionName = "Items",
                    leaseCollectionName = "Leases",
                    createLeaseCollectionIfNotExists = true,
                    connectionStringSetting = "AzureCosmosDBConnection")
                    List<String&gt; items,

            final ExecutionContext context
    ) {
        context.getLogger().info(items.size() + " item(s) is/are inserted.");
        if (!items.isEmpty()) {
            items.stream().forEach(s -&gt;context.getLogger().info("Element: " + s));
        }
    }
}

取得できるのは変更された結果のドキュメントです。Stringとして取得してJSONObjectに変換すれば、変更データを取得できます。

なお、アノテーションで設定するプロパティは以下のドキュメントに記載があります。

CosmosDBTrigger Interface
https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation.CosmosDBTrigger?view=azure-java-stable

ドキュメントに記載のないデフォルト値は以下の通りです。

プロパティデフォルト値
name
dataType“”
databaseName
collectionName
leaseConnectionStringSetting“”
leaseCollectionName“”
leaseDatabaseName“”
createLeaseCollectionIfNotExistsfalse
leasesCollectionThroughput-1
leaseCollectionPrefix“”
checkpointInterval-1
checkpointDocumentCount-1
feedPollDelay5000
connectionStringSetting
leaseRenewInterval17000
leaseAcquireInterval13000
leaseExpirationInterval60000
maxItemsPerInvocation-1
startFromBeginningfalse
preferredLocations“”

Input

SQL API を使用して 1 つ以上の Azure Cosmos DB ドキュメントを取得して関数の入力パラメーターに渡します。ドキュメント ID またはクエリ パラメーターは、関数を呼び出したトリガーに基づいて決定することができます。

Azure Functions 2.x の Azure Cosmos DB バインド – 入力
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-cosmosdb-v2#input

動的な条件設定のために、Query ParameterもしくはPath Parameterで渡す値を指定できます。ドキュメント上はPath Parameterをルートデータと表現しています。Cosmos DBからPOJOもしくはStringとしてデータを取得できます。

Query Parameter (+ SQL Query)

Query Parameter(以下の例ではaddress)をFunctionに渡しています。Function内では、指定されたAddressをwhere句に指定するSQL Queryを発行し、結果をPOJOとして取得、HTTP Responseとして返しています。

このFunctionには、 <Function App URL>/api/cosmosDBInputMonitor でアクセスできます。

// Itemというクラスを作成しておく

@FunctionName("cosmosDBInputMonitor")
public HttpResponseMessage cosmosdbQuery(
        @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String&gt;&gt; request,
        @CosmosDBInput(
                name = "CosmosDB2Functions",
                databaseName = "database",
                collectionName = "Items",
                partitionKey = "{Query.address}",
                sqlQuery = "select * from Items r where contains(r.address, {address})",
                connectionStringSetting = "AzureCosmosDBConnection")
                Item[] items,
        final ExecutionContext context
) {
    if (Optional.of(items).isPresent()) {
        return request.createResponseBuilder(HttpStatus.OK)
                .header("Content-Type", "application/json")
                .body(items)
                .build();
    } else {
        return request.createResponseBuilder(HttpStatus.NOT_FOUND)
                      .body("{\"error\":\"Not found\"}")
                      .build();
    }
}

Path Parameter (+ SQL Query)

以下の例はPath Parameterで指定する例です。この例では、

route = items/{address}

と設定しているため、このFunctionには、 <Function App URL>/api/items/{address} でアクセスできます。

// Itemというクラスを作成しておく

@FunctionName("cosmosDBInputMonitor2")
public HttpResponseMessage cosmosdbQuery2(
        @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET},
                authLevel = AuthorizationLevel.ANONYMOUS,
                route = "items/{address}")
                HttpRequestMessage<Optional<String&gt;&gt; request,
        @CosmosDBInput(
                name = "CosmosDB2Functions",
                databaseName = "database",
                collectionName = "Items",
                sqlQuery = "select * from items r where contains(r.address, {address})",
                connectionStringSetting = "AzureCosmosDBConnection")
                Items[] items,
        final ExecutionContext context
) {
    if (Optional.of(items).isPresent()) {
        return request.createResponseBuilder(HttpStatus.OK)
                .header("Content-Type", "application/json")
                .body(items)
                .build();
    } else {
        return request.createResponseBuilder(HttpStatus.NOT_FOUND)
                      .body("{\"error\":\"Not found\"}")
                      .build();
    }
}

アノテーションで設定するプロパティは以下のドキュメントに記載があります。

CosmosDBInput Interface
https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation.cosmosdbinput?view=azure-java-stable

ドキュメントに記載のないデフォルト値は以下の通りです。

プロパティデフォルト値
name
dataType“”
databaseName
collectionName
id“”
sqlQuery“”
connectionStringSetting
partitionKey“”

Output

Azure Cosmos DB 出力バインドを使用すると、SQL API を使って Azure Cosmos DB データベースに新しいドキュメントを記述できます。

Azure Functions 2.x の Azure Cosmos DB バインド – 入力
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-cosmosdb-v2#output

@CosmosDBOutput アノテーションをつける場合、

  • Functionの戻り値をCosmos DBに格納することを意図している場合は、メソッドに注釈をつける
  • Outputバインディングを使ってCosmos DBに格納することを意図している場合は、OutputBindingクラスインスタンスに注釈をつける

という注意事項があります。

戻り値を格納する

POSTで複数の項目を入れて、Echo backするようなFunctionです。このFunctionの戻り値をCosmos DBに格納しています。

// 戻り値を格納したいので、 @CosmosDBOutputは外
@FunctionName("output1")
@CosmosDBOutput(name = "CosmosDB2Functions",
        databaseName = "database",
        collectionName = "Items",
        connectionStringSetting = "AzureCosmosDBConnection")
public Item[] echo (
        @HttpTrigger(
                name = "req",
                methods = {HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<Item[]&gt;&gt; request,
        final ExecutionContext context) {
    return request.getBody().get();
}

戻り値としてrequest.getBody()ではなく、request.getBody().get()を指定しているのは、前者の場合、以下のようにvalue要素ごと格納されるからです。

{
  "value": {
    ....
  }
}

OutputBindingを利用して格納する

この場合、メソッドの引数を使ってCosmos DBにデータを格納するため、@CosmosDBOutputは引数outputItemに対して指定していることに注意する必要があります。

// OutputBindingを使うので、@CosmosDBOutputは引数outputItemに指定

@FunctionName("output2")
public HttpResponseMessage echo (
        @HttpTrigger(
                name = "req",
                methods = {HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<List<Item&gt;&gt;&gt; request,
        @CosmosDBOutput(
                name = "CosmosDB2Functions",
                databaseName = "database",
                collectionName = "Items",
                connectionStringSetting = "AzureCosmosDBConnection")
                OutputBinding<List<Item&gt;&gt; outputItem,
        final ExecutionContext context
) {
    outputItem.setValue(request.getBody().get());
    return request.createResponseBuilder(HttpStatus.CREATED)
                  .header("content-type","application/json")
                  .body(request.getBody().get())
                  .build();
}

アノテーションで設定するプロパティは以下のドキュメントに記載があります。

CosmosDBOutput Interface
https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation.cosmosdboutput?view=azure-java-stable

ドキュメントに記載のないデフォルト値は以下の通りです。

プロパティデフォルト値
name
dataType“”
databaseName
collectionName
createIfNotExistsfalse
connectionStringSetting
partitionKey“”
collectionThroughput-1
useMultipleWriteLocationsfalse
preferredLocations“”