Skip to content

Java jackson 3 version upgrade #569

Description

@selvacreation

New version of jacson 3.x is released. but latest restate springbboot SDK is using jacson 2.x.
https://mvnrepository.com/artifact/tools.jackson.core/jackson-core

Activity

  1. changed the title [-]Java jacson version upgrade[/-] [+]Java jackson 3 version upgrade[/+] on Dec 30, 2025
  2. slinkydeveloper commented on Jan 5, 2026

    @slinkydeveloper
    Contributor

    This is high on my list!

  3. cjmaria commented on Sep 5, 2026

    @cjmaria

    I had to jump through some hoops recently with the sdk due to the dependency on Jackson 2. Is the intent of the maintainers to move to Jackson 3 or is Jackson 2 dependence planned to be continued for some time?

    If so, I may begin the following proposal to reduce friction for Jackson 3-only projects:

    Streamline Jackson 3 usage with existing Restate Java SDK

    Feature Request / Bug: Modern Jackson 3 Support, Default JSR-310 (Java 8 Time), and Global Programmatic SerdeFactory Configuration

    Summary

    When building modern Java applications (e.g., Spring Boot 4+, Java 21/26) that use Jackson 3 (tools.jackson.databind.*) and standard Java 8 Date/Time types (java.time.Instant, java.time.LocalDate), integrating with Restate Java SDK (v2.9.x) requires substantial workarounds.

    Specifically:

    1. Default ObjectMapper lacks JSR-310 support: Out of the box, JacksonSerdeFactory.DEFAULT fails when serializing java.time.Instant and LocalDate.
    2. Hard-coupling to Jackson 2 (com.fasterxml.jackson.*): Restate's JacksonSerdeFactory cannot be subclassed or customized with a Jackson 3 ObjectMapper due to package/type mismatches.
    3. No programmatic global SerdeFactory configuration on Endpoint.builder(): Services must each be individually annotated with @CustomSerdeFactory(Class), and ServiceLoader SPI (DefaultSerdeFactoryProvider) collisions occur if sdk-serde-jackson is on the classpath.

    1. Problem Details

    A. Default Jackson Serde Fails on java.time.Instant and LocalDate

    When sending or receiving payloads containing standard java.time types through handler parameters, awakeables, or durable state, Restate throws:

    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.Instant` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (or disable `MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES`)
    

    Why this hurts: Modern Java applications almost exclusively use java.time.Instant and java.time.LocalDate rather than java.util.Date. Having the default serde fail on basic Java time types requires every developer to implement a custom serde factory just to pass a timestamp.

    B. Incompatibility with Jackson 3 (tools.jackson.databind.*)

    In Jackson 3, package names moved from com.fasterxml.jackson.databind.* to tools.jackson.databind.*, and Java 8 date/time is supported natively without external modules.

    However, the Restate Java SDK documentation recommends:

    class MyJacksonSerdeFactory extends JacksonSerdeFactory {
      public MyJacksonSerdeFactory() {
        super(new ObjectMapper().configure(...));
      }
    }

    Because JacksonSerdeFactory strictly accepts com.fasterxml.jackson.databind.ObjectMapper, projects enforcing Jackson 3 cannot reuse JacksonSerdeFactory or JacksonSerdes. Developers must manually implement the low-level dev.restate.serde.SerdeFactory interface from scratch using Serde.using(...) and tools.jackson.databind.JavaType.

    C. Inability to Programmatically Set a Global SerdeFactory on Endpoint

    Currently, when registering services on an Endpoint:

    Endpoint.builder()
        .bind(service1)
        .bind(service2)
        .build();

    There is no method such as Endpoint.builder().withSerdeFactory(mySerdeFactory).

    Instead, developers must:

    • Annotate every single @Service, @VirtualObject, and @Workflow class with @CustomSerdeFactory(MyFactory.class).
    • Or rely on the ServiceLoader SPI (DefaultSerdeFactoryProvider). However, dev.restate:sdk-serde-jackson already bundles a DefaultSerdeFactoryProvider in META-INF/services/. If an application registers its own DefaultSerdeFactoryProvider, ServiceLoader.load(DefaultSerdeFactoryProvider.class) discovers 2 providers. When multiple providers are discovered, Restate's ClientImpl disables default serde resolution with a warning:

      "When creating the Client, more than one SerdeFactory was found. To prevent unexpected behavior, the client was configured without SerdeFactory."


    2. Proposed Improvements

    1. Register JavaTimeModule by Default in JacksonSerdeFactory

    In dev.restate:sdk-serde-jackson, default ObjectMapper instances should register JavaTimeModule (or invoke findAndRegisterModules()):

    public static ObjectMapper defaultObjectMapper() {
        return new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }

    This single change would eliminate InvalidDefinitionException for all Jackson 2 users out of the box.

    2. Introduce a dev.restate:sdk-serde-jackson3 Module (or decouple SerdeFactory from Jackson 2)

    Provide first-class Jackson 3 support by publishing a dedicated sdk-serde-jackson3 artifact using tools.jackson.databind.*, or provide an interface abstraction for JSON object mappers so that applications can supply either Jackson 2 or Jackson 3 without custom boilerplate.

    3. Add Endpoint.Builder#withDefaultSerdeFactory(SerdeFactory)

    Allow developers to configure a default SerdeFactory programmatically when building the Endpoint:

    Endpoint.builder()
        .withDefaultSerdeFactory(new MyCustomSerdeFactory())
        .bind(service1)
        .bind(service2)
        .build();

    If present, this factory would serve as the fallback for any service that does not declare an explicit @CustomSerdeFactory annotation, avoiding SPI collisions and repetitive annotations across dozens of services.

    4. Symmetric Builder API for Client and Endpoint

    Both Endpoint.builder() and Client.builder() (or Client.connect(baseUri, serdeFactory)) should share a consistent mechanism for setting the default SerdeFactory, so that client-side and server-side serde behavior remain identical across durable awakeables and RPC calls.


    Environment

    • Restate Java SDK: 2.9.3
    • JDK: 21+ / 26
    • Build Tool: Gradle 9+ Kotlin DSL
    • Jackson: tools.jackson.core:jackson-databind:3.1.4 (Jackson 3 BOM 3.2.1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependenciesPull requests that update a dependency file

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions