Azure / Azure/azure-functions-java-worker

OutputBinding not compatible with com.azure.core.models.CloudEvent

未关闭
#720 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area:java-functions Enhancement Needs: Triage (Functions) New Feature reviewed
主要语言
Java
星标
103
派生
74
平均合并
4 天 8 小时
30 天内合并 PR
2

描述

#### Investigative information

Please provide the following:

- Timestamp:
- Function App name:
- Function name(s) (as appropriate):
- Invocation ID:
- Region:

I'm not sure what the format of a CosmosDBTrigger is, and I am unable to provide this information for my current setup at the time.

#### Repro steps

As a function:

Trigger a function with com.azure.core.models.CloudEvent as the type of the OutputBinding in combination with @EventGridOutput.

```java
final @EventGridOutput(
name = "OutputEvent",
topicEndpointUri = "EventGridTopicEndpoint",
topicKeySetting = "EventGridTopicKey")
OutputBinding eventGridOutput
```

As a standalone issue:

```java
package org.example.functions;

import com.azure.core.models.CloudEvent;
import com.azure.core.models.CloudEventDataFormat;
import com.azure.core.util.BinaryData;
import com.google.gson.Gson;

public class Foo {

public static void main(String[] args) {
CloudEvent cloudEvent = new CloudEvent("mySource", "myType", BinaryData.fromObject(new Object()), CloudEventDataFormat.JSON, "application/json");
Gson gson = new Gson();
gson.toJson(cloudEvent);
}

}
```
More specific, it's the serialization of com.azure.core.implementation.util.SerializableContent, used by com.azure.core.util.BinaryData#fromObject(java.lang.Object) that causes the issue

```java
package org.example.functions;

import com.azure.core.implementation.serializer.DefaultJsonSerializer;
import com.google.gson.Gson;

public class Bar {

public static void main(String[] args) {
Gson gson = new Gson();
gson.toJson(new SerializableContent(new Object(), new DefaultJsonSerializer()));
}
}
```

#### Expected behavior

The CloudEvent is serialized and sent to the desired output binding.

#### Actual behavior

The runtime is unusable after one trigger due a (swallowed?) StackOverflowError and nothing is sent to the output as a result.

#### Known workarounds

- Create a custom class that implements the CloudEvent spec.

#### Related information

Provide any related information

Basically it all boils down to Jackson vs Gson. The [CloudEvent](https://github.com/Azure/azure-sdk-for-java/blob/21d71f77f0b489db1667b6bd5a989208b1372d7c/sdk/eventgrid/azure-messaging-eventgrid/src/main/java/com/azure/messaging/eventgrid/implementation/models/CloudEvent.java#L19) uses Jackson annotations to imply serialization rules while the azure-functions-java-worker uses [Gson](https://github.com/Azure/azure-functions-java-worker/blob/31e0a81e8522327c201bacbfc82665fcded6953e/src/main/java/com/microsoft/azure/functions/worker/binding/RpcUnspecifiedDataTarget.java#L6) for serialization. Ironically, the last change on RpcUnspecifiedDataTarget.java, was switching from Jackson to Gson.

Note that com.azure.core.models.CloudEvent#binaryData is annotated with @JsonIgnore. This property is what causes the StackOverflowError when serializing with Gson.

I'm using java 8 to dodge the issues caused by reflection due modularization in java 9.
@CosmosDBTrigger as trigger
@EventGridOutput as output binding

Ideally, there is a consensus in using Jackson vs Gson throughout the azure java projects to prevent such issues in the future.

Furthermore, the CloudEvent implementation doesn't even work with Jackson due to com.azure.core.models.CloudEvent#getData not returning the actual data. Instead it serializes to SerializableContent.

```java
@JsonProperty("data")
private JsonNode data;

public BinaryData getData() {
if (this.binaryData == null) {
if (this.data != null) {
this.binaryData = BinaryData.fromObject(this.data, SERIALIZER);
} else if (this.dataBase64 != null) {
this.binaryData = BinaryData.fromBytes(Base64.getDecoder().decode(this.dataBase64));
}
}

return this.binaryData;
}
```

Serializing CloudEvent with Jackson:

```java
package org.example.functions;

import com.azure.core.models.CloudEvent;
import com.azure.core.models.CloudEventDataFormat;
import com.azure.core.util.BinaryData;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Jackson {

public static void main(String[] args) throws Exception {
CloudEvent cloudEvent = new CloudEvent("mySource", "myType", BinaryData.fromObject(new Object()), CloudEventDataFormat.JSON, "application/json");
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.writeValueAsString(cloudEvent));
}
}
```
Output:

```json
{"id":"19b834e0-803b-4c12-9d21-4e6efea3f3b9","source":"mySource","data":{"replayable":true,"length":2},"data_base64":null,"type":"myType","time":null,"specversion":"1.0","dataschema":null,"datacontenttype":"application/json","subject":null}
```
Note that the data property is the serialized representation of SerializableContent through 'replayable' and 'length'.

The CloudEvent implementation for Event Grid is unusable at this point without changes to the azure-sdk-for-java.
https://github.com/Azure/azure-sdk-for-java/issues/36000

Please let me know if you require addition info.

贡献指南

打开贡献指南

调研方向

从 src/main/java/com/microsoft/azure/functions/worker/binding/RpcUnspecifiedDataTarget.java 开始,并使用 issue 中的独立 Gson 示例重现该故障。将其序列化行为与所引用的 Azure CloudEvent 实现进行比较,并验证 CloudEvent 可以在不发生 StackOverflowError 的情况下完成序列化,并通过 EventGrid 输出绑定发送。

由索引模型根据 Issue 内容生成。

评估

技术栈
azure, java
领域
backend, cloud
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。