Skip to content

(WIP)Upgrade to Spring Boot 4.1.1 and Jackson 3 - #1573

Draft
bradyyie wants to merge 10 commits into
mainfrom
feat/spring-boot-4-upgrade
Draft

(WIP)Upgrade to Spring Boot 4.1.1 and Jackson 3#1573
bradyyie wants to merge 10 commits into
mainfrom
feat/spring-boot-4-upgrade

Conversation

@bradyyie

@bradyyie bradyyie commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Upgrades the framework stack to Spring Boot 4.1.1 and migrates the codebase from Jackson 2 to
Jackson 3. This brings Spring Framework 7, Spring Security 7, Tomcat 11, Jakarta EE 11, springdoc 3
and Spring AI 2.0 with it.

Versions now come from the Spring Boot BOM unless there is a stated reason not to. Overrides that
existed only because an older BOM lagged behind have been removed: framework, security, log4j2,
netty, kafka, groovy, tomcat, flyway, micrometer, awaitility and jackson.

  • Elasticsearch, because Conductor ships selectable ES6/7/8 and OpenSearch backends and the BOM
    tracks a client that will not talk to the older clusters
  • Testcontainers, because the BOM moved to 2.x and that migration is its own piece of work

Moves the build onto the Spring Boot 4.1.1 BOM and migrates the codebase from
Jackson 2 (com.fasterxml.jackson) to Jackson 3 (tools.jackson).

Build:
- Boot plugin and BOM 3.5.14 -> 4.1.1
- Removes overrides the BOM now covers: framework, security, log4j2, netty,
  kafka, groovy, jedis, tomcat, flyway, micrometer, awaitility, jackson
- Keeps only the Elasticsearch and Testcontainers overrides, each with a reason
- Drops dead version variables and the unused jsr311-api dependency
- Pins spring-retry, which Boot 4 no longer manages

Jackson 3:
- ObjectMapperProvider builds an immutable mapper via JsonMapper.builder
- ObjectMapperConfiguration removed; its settings move to the builder customizer
- JsonProtoModule moves to ValueSerializer/ValueDeserializer
- json-schema-validator 3.x and jackson-jq 2.0 for native Jackson 3 support
- Elasticsearch and OpenSearch transports keep a Jackson 2 mapper at the boundary

Spring AI 2.0, springdoc 3.1, and Boot 4 package relocations applied.
- e2e keeps Jackson 2: it drives the published client SDK, which is a Jackson 2
  artifact, so those tests declare the Jackson 2 compile dependencies directly
- Boot 4 test relocations: AutoConfigureMockMvc moved to spring-boot-webmvc-test,
  DataSource and Flyway auto-configuration to spring-boot-jdbc and spring-boot-flyway
- groovy-all is not managed by groovy-bom, so specs depend on groovy plus groovy-json
- JsonNode.findValuesAsText is findValuesAsString in Jackson 3
- OpenSearch test transports use a Jackson 2 mapper, matching the production config
- StartWorkflow now catches JacksonException as well: Jackson 3 no longer reports
  a failed convertValue as IllegalArgumentException, so a malformed startWorkflow
  payload stopped failing the task
- Tests that rely on plain @mock fields open their mocks explicitly, since Boot 4
  dropped the listener that used to do it
- START_WORKFLOW_PARAMETER is package-private so the same-package Spock spec can
  read it under Groovy 5, which honours private access
- DummyPayloadStorageTest accepts JacksonException, which no longer extends IOException
…uite

- protobuf moves to 4.x. The 3.x pin from #964 was there because protobuf 4 with
  GraalVM polyglot 24.x made Gradle request a polyglot4 artifact that does not
  exist; GraalVM 25 resolves cleanly. Boot 4 forces the move regardless, since
  micrometer's OTLP registry now pulls opentelemetry-proto built on protobuf 4.
  protoc follows revProtoBuf so gencode and runtime agree
- httpclient5 comes from the BOM. The stale 5.3.1 variable was masked by a force
  that this upgrade removed, and 5.3.1 lacks TlsSocketStrategy
- json-jq surfaces the parser message again: jackson-jq 2.x changed the wrapper
  exception text the message filter was keyed on
- test-harness excludes the six SDK-driven suites. The published conductor-client
  is Jackson 2 and links against this project's conductor-common, which is now
  Jackson 3; see issues.md
…efresh docs

- The @Inject and @singleton annotations on the two publisher classes were inert:
  both are constructed explicitly, and Spring dropped javax.inject support in 6.x
- management.metrics.web.server.request.autotime.percentiles no longer exists in
  Boot 4; percentiles move to management.metrics.distribution.percentiles, which
  restores the quantile series on /actuator/prometheus
- AGENTS.md records which pins the upgrade retired and states the BOM-first rule
- issues.md covers the container-gated suites and what was verified at runtime
The publisher posts from a background thread and the first notification pays the
one-off cost of loading the serialization and metrics classes, measured at about
1.8s against a one second wait. Only the wait changes; the verification is the same.
Spring Boot 4 dropped spring-retry from its BOM because the functionality moved
into spring-core. This replaces the library rather than pinning it.

- RetryPolicy.builder() replaces SimpleRetryPolicy plus a backoff policy. Note
  maxRetries counts retries where maxAttempts counted attempts, so each budget
  drops by one to keep the same number of calls
- the three SQL deadlock policies overrode canRetry to inspect the last
  throwable, which is what predicate(Predicate<Throwable>) is for, so they are
  now plain predicates rather than policy subclasses
- Retryable takes no argument; the RetryContext parameter was unused at all
  twelve call sites
- callbacks that only throw unchecked exceptions use invoke(), which rethrows
  the original cause once retries are exhausted. That keeps DefaultEventProcessor
  seeing a TransientException and keeps the SQLException as the cause of the
  NonTransientException the SQL DAOs raise
- the two Elasticsearch executeWithRetry ladders unwrap RetryException before
  testing the exception type, otherwise every failure would have collapsed into a
  generic IOException

TestDefaultEventProcessor still pins three attempts for a transient failure and
one for a non-retriable one.
Enabling redis-concurrency-limit failed at startup because the module declares
spring-data-redis compileOnly and the server never shipped it, so the feature
could not load at all. The server now declares it runtimeOnly.

issues.md records the Redis verification: a live Redis 7.4 backing persistence,
queues and the Redisson lock through a full workflow lifecycle and 25 concurrent
workflows, plus a direct check of Spring Data Redis 4.1.1 with Jedis 7.4.1 through
the concurrency limit connection factory. That pairing is what the retired jedis
3.6.0 pin existed for.
@bradyyie bradyyie changed the title Upgrade to Spring Boot 4.1.1 and Jackson 3 (WIP)Upgrade to Spring Boot 4.1.1 and Jackson 3 Aug 25, 2026
@bradyyie
bradyyie marked this pull request as draft August 25, 2026 20:07
…a validator

test-harness: the conductor-client fat JAR was moved onto the test classpath as a
files() dependency, which Gradle places ahead of the project modules. That flipped
the precedence the module has always relied on, so the JAR's older copies of
classes the project also ships won, and the SDK's TaskContext shadowed the one in
conductor-common. Declaring the client as a module dependency after the project
modules restores the original order. WorkflowFunctionalTest joins the excluded
suites: it inherits SDK usage from FunctionalTestBase and hits the same Jackson 2
linkage as the others.

json-schema-validator was applied to every module from the root build even though
only common and ai use it, which dragged mockserver's 1.x transitive up to 3.x and
broke its expectation validation. It is now declared where it is used, and the
http-task test classpath keeps the version mockserver was built against.

Removing that global exposed seven modules that were taking Jackson through it
without declaring it. Each now declares what it imports.
The OpenSearch java client serialises with Jackson 2, so the type it is asked to
deserialize into has to be a Jackson 2 type. The Jackson 3 migration rewrote these
ObjectNode type tokens along with everything else, and the Jackson 2 mapper then
tried to introspect Jackson 3's ObjectNode as a bean and failed on its overloaded
setAll. Only the tokens crossing the client boundary change; the nodes this class
builds from its own mapper stay on Jackson 3.

es7, es8 and os2 are unaffected: they deserialize into Conductor model classes or
Map, and Jackson annotations are shared between the two versions.
@bradyyie

Copy link
Copy Markdown
Contributor Author

#1280

@bradyyie

Copy link
Copy Markdown
Contributor Author

Branch will be used to create 4.0.0-alpha.X fixes made here or merges to this branch from other branches will be included

The reason for this:

  • is to allow OSS to continue with 3.X.X versions and fixes without blocking anyone.
  • Allows for alpha images for end users to use as pre-release

Fixes and PRs to this branch i am happy to approve HOWEVER please DO NOT file issues about this branch. Fix it or wait for it to be fixed 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant