Skip to content

Bump project to Java 17#779

Merged
donald-pinckney merged 2 commits intomainfrom
d/bump-java-17
May 1, 2026
Merged

Bump project to Java 17#779
donald-pinckney merged 2 commits intomainfrom
d/bump-java-17

Conversation

@donald-pinckney
Copy link
Copy Markdown
Contributor

@donald-pinckney donald-pinckney commented May 1, 2026

Summary

Spring AI work surfaced that the project's CI and build settings still pinned Java 11 in several places. This PR moves to Java 17 everywhere:

  • docker/github/Dockerfile: eclipse-temurin:11-focal:17-jammy (image used by the unittest CI job)
  • root build.gradle: drop the Spring-Boot-version-conditional Java target (Spring Boot 2.7 runs on Java 17 fine)
  • springboot-basic/build.gradle: drop a dead isJava11Compatible() branch with identical arms
  • ci.yml: keep Java 17 on the Code format job (cherry-picked from the in-flight Spring AI samples PR), clean up the inline comment that framed it as a springai-only exception

Split out from #775 so the Java-version bump can be reviewed independently.

Test plan

  • ./gradlew clean spotlessCheck compileJava passes locally
  • CI green on this branch

donald-pinckney and others added 2 commits May 1, 2026 11:38
The springai sample modules require Java 17 (Spring AI 1.1 needs
Spring Boot 3.x, which needs Java 17). Running spotlessCheck on
Java 11 made google-java-format choke on text blocks in those
sources with "unclosed string literal" errors. Bump the format
job's setup-java to 17 so it can parse the springai sources.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spring AI requires Java 17, but parts of the build (CI Docker image,
root Java target, format-job JDK) were still pinned to Java 11. The
follow-on for the spring-ai work was a partial bump that only fixed
the format job. This finishes the bump everywhere:

- docker/github/Dockerfile: eclipse-temurin:11-focal -> :17-jammy.
  This is the image the unittest CI job runs ./gradlew test inside.
- build.gradle: drop the Spring-Boot-version-conditional Java target
  (Spring Boot 2.7 runs fine on Java 17, so there's no reason to
  pin source/target compatibility to 11).
- springboot-basic/build.gradle: drop a dead isJava11Compatible()
  branch (both arms were identical).
- ci.yml: keep Java 17 for the format job; clean up the inline
  comment that framed it as a springai-only exception.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@donald-pinckney donald-pinckney requested review from a team, antmendoza and tsurdilo as code owners May 1, 2026 15:39
@donald-pinckney donald-pinckney enabled auto-merge (squash) May 1, 2026 15:45
@donald-pinckney donald-pinckney merged commit c4f8e78 into main May 1, 2026
10 checks passed
@donald-pinckney donald-pinckney deleted the d/bump-java-17 branch May 1, 2026 15:45
@donald-pinckney donald-pinckney mentioned this pull request May 1, 2026
4 tasks
donald-pinckney added a commit that referenced this pull request May 1, 2026
Adds Spring AI to the module list at the top, describes the four
nested samples (basic, mcp, multimodel, rag) under a new "Running
Spring AI Samples" section, and consolidates the per-sample Java
version notes into a single "Java 17+ for all samples" line now
that #779 bumped the project-wide target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
donald-pinckney added a commit that referenced this pull request May 1, 2026
* Spring AI samples

* Fix sample configs and remove runtimeOnly workarounds

- Remove runtimeOnly spring-ai-rag and spring-ai-mcp from shared
  config (no longer needed after T6 plugin split in sdk-java)
- Fix workflow class package references (old prototype packages)
- Add web-application-type: none to RAG and multimodel configs
- Exclude conflicting chat auto-configs in multimodel sample

All 5 samples now boot successfully against a Temporal dev server.
MCP sample requires Node.js/npx for the MCP server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update samples for T15: remove @DeterministicTool and sandboxing sample

- StringTools: remove @DeterministicTool (plain tools run in workflow
  context by default now)
- Delete springai-sandboxing sample (SandboxingAdvisor removed from SDK)
- Update comments referencing @DeterministicTool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clean up Spring AI samples PR for merge

- Remove committed build/ directories and add them to .gitignore.
- Gate includeBuild('../sdk-java') on sibling checkout existence so the
  build works out-of-the-box when the springai samples are not being
  touched (and will resolve cleanly once temporal-spring-ai publishes).
- core: pin io.grpc:grpc-util version explicitly and switch the SSL sample
  to AdvancedTlsX509KeyManager.updateIdentityCredentials (non-deprecated
  API) to fix compilation under -Werror with the composite-build grpc.
- springai-multimodel: fix doc mismatch on model names, route unprefixed
  input to the "default" model, drop hard-coded model-version hints from
  system prompts.
- springai-rag: correct Javadoc to match actual code (no EmbeddingModelActivity),
  accept bare "ask" as a usage-prompt trigger, normalize text block indentation.
- springai-mcp: fix advisor name comment, normalize text block indentation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use mavenLocal for temporal-spring-ai instead of composite build

Replace the includeBuild('../sdk-java') dependency-substitution block with
a plain mavenLocal() repository and a pinned 1.35.0-SNAPSHOT coordinate for
the springai* samples. Build temporal-spring-ai locally with
`./gradlew publishToMavenLocal` in an sdk-java checkout and the samples
pick it up via mavenLocal — no composite build, no SDK-wide substitution.

A nice side-effect: the core module no longer has a newer grpc forced onto
its classpath, so the SSL sample's gRPC workarounds (explicit grpc-util
dep + updateIdentityCredentials switch) are no longer needed. Revert them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add TASK_QUEUE.json tracking remaining PR work

Tracks the mavenLocal workaround unwind (blocked on temporal-spring-ai
publishing) and unaddressed reviewer threads. Delete before merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Updated after changes to Spring AI

* Add snipsync markers to Spring AI samples

Wraps natural snippet boundaries in the spring-ai samples so the
docs/spring-ai-integration page can pull canonical code via snipsync
instead of carrying inline copies. Six markers added:

- samples-java-spring-ai-chat-workflow-init: ChatWorkflowImpl @WorkflowInit
- samples-java-spring-ai-activity-tool: WeatherActivity interface
- samples-java-spring-ai-side-effect-tool: TimestampTools class
- samples-java-spring-ai-plain-tool: StringTools class
- samples-java-spring-ai-per-model-options: ChatModelConfig per-model bean
- samples-java-spring-ai-provider-options: MultiModelWorkflowImpl think route

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* spring-ai samples: pin to released temporal-spring-ai 1.35.0

temporal-spring-ai is now on Maven Central. Drop the mavenLocal()
repository and the SNAPSHOT pin that were bridging the gap; the
springai* samples now resolve from mavenCentral like everything else.
springAiSdkVersion stays separate from javaSDKVersion because the
spring-ai module requires a newer SDK than the rest of the samples
currently pin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Group spring-ai samples under a single springai/ directory

Moves the four spring-ai sample modules from top-level peers into
nested subprojects of springai/, so the repo root has one springai/
instead of four (springai, springai-mcp, springai-multimodel,
springai-rag). Each module keeps its own build.gradle and Spring Boot
entry point — only the directory layout and Gradle project paths
change. Invocations become :springai:basic, :springai:mcp, etc.

Also folds in the earlier simplification: now that javaSDKVersion is
1.35.0 globally, gradle/springai.gradle drops the separate
springAiSdkVersion variable and references the global one directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/mcp: await initialization in chat signal handler

Replaces the early-return-with-placeholder pattern in McpWorkflowImpl.chat()
with Workflow.await(() -> initialized). Signals can yield, so awaiting is
the idiomatic Temporal way to handle "operation arrived before init
finished" — the client's signal RPC has already returned, and the
workflow-side handling just resumes once run() completes MCP tool
discovery and finishes building the chat client.

listTools() stays placeholder-based because it's a @QueryMethod and
queries cannot yield.

Addresses brianstrauch review comment on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/multimodel: drop redundant default: CLI prefix

The CLI exposed both `default:` and `openai:` prefixes, which both
ended up calling OpenAI — ChatModelConfig declares @primary on
openAiChatModel, so ActivityChatModel.forDefault() resolves to it.
Reviewers reasonably found this confusing.

Drop the explicit `default:` prefix; route no-prefix input to the
"default" workflow client instead. The chatClients entry stays so
the sample still demonstrates both forDefault() (@primary resolution)
and forModel(name) (explicit lookup) — added a comment block at the
registration site explaining that the dual entry is intentional.

Addresses brianstrauch review comment on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/rag: show usage when add/search are typed without args

Reviewer noted that bare `ask` falls through to printing usage but
bare `add` or `search` did not — the startsWith check required a
trailing space. Apply the same equals|startsWith pattern that the
ask branch already uses, with a length guard around the substring.

Addresses brianstrauch review comment on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add ai-sdk to CODEOWNERS for springai

* springai/multimodel: include "think" in chat() javadoc model names

The javadoc listed "openai", "anthropic", "default" but the workflow
also accepts "think" (Anthropic with extended thinking enabled).
Add it to the @param doc so users don't send a name and wonder why
it falls through to the unknown-model branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/basic: explain why run()'s systemPrompt parameter is unused

@WorkflowInit requires the constructor and the @WorkflowMethod to
share a parameter list, so run(String) must take systemPrompt even
though only the constructor uses it. Add a comment so readers don't
trip over the apparent unused parameter (and so static analyzers
that flagged it have an explanation in-source).

Addresses Copilot review on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* gradle/springai: document the Spring Boot plugin/BOM version skew

The root build.gradle pins the Spring Boot Gradle plugin at
springBootPluginVersion (2.7.13) for the legacy springboot/ samples,
while Spring AI 1.1.0 needs Spring Boot 3.5.x via BOM import. The
plugin and BOM are independent enough that this works in practice,
but a future reader (or static analyzer) reasonably gets confused.

Add a comment block explaining the trade-off and naming the two
follow-up paths that would actually fix it (move plugin out of root
plugins block, or migrate legacy springboot/ to 3.x).

Addresses Copilot review on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* .gitignore: collapse per-module build/out entries into globs

Replaces twelve explicit per-module entries (/build, /core/build,
/springai/basic/build, ...) with two globs (**/build/, **/out/) so
new sample modules don't need a .gitignore edit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* README: document Spring AI samples and Java 17 requirement

Adds Spring AI to the module list at the top, describes the four
nested samples (basic, mcp, multimodel, rag) under a new "Running
Spring AI Samples" section, and consolidates the per-sample Java
version notes into a single "Java 17+ for all samples" line now
that #779 bumped the project-wide target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai samples: address Copilot review nits

Five small fixes flagged in PR #775 review:

- Rename springai/basic/src/main/resources/application.yml to .yaml
  to match the convention used by every other sample in the repo.
- Fix the @code ./gradlew :example-* paths in McpApplication,
  RagApplication, and MultiModelApplication javadocs to the actual
  module paths (:springai:mcp, :springai:rag, :springai:multimodel).
- Add spring.main.web-application-type: none to springai/mcp's
  application.yaml. The module pulls in spring-boot-starter-webflux
  which would otherwise spin up a reactive web server we don't want.
  Matches the other three springai samples.
- Drop stale references to EmbeddingModelActivity from the
  RagApplication javadoc and startup banner. The sample uses
  VectorStoreActivity exclusively; embeddings are produced inside
  the configured Spring AI VectorStore, not via a separate activity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Untrack .vscode/ IDE settings

Slipped into the previous commit via git add -A. .vscode/ is
per-developer IDE config — add to .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* undo gitignore change

* springai/multimodel: use LinkedHashMap for chatClients

The unknown-model error message renders chatClients.keySet() into
lastResponse, which is workflow state. HashMap iteration order isn't
guaranteed across JVMs, so a replay on a different worker could
produce a different rendered string and surface as a non-determinism
error. LinkedHashMap iterates in insertion order, which is
deterministic regardless of JVM.

Addresses Copilot review on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/rag: capture lastResponse before signaling, not after

waitForResponse() previously read getLastResponse() *after* the
signal had been sent. If the workflow processed the signal between
the signal call and the first poll, the captured baseline was
already the new response and the loop waited for a second change
that never came, then timed out.

Fix matches the pattern McpApplication uses: capture
previousResponse before the signal, pass it into waitForResponse,
and poll for a value different from that pre-signal baseline.

Addresses Copilot review on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* springai/multimodel: capture previous response before signaling

Same bug pattern that RagApplication had: the polling loop compared
against an empty-string baseline, so the very first poll could
return the stale prior response and print it as if it were the new
one. Capture previousResponse before sending the chat signal and
wait until getLastResponse() differs from that pre-signal baseline.

Also drop the redundant initial Thread.sleep(100) — the loop's own
sleep handles backoff, and reading immediately is fine when we're
comparing against the pre-signal baseline.

Addresses Copilot review on PR #775.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Remove TASK_QUEUE.json

Internal tracking file scoped to PR #775. All listed tasks and
review threads have been resolved or replied to; the file's job
is done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update README.md

Co-authored-by: Maciej Dudkowski <maciej.dudkowski@temporal.io>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Maciej Dudkowski <maciej.dudkowski@temporal.io>
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.

2 participants