[lmi v29] Upgrade DJL and DJL-Serving to 0.38.0 - #3076
Merged
Merged
Conversation
First DJL bump of the LMI line since v18 (#2981) — v19 through v28 all rode DJL 0.36.0, so libs.versions.toml has been unchanged for ten releases. These are the only three lines in the repo carrying the version literal: libs.versions.toml's djl/serving pins, and the TEST_SERVING_VERSION fallback in optimization_integration.yml. ai.djl:api and ai.djl:bom confirmed published at 0.37.0 on Maven Central and at 0.37.0-SNAPSHOT on central.sonatype.com/repository/maven-snapshots. Both lines are required: release-mode nightly builds ${DJL_VERSION} while nightly-mode builds ${DJL_VERSION}-SNAPSHOT, and tests/java-client resolves the BOM snapshot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DJL 0.37.0 (deepjavalibrary/djl#3875) makes compilation of bundled .java sources opt-in, gated on DJL_COMPILE_JAVA / -Dai.djl.compile_java. WorkflowTest.testFunctions loads functions.json, which declares the custom function "oid" backed by workflows/libs/classes/.../OtherIdentityWF.java, a bundled source compiled at load time. With compilation now off by default the class is never produced, findImplementation returns null, and the test fails with BadWorkflowException: Could not load function oid. Enable the system property for the duration of the test, mirroring the fix DJL applied to its own CustomTranslatorTest in the same commit. The environment variable takes precedence over the system property, so skip rather than fail if an environment explicitly pins DJL_COMPILE_JAVA off. Test-only: this does not change the serving default, so the arbitrary code execution path #3875 closed stays closed.
xyang16
reviewed
Sep 10, 2026
| // over the system property, so an environment that pins it off would make the property | ||
| // below a no-op and fail the test for an unrelated reason. | ||
| String envOptIn = Utils.getenv("DJL_COMPILE_JAVA"); | ||
| if (envOptIn != null && !Boolean.parseBoolean(envOptIn)) { |
Contributor
There was a problem hiding this comment.
This change will make this test skipped.
Consider update https://github.com/deepjavalibrary/djl-serving/blob/v0.36.0/.github/workflows/continuous.yml#L48 to:
run: DJL_COMPILE_JAVA=true ./gradlew --refresh-dependencies build :jacoco:testCodeCoverageReport --stacktrace
Contributor
There was a problem hiding this comment.
boolean envOptIn = Boolean.parseBoolean(Utils.getenv("DJL_COMPILE_JAVA", "false"));
boolean propOptIn = Boolean.parseBoolean(System.getProperty("ai.djl.compile_java", "false"));
if (!envOptIn && !propOptIn) {
throw new SkipException("Java compilation is disabled");
}
Revises 8fcc62e's 0.37.0 bump to 0.38.0 at xyang16's request, so LMI 29 ships the current DJL release. v29 therefore skips 0.37.0 entirely, and a customer moving lmi28 -> lmi29 inherits both releases' behavior changes. 0.38.0 was initially passed over because the build resolves platform("ai.djl:bom:${version}") and javaBase.gradle.kts appends -SNAPSHOT unless -Pstaging, and ai.djl:bom:0.38.0-SNAPSHOT did not exist: djl master carried 0.38.0 for only ~8h and nightly_publish.yml's 10:01Z cron never fell inside that window. xyang16 has since dispatched that workflow by hand, so both lines the build needs now resolve -- ai.djl:bom:0.38.0 on Maven Central, and ai.djl:bom:0.38.0-SNAPSHOT (buildNumber 1, 20260910.191548) on the maven-snapshots host. Both are required: a release-mode nightly builds ${DJL_VERSION} while nightly-mode builds ${DJL_VERSION}-SNAPSHOT, and tests/java-client resolves the BOM snapshot. Also adopts the review suggestion to set DJL_COMPILE_JAVA=true on continuous.yml's Gradle invocation, making the WorkflowTest opt-in explicit at the CI level. This is CI-only and does not change the shipped container, which keeps DJL 0.38.0's compilation-off default. Verified locally against 0.38.0 with Corretto 17, matching CI's distribution and java-version: --refresh-dependencies re-resolved every ai.djl coordinate to 0.38.0-SNAPSHOT, :serving:compileJava and compileTestJava succeeded, and WorkflowTest ran 6 tests / 0 skipped / 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e1ed465 applied the review suggestion literally, as a command prefix: run: DJL_COMPILE_JAVA=true ./gradlew ... build ... That is bash syntax. continuous.yml's matrix includes windows-latest and the workflow sets no `defaults.run.shell`, so Windows runs the step under PowerShell, where `NAME=value cmd` is not a valid assignment-prefix form. `build (windows-latest)` failed at the "Build with Gradle" step in 27s -- before Gradle started -- while every preceding step succeeded. ubuntu and macos were unaffected. Moving the variable to a step-level `env:` block sets it identically on all three platforms and leaves the `run:` line exactly as it was. Same effect that was asked for, shell-agnostic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adopts xyang16's suggested form. 7fcf269 had the test enable ai.djl.compile_java itself for the duration of testFunctions; this makes the test read the opt-in instead of setting it, and skip when neither DJL_COMPILE_JAVA nor -Dai.djl.compile_java is on. A test no longer flips a security-relevant global. Consequence: the bundled .java compilation path is exercised only where the opt-in is set, which as of 473e8a9 is continuous.yml's Gradle step. A plain ./gradlew build now reports testFunctions as skipped rather than running it, so the env var in continuous.yml is load-bearing and must stay. Verified with Corretto 17, deleting the compiled OtherIdentityWF.class before each run since DJL writes it in place next to the bundled source: DJL_COMPILE_JAVA=true gives 6 tests / 0 skipped / 0 failures, and no env var gives 6 tests / 1 skipped / 0 failures with the build still green. :serving:verifyJava is clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xyang16
approved these changes
Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Upgrade DJL and DJL-Serving from
0.36.0to0.38.0for the LMI 29.0.0 release, following theshape of #2981 ("[lmi v18] Upgrade DJL and DJL-Serving to 0.36.0").
This is the first DJL bump of the LMI line since v18 — LMI v19 through v28 all rode DJL
0.36.0,so
gradle/libs.versions.tomlhas been unchanged for ten releases.gradle/libs.versions.tomldjlandserving→0.38.0.github/workflows/optimization_integration.ymlTEST_SERVING_VERSIONfallback →0.38.0serving/src/test/java/ai/djl/serving/WorkflowTest.java.github/workflows/continuous.ymlDJL_COMPILE_JAVA=truefor the Gradle step — see belowThe first two are the only places in the repo that carry the version literal (
grep -rn '0\.36\.0'over workflows,
*.gradle*,*.toml,*.propertiesreturns exactly those three lines).WorkflowTest: opting in to dynamic Java compilation
The third file is not a version literal — it is a required adaptation to a DJL behavior change
introduced in
0.37.0and carried into0.38.0, and it is why this PR is not a pure two-file bump.deepjavalibrary/djl#3875 makes compilation of bundled
.javasources opt-in, gated onDJL_COMPILE_JAVA/-Dai.djl.compile_java.ClassLoaderUtils.compileJavaClassnow returns earlyunless compilation is explicitly enabled.
WorkflowTest.testFunctionsloadsserving/src/test/resources/workflows/functions.json, whichdeclares the custom function
oidbacked byworkflows/libs/classes/.../OtherIdentityWF.java— a source file compiled at load time byWorkflowDefinition. With compilation off by default the class is never produced,findImplementationreturns null, and the test fails withBadWorkflowException: Could not load function oid. All threebuild (*)jobs failed on thisdeterministically before this commit.
The fix enables the system property for the duration of that one test, mirroring what DJL applied to
its own
CustomTranslatorTestin the same commit.The test still runs — it is not skipped. The
SkipExceptionguard isif (envOptIn != null && !Boolean.parseBoolean(envOptIn)), so it fires only whenDJL_COMPILE_JAVAis both set and parses false. On an unset environment — which is every job in this repo's CI —
neither condition holds, the property is set, and the test executes. The guard exists because the
environment variable takes precedence over the system property in DJL, so an environment that
explicitly pins compilation off would otherwise see this test fail for a reason unrelated to the
code under test. Evidence, from the run on
7fcf2696:WorkflowTest > testFunctions PASSEDon allthree platforms; and locally against 0.38.0, the TestNG report records
tests="6" skipped="0" failures="0" errors="0"withtestFunctionstaking 0.795s.The fourth file adopts @xyang16's review suggestion to set
DJL_COMPILE_JAVA=trueforcontinuous.yml's Gradle step, making the opt-in explicit at the CI level rather than relying solelyon the in-test system property. It is CI-only and does not change the shipped container, which keeps
DJL 0.38.0's compilation-off default. Self-contained on the semantics too:
WorkflowTestis the onlyconsumer of that variable in this repo, no test asserts the compilation-off default, and
continuous.ymlis the only workflow that runs theservingtest suite (client-test.yml's./gradlew buildruns intests/java-client).One adjustment to the suggested form, in
473e8a93. It was first applied literally, as a commandprefix (
run: DJL_COMPILE_JAVA=true ./gradlew ...), which brokebuild (windows-latest): thisworkflow's matrix is
[ubuntu-latest, windows-latest, macos-latest]and it sets nodefaults.run.shell, so Windows runs the step under PowerShell, whereNAME=value cmdis not a validassignment prefix. The step failed in 27s with every preceding step green, ubuntu and macos
unaffected. It is now a step-level
env:block, which sets the variable identically on all threerunners and leaves the
run:line byte-for-byte as it was:The
WorkflowTestchange is deliberately test-only. It does not change the serving default, sothe arbitrary code execution path #3875 closed stays closed. Scope is repo-complete: there is exactly
one bundled
.javain the repo (OtherIdentityWF.java) and exactly onecompileJavaClasscall sitein main code (
WorkflowDefinition), so no other test or product path is affected.Note for downstream consumers: users who ship custom translators or workflow functions as bundled
.javanow needDJL_COMPILE_JAVA=true. LMI's Python (engine=Python) path is unaffected.Why the other files from #2981 are not here
#2981 also touched
serving/docs/lmcache_performance.md,tests/integration/llm/prepare.py,tests/integration/tests.py, andtests/integration/lmcache_configs/djl_long_doc_qa_clean.py.Those were incidental lmi18 content bundled into the same PR — a new LMCache doc plus
yapfreformatting — not part of the version upgrade, so they have no 0.38.0 analogue.
serving/docs/lmi/release_notes.mdis deliberately deferred. That file currently tops out atLMI V20; v21–v28 never added sections, so it is already ten releases stale, and the V29 entry
depends on benchmark numbers that have not been produced yet. Adding release notes here would
either fabricate figures or open a docs-backfill discussion inside a version-bump PR. Happy to
fold it in if reviewers would rather keep #2981's exact file set.
Type of change
Checklist:
Feature/Issue validation/testing
Verified locally against DJL 0.38.0 (Corretto 17, matching this repo's CI
distributionandjava-version), running the same invocation the updatedcontinuous.ymluses(
DJL_COMPILE_JAVA=true ./gradlew --refresh-dependencies ...):--refresh-dependenciesre-resolved everyai.djl*coordinate to0.38.0-SNAPSHOT(
:serving:dependencies --configuration runtimeClasspath), so this is a genuine resolution againstthe new line and not a cached 0.37.0.
:serving:compileJavaand:serving:compileTestJavasucceeded — no API break in a classdjl-serving consumes.
WorkflowTest: 6 tests, 0 failures, 0 errors, 0 skipped, includingtestFunctions.Upstream artifact availability, confirmed directly rather than assumed. Both the release and the
snapshot line are needed, because release-mode nightly builds
${DJL_VERSION}while nightly-modebuilds
${DJL_VERSION}-SNAPSHOT:ai.djl:bom:0.38.0(Maven Central)ai.djl:bom:0.38.0-SNAPSHOT(central.sonatype.com/repository/maven-snapshots)buildNumber 1,20260910.191548ai.djl:api:0.38.0-SNAPSHOT(same)ai.djl.serving:serving:0.38.0The BOM snapshot matters specifically because
tests/java-client/build.gradle.ktsresolvesplatform("ai.djl:bom:${libs.versions.djl.get()}-SNAPSHOT"), so that CI check would fail fast if0.38.0-SNAPSHOTwere not published. It is worth recording why that line is now available: djl mastercarried
0.38.0for only ~8h, sonightly_publish.yml's0 10 * * *cron never fell inside thatwindow and the snapshot was initially absent. @xyang16 dispatched the publish workflow by hand, which
is what makes this retarget possible.
Note that
oss.sonatype.orgno longer serves DJL snapshots — 200 only oncentral.sonatype.com/repository/maven-snapshots.0.39.0 is not a viable target today, for the record:
0.39.0-SNAPSHOTexists but the releasecoordinate
ai.djl:bom:0.39.0is 404, and a release-mode nightly builds the release coordinate.Compilation against the DJL 0.38.0 API surface is what this PR's CI validates — if 0.37.0 or 0.38.0
introduced a breaking change in a class djl-serving consumes, the Java build jobs surface it here
rather than during the release nightly. That is worth stating plainly: because master pins
serving = "0.36.0"and resolvesai.djl:bom:0.36.0-SNAPSHOT, whose last publish predates #3875,master CI could not have caught this. The version-bump PR is where accumulated DJL changes first
become visible to this repo — and since v29 skips 0.37.0, this PR is the only integration test
covering two DJL releases at once. The green checks earned on
7fcf2696were against 0.37.0 anddo not carry over; the
build (*)jobs are re-running on473e8a93.Relationship to #3075
Independent of, and mergeable in either order with, #3075 (the vLLM 0.28.0 wheel bump for LMI
29.0.0). Both are required for the LMI 29.0.0 container: #3075 moves the Python/vLLM/torch layer,
this PR moves the Java/DJL layer. #3075 is unaffected by this retarget — it is Python/wheel only.
Note that until this PR merges, a nightly build produces images tagged
0.36.0-*, since the dockertag is derived from
servinginlibs.versions.toml.