このエントリは2020/10/30現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。
Event GridとCloudEvents関連のエントリはすでに3件公開している。
CloudEventsを使う
https://logico-jp.io/2020/09/06/use-cloudevents-schema-in-azure-event-grid/
Azure Event Grid Java SDK で CloudEvents を扱う際の注意事項
https://logico-jp.io/2020/10/23/tips-for-using-event-grid-sdk-to-handle-cloudevents/
CloudEventsのAPIを使ってAzure Event Gridにイベントを投げ込む
https://logico-jp.io/2020/10/30/using-cloudevents-apis-to-post-events-to-azure-event-grid/
このエントリでは、前回のエントリの逆、つまりEvent Grid Topicをサブスクライブするアプリケーションを、FunctionsのBindingやEvent Grid SDKではなく、CloudEvents APIを使って作成してみる。前提条件は前回のエントリに記載した通りなので省略する。
構成
前回のエントリと同様、Event Gridの前後に以下のようなアプリケーションを配置する。このエントリで作成するのはJAX-RSアプリケーション。テストの開始点として先日作成したクライアントアプリケーション、ならびに投入イベント確認目的で、チュートリアルで提供されるViewerアプリケーションも利用する。

依存関係
今回CloudEventsのJAX-RS用のAPIとJacksonを使ったHTTP Binding APIを使うため、以下の依存関係を追加しておく(2020/10/30現在)。
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-restful-ws</artifactId>
<version>2.0.0-milestone3</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-json-jackson</artifactId>
<version>2.0.0-milestone3</version>
</dependency>
endopoint作成
これは通常のJAX-RSのお作法と変わらない。現時点ではEvent GridがStructured Content Modeにしか対応していないため、必ずJSONで届く。そのため、今回はJsonObjectで待ち受けている。
@Path("/updates")
@POST
public Response receiveEvent(Optional<JsonObject> obj) {
if(obj.isEmpty()) return Response.noContent().status(Response.Status.OK).build();
EventFormat format = EventFormatProvider
.getInstance()
.resolveFormat(JsonFormat.CONTENT_TYPE);
CloudEvent ce = format.deserialize(obj.get().toString().getBytes(StandardCharsets.UTF_8));
JsonObject customData = JsonUtil.toJson(new String(ce.getData())).asJsonObject();
// output to console
System.out.println("Received JSON String -- " + obj.get().toString());
System.out.println("Converted to CloudEvent -- " + ce.toString());
System.out.println("Data in CloudEvent -- " + customData.toString());
return Response.noContent().status(Response.Status.ACCEPTED).build();
}
その他やっていることは以下の通り。
- CloudEventsインスタンスへのデシリアライズは、deserializeメソッドを利用
- CloudEventインスタンスにwithDataメソッドで入れたデータはバイト配列になっているので、これをJSONに戻すために、
JsonUtil::toJson()
を使ってJsonValue に変換し、さらに asJsonObject() メソッドでJsonObjectに変換
Event GridからのWebhookを有効化
最初のエントリでも記載したが、Event GridからのWebhookを有効化するため、OPTIONSメソッドでの挙動を追加しておく必要がある。これもJAX-RSのお作法に従えばよく、特に難しくはない。レスポンスには、HTTP HeaderとしてWebhook-Allowed-Origin=eventgrid.azure.netを追加する。
@Path("/updates")
@OPTIONS
public Response isWebhookEnabled() {
return Response.ok()
.allow("GET", "POST", "OPTIONS")
.header("Webhook-Allowed-Origin","eventgrid.azure.net")
.build();
}
テスト
今回のアプリケーションはHelidon MP 2.1.0をベースにCloudEventsを待ち受ける際に必要な依存関係を追加し、コンテナ化してApp Serviceで稼働させている(コンテナ化して稼働させたほうが楽、というだけで他意はない)。
Docker Hub上のリポジトリからApp Serviceにデプロイし、稼働するまでは以下のような感じ。
# Resource Group
az group create -n $RESOURCE_GROUP -l $REGION
# App Service Plan
az appservice plan create -n $PLAN_NAME -g $RESOURCE_GROUP -l $REGION --is-linux --sku P1V2 --number-of-workers 1
# App Service
az webapp create -n $WEBAPP_NAME -g $RESOURCE_GROUP -p $PLAN_NAME -i $DOCKER_IMAGE
つづいて、Event Grid Topicをサブスクライブするように構成。前回のエントリで使ったViewerアプリがサブスクライブする同じEvent Grid Topicに対して構成する。構成方法はEvent Gridのチュートリアルと同じ手順でOK。
で、これまた前回のエントリで作成したテストクライアントからイベントを投げ込む。今回は2発投げ込んでみた。まずはViewerで確認。

で、作成したJAX-RSアプリ側はApp Serviceのログで確認。先ほどのprintlnメソッドの出力が3行づつ出ていることがわかる。

ということで、JAX-RSアプリでも2件、(当然ではあるが)同じデータ、そしてCloudEventsのAPIで処理できることを確認できた。
まとめ
前回、今回でCloudEvents APIを使って送受信できることを確認した。JAX-RSとJacksonのAPIがそろっているので、MicroProfileでもJakarta EEでも簡単に利用でき、またAzure Event Gridへの出入りするイベントも問題なく処理できるので、Azure Event GridのSDK固有のお作法を理解する必要がないのは汎用性があってよいかもしれない。