-
Notifications
You must be signed in to change notification settings - Fork 52
Description
What happened:
There are two related issues in the SDK's Jackson serialization layer that prevent proper integration with DI frameworks (like Quarkus or Spring) and cause native CloudEvent filtering/evaluation to fail.
Issue 1: Static ObjectMapper Trap in JsonUtils
io.serverlessworkflow.impl.jackson.JsonUtils caches the ObjectMapper in a static field at class load time:
private static ObjectMapper mapper = ObjectMapperFactoryProvider.instance().get().get();If a framework attempts to inject a custom ObjectMapper via ObjectMapperFactoryProvider.instance().setFactory(...) after the JsonUtils class has been loaded, the factory override is completely ignored. This forces developers to use reflection to overwrite the static field.
Issue 2: Missing CloudEvent Serialization Support
The SDK natively exposes io.cloudevents.CloudEvent in its DSL (e.g., FuncEventFilterSpec). However, the default ObjectMapper fallback in ObjectMapperFactoryProvider does not register the CloudEvents Jackson module. When the engine evaluates event predicates (converting CloudEvent to JsonNode and back), Jackson throws either:
InvalidDefinitionException(Cannot construct instance of interface io.cloudevents.CloudEvent)MismatchedInputException(Missing mandatory specversion attribute due to default POJO serializers mangling the CE envelope).
Proposed Solution
Remove static caching: Update JsonUtils to fetch the mapper dynamically:
public static ObjectMapper mapper() {
return ObjectMapperFactoryProvider.instance().get().get();
}Optimize fallback & register CE natively: To prevent performance drops from creating a new ObjectMapper() on every call, update ObjectMapperFactoryProvider to provide a static, pre-configured singleton as the default fallback. This default mapper must include the official CloudEvents module:
private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper()
.findAndRegisterModules()
.registerModule(io.cloudevents.jackson.JsonFormat.getCloudEventJacksonModule());
Native Converters for CloudEvent Types: Update JsonUtils to explicitly intercept CloudEvent and CloudEventData types, bypassing Jackson POJO serialization in favor of the official EventFormatProvider.
- In
fromValue(Object value):
} else if (value instanceof CloudEvent ce) {
byte[] ceBytes = EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE).serialize(ce);
return mapper().readTree(ceBytes);
}- In
convertValue(JsonNode jsonNode, Class<T> returnType):
} else if (CloudEvent.class.isAssignableFrom(returnType)) {
byte[] ceBytes = mapper().writeValueAsBytes(jsonNode);
obj = EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE).deserialize(ceBytes);
}Anything else we need to know?:
This was something I should've tested when implementing the event filtering DSL.
Environment:
- Specification version used: