Skip to content

fix: migrate to http4k 6 (Java 21 / Kotlin 2.4) + tooling & dependency upgrades (#95)#96

Open
juherr wants to merge 16 commits into
IZIVIA:devfrom
juherr:juherr/init-claude-md
Open

fix: migrate to http4k 6 (Java 21 / Kotlin 2.4) + tooling & dependency upgrades (#95)#96
juherr wants to merge 16 commits into
IZIVIA:devfrom
juherr:juherr/init-claude-md

Conversation

@juherr

@juherr juherr commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What & why

Fixes #95. Applications that moved to http4k 6.x crash at runtime with
NoClassDefFoundError: org/http4k/routing/WsRouter because the toolkit was
compiled against http4k 5.x (binary incompatibility). This PR recompiles the
toolkit against http4k 6.53.0.0 and, as a consequence, brings the build
chain forward (http4k 6 requires Java 21 and ships Kotlin 2.4 bytecode).

⚠️ Breaking changes

  • Requires Java 21 (http4k 6 minimum; was Java 17).
  • Built against http4k 6.x and Kotlin 2.4 (was http4k 5.x / Kotlin 1.9).

Consumers on Java 17 / http4k 5 must stay on the current released line.

Core migration (http4k 5 → 6)

  • ocpp-wamp: WS routing import routing.ws.bindrouting.websocket.bind;
    WsMessage(Body(...))WsMessage(...) (constructor removed in v6).
  • toolkit CSMS: routes()/websockets() now throw on empty input → pass
    null for the absent protocol side.
  • ocpp-transport-soap: http4k-contracthttp4k-api-openapi (artifact
    renamed in v6; org.http4k.contract.* API unchanged).
  • The custom Undertow adapter (Undertow.kt, subprotocol handshake) compiles
    unchanged on v6 — PolyServerConfig / Http4kUndertowHttpHandler /
    Http4kWebSocketCallback / RoutingWsHandler are intact.

Build chain & tooling

  • JVM target 17 → 21 (build + CI).
  • Kotlin 1.9.22 → 2.4.0; migrate kotlinOptionscompilerOptions DSL;
    decouple buildSrc from the Kotlin plugin (it only exposes it to subprojects).
  • Gradle 8.12 → 9.6.0: migrate the build-scan plugin
    com.gradle.enterprisecom.gradle.develocity 4.4.3, bump refreshVersions
    to 0.60.6, and declare junit-platform-launcher explicitly (Gradle 9 no
    longer adds it implicitly).
  • CI: JDK 17 → 21, and GitHub Actions pinned to commit SHAs.
  • KGP 2.x: testFixturesImplementation no longer extends implementation
    (declare kotlinx-datetime explicitly in generic-api).

Dependency upgrades (to latest stable)

jackson 2.22.0 · kotlinx-coroutines 1.11.0 · commons-lang3 3.20.0 ·
logback 1.5.35 · undertow 2.4.1.Final (drop unused undertow-servlet,
discontinued in 2.4.x) · json-schema-validator 1.5.9 · mockito-kotlin 6.3.0
(mockito 5.x; drop mockito-inline) · JUnit 6.1.0.

(kotlin-logging, mapstruct and slf4j only have pre-releases beyond their
current stable, so they were left as-is.)

🚧 Needs an architecture decision (intentionally NOT upgraded here)

Two libraries cannot be moved to their latest release as a routine bump —
each is a major migration with consumer-visible behavioural impact, so
they're kept at the latest compatible version and flagged for a deliberate
decision:

json-schema-validator — kept at 1.5.9 (latest 1.x)

  • 3.x depends on Jackson 3 (tools.jackson.*), incompatible with the
    toolkit's pervasive Jackson 2.x (com.fasterxml.jackson). Adopting it means
    either a full Jackson 2 → 3 migration of the whole toolkit, or a
    conversion bridge (double-parsing).
  • The 2.0.x line is Jackson-2-compatible but already ships the rewritten
    API (Schema/SchemaRegistry/Error), which drops ValidatorTypeCode
    and numeric error codes
    . The OCPP error code field would change from
    numeric (1001/1028) to JSON Schema keywords (additionalProperties/
    required) — a change to the error responses we send.
  • → Decision needed: adopt Jackson 3 toolkit-wide, or move to networknt 2.0.x
    and accept (or compat-map) the error-model change.

kotlinx-datetime — kept at 0.4.1

  • 0.6+/0.7+ deprecate then remove kotlinx.datetime.Instant in favour of
    kotlin.time.Instant (~117 files use it), make the toJavaInstant /
    toKotlinInstant interop helpers internal, and make Instant.parse
    stricter
    — it rejects timestamps without seconds (2023-10-06T12:33Z),
    an OCPP interoperability concern.
  • → Decision needed: migrate to kotlin.time.Instant, or use the
    0.x-compat flavour and decide whether to keep lenient timestamp parsing.

Verification

./gradlew build is green on JDK 21 (full suite, ~565 tests), including the
WAMP WebSocket handshake (WampIntegrationTest) and the CSMS assembly
(IntegrationTestCSApi).

🤖 Generated with Claude Code

juherr added 11 commits June 23, 2026 21:39
http4k 6.x removed org.http4k.routing.WsRouter, causing a runtime
NoClassDefFoundError for applications running on http4k 6. Recompile
the toolkit against http4k 6.53.0.0.

http4k 6 requires Java 21 and is built with Kotlin 2.4, so:
- bump JVM target 17 -> 21 (build + CI)
- bump Kotlin 1.9.22 -> 2.4.0, migrate kotlinOptions -> compilerOptions DSL
- decouple buildSrc from the Kotlin plugin (it only needs to expose it to
  subprojects; keeping it on buildSrc's own classpath conflicts with the
  kotlin-dsl embedded language version)

http4k 6 API changes:
- ocpp-transport-soap: http4k-contract -> http4k-api-openapi
- OcppWampServerApp: routing.ws.bind -> routing.websocket.bind
- UndertowWebSocketCallBack: removed WsMessage(Body) constructor
- CSMS: routes()/websockets() now throw on empty input -> pass null

Also: KGP 2.x no longer makes testFixturesImplementation extend
implementation, so kotlinx-datetime is declared explicitly in generic-api.

BREAKING CHANGE: the toolkit now requires Java 21 and http4k 6.x.
Refresh the non-http4k dependencies now that the toolkit builds on
Java 21 / Kotlin 2.4:
- jackson 2.16.1 -> 2.19.4
- kotlinx-coroutines 1.6.4 -> 1.10.2
- kotlinx-datetime 0.4.0 -> 0.4.1 (also reconcile the hardcoded 0.3.2 in
  coreProject and generic-api testFixtures onto the managed version)
- undertow 2.3.12 -> 2.3.24.Final
- junit-jupiter 5.9.1 -> 5.11.4
- mockk 1.14.0 -> 1.14.11
- logback-classic 1.4.4 -> 1.5.18
- slf4j-api 2.0.3 -> 2.0.18
- commons-lang3 3.12.0 -> 3.18.0
- mapstruct 1.5.3 -> 1.6.3
- json-schema-validator 1.0.73 -> 1.0.88
- kotlin-logging 3.0.4 -> 3.0.5
- mockito 4.8.1 -> 4.11.0, mockito-kotlin 4.0.0 -> 4.1.0
- strikt 0.34.1 -> 0.35.1

Deliberately kept within the current major for deps whose next major needs
a code migration: json-schema-validator (2.x/3.x API rewrite), mockito
(5.x drops mockito-inline), kotlinx-datetime (0.6+/0.7 removes
kotlinx.datetime.Instant), junit-jupiter (5.14 needs a newer platform
launcher than Gradle bundles; 6.x is a new major), kotlin-logging 4.x.
- bump Gradle wrapper 8.12 -> 8.14.5 (the Kotlin 2.4 plugin warns that
  Gradle < 8.14.4 is deprecated and unsupported from Kotlin 2.5)
- raise Kotlin language/api version 2.0 -> 2.2 (2.0 is deprecated by the
  2.4 compiler)
Adaptations required by the Gradle 9 major:
- migrate the build-scan plugin com.gradle.enterprise 3.13.1 ->
  com.gradle.develocity 4.4.3 (gradleEnterprise{} -> develocity{},
  termsOfService* -> termsOfUse*); the old plugin called the removed
  Provider.forUseAtConfigurationTime() and broke at configuration time
- bump refreshVersions 0.51.0 -> 0.60.6 (Gradle 9 compatible)
- declare junit-platform-launcher explicitly on the test runtime classpath
  (Gradle 9 no longer adds it implicitly)
Straightforward version bumps to the latest stable, no source changes:
- jackson 2.19.4 -> 2.22.0
- kotlinx-coroutines 1.10.2 -> 1.11.0
- commons-lang3 3.18.0 -> 3.20.0
- logback-classic 1.5.18 -> 1.5.35
undertow-servlet was discontinued after 2.3.x (no 2.4.x release) and was
not used anywhere in the code (only undertow-core APIs are referenced), so
drop the undertow-servlet dependency and bump undertow-core to 2.4.1.Final.
Adaptations for the json-schema-validator 1.0.88 -> 1.5.9 bump:
- since 1.5.x validation messages are localized with the default JVM
  locale; pin OcppJsonValidator to Locale.ENGLISH so OCPP error details
  stay deterministic regardless of the server locale
- the order of reported validation messages changed (additionalProperties
  now before required); update the affected assertion indices in
  Ocpp20JsonParserTest

Kept on the 1.x line: 2.x/3.x is a ground-up API rewrite (Schema /
SchemaRegistry / Error replace JsonSchema / JsonSchemaFactory /
ValidationMessage / ValidatorTypeCode) that changes the OCPP validation
error model and needs a dedicated migration.
mockito-kotlin 4.1.0 -> 6.3.0, which pulls mockito-core 5.x transitively.
The mockito-inline artifact was discontinued after 5.2.0 (the inline mock
maker is the default in mockito 5), so drop the mockito-inline test
dependency and the now-unused version.mockito key. The toolkit test only
uses Mockito.spy + org.mockito.kotlin.*, both still provided.
junit-jupiter 5.11.4 -> 6.1.0 (JUnit 6 unified the jupiter and platform
versions), and bump the explicit junit-platform-launcher to the matching
6.1.0. No test API changes were needed. JUnit 6 requires Java 17+, already
satisfied by the Java 21 toolchain.
Pin actions/setup-java, actions/checkout and gradle/gradle-build-action to
full commit SHAs (with the version in a trailing comment) instead of
floating major tags, for supply-chain safety.
Address review feedback: keep all versions in versions.properties / under
refreshVersions control rather than hardcoding them in build scripts.
- root build.gradle.kts: drop the explicit kotlin("jvm") version "2.4.0";
  refreshVersions resolves it from version.kotlin
- coreProject: kotlinx-datetime and junit-platform-launcher now use the ":_"
  placeholder (junit-platform-launcher version moved to versions.properties)
- generic-api: same ":_" for the testFixtures kotlinx-datetime dependency
@juherr juherr force-pushed the juherr/init-claude-md branch from d33f655 to b1e4957 Compare June 24, 2026 07:27
juherr added 5 commits June 24, 2026 09:48
WebsocketTest.receiveMessageClass pushed a server-initiated CALL after a
fixed Thread.sleep(100), which is racy on slow CI (NoConnectionException on
GitHub Actions). Retry the send until the connection is registered
server-side, up to a 5s timeout.
gradle/gradle-build-action and its 'arguments' parameter are deprecated.
Use gradle/actions/setup-gradle (pinned to its commit SHA) and run the
build through a plain './gradlew build' step.
http4k 6 moved PolyHandler from org.http4k.server to org.http4k.core and
deprecated the old alias/constructor. Import from the new package.
Silence the Jackson deprecation warnings surfaced by the jackson 2.22 bump:
- drop the redundant setSerializationInclusion(...) calls: each is followed
  by an equivalent setDefaultPropertyInclusion(...) that already sets the
  same default inclusion
- drop the redundant enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_*) calls in
  the 1.5/1.6 SOAP mappers (the base OcppSoapMapper already enables them and
  they are inherited via the copy constructor)
- keep (and @Suppress) the base OcppSoapMapper enable(MapperFeature) calls:
  the builder-based alternatives do not reproduce the
  XmlMapper(factory, CustomXmlModule) construction this mapper depends on

No behavior change; the SOAP 1.5/1.6 and JSON parser test suites still pass.
…ature)

Reference jackson-databind#3782: the builder/rebuild() replacement is only
available on JsonMapper in 2.x (XmlMapper has no rebuild(), and a fully
functioning rebuild() lands in Jackson 3.0), XmlMapper.builder() does not
reproduce the XmlMapper(factory, CustomXmlModule) construction, and the
case-insensitive features are required. Documents that a clean fix needs the
Jackson 3 migration (same prerequisite as json-schema-validator 3.x).
@sonarqubecloud

Copy link
Copy Markdown

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.

Support http4k 6.x (currently pinned to 5.13.8.0 → NoClassDefFoundError: org/http4k/routing/WsRouter at runtime)

1 participant