Skip to content

java: MeshJobs cancel/status/await facades (Java parity for #1074) - #1081

Merged
dhyansraj merged 1 commit into
mainfrom
feature/1080-java-mesh-jobs-facades
May 22, 2026
Merged

java: MeshJobs cancel/status/await facades (Java parity for #1074)#1081
dhyansraj merged 1 commit into
mainfrom
feature/1080-java-mesh-jobs-facades

Conversation

@dhyansraj

@dhyansraj dhyansraj commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Java parity for the mesh.jobs.cancel/status/wait facades shipped in Python (#1077) and TypeScript (#1079). Three new static methods (with overloads) on MeshJobs mirror the existing postEvent / subscribeEvents DDDI-clean pattern — callers with only a jobId no longer need to construct new JobProxy(jobId, registryUrl) and pass MCP_MESH_REGISTRY_URL explicitly.

public static void cancel(String jobId, String reason) throws MeshException
public static void cancel(String jobId) throws MeshException
public static Map<String, Object> status(String jobId) throws MeshException
public static Object await(String jobId, double timeoutSecs) throws MeshException
public static Object await(String jobId) throws MeshException

Each: resolveRegistryUrl()getOrCreateProxy() (LRU-cached) → dispatch to the existing JobProxy instance method → substring-match translation to typed exceptions via a new package-private translateJobError helper.

Java naming nuance: await (not wait) to avoid readability confusion with the inherited Object.wait() / wait(long) / wait(long, int) overload family, and to match the existing JobProxy.await(double) precedent.

What was already in place (no work needed)

  • FFI: mesh_job_proxy_cancel/status/wait in jobs_ffi.rs
  • JNR: declarations in MeshCore.java:648-670
  • JobProxy.cancel/status/await instance methods
  • JobNotFoundException, JobTerminalException, MeshException classes
  • LRU proxy cache infrastructure

Bonus generalization

resolveRegistryUrl's error message prefix generalized from "MeshJobs.postEvent:""MeshJobs:". Now accurate for all four facades. Mirrors the same generalization the Python BLOCKER fix made in #1077 and the TS bonus in #1079 — the trilogy is now consistent across all three runtimes in this respect.

Trilogy complete

Out of scope

Review Notes

Independent review: 0 BLOCKER, 1 WARNING (addressed), 4 INFOs (3 addressed, 1 skipped as docs cosmetic).

  • W1 — Triplicated try/catch boilerplate fixed: each facade's translation block collapsed from 6 lines to a one-liner throw translateJobError(exc);. The previous indirection (if (translated != exc) throw translated; throw exc;) was dead-equivalent on Java because translateJobError chains the original exception as cause via the typed constructor. Net -12 lines on the facade region.
  • I1 — await naming Javadoc accuracy: the previous text claimed "overloading would be a compile error" (wrong — wait(double) is overloading, legal). Updated wording across 3 surfaces (MeshJobs.java, JobProxy.java, jobs_java.md) to cite the real reason: readability confusion with the inherited Object.wait() overload family + the existing JobProxy.await(double) precedent.
  • I2 — Test message symmetry: JobTerminalException translation test now asserts message preservation matching the JobNotFoundException test.
  • I3 — @DisabledIfEnvironmentVariable: the three *_failsCleanlyWhenRegistryUrlUnset tests now use JUnit 5's declarative annotation instead of silent return — skips are visible in test reports, no green-test illusion in CI shells that export MCP_MESH_REGISTRY_URL.

I4 (4-column docs table) skipped as cosmetic.

Closes #1080

Test plan

  • mvn install -pl mcp-mesh-core,mcp-mesh-sdk -am: BUILD SUCCESS
  • mvn test -pl mcp-mesh-sdk: 98 tests pass (MeshJobsTest 27/27)
  • @DisabledIfEnvironmentVariable verified visible in report when env set
  • tsuite uc23_meshjob_java --parallel 4: 27/27
  • mkdocs build clean
  • Cross-runtime scope: no edits to Python / TS / Rust / FFI / JNR / JobProxy.cancel/status/await instance methods / Python jobs.md / TS jobs_typescript.md
  • Public-artifact framing: no naming downstream consumers; structural language throughout

Summary by CodeRabbit

  • New Features

    • Added convenience static methods to manage job lifecycle—cancel, check status, and await completion—using only a job ID, without requiring a JobProxy instance.
  • Documentation

    • Enhanced job lifecycle documentation with comprehensive examples, detailed timeout semantics, and cross-runtime operation guidance.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@dhyansraj has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 37 minutes and 16 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1e3fbfa-7c21-4d27-befe-007aa7f10ff5

📥 Commits

Reviewing files that changed from the base of the PR and between bfcd36a and b37638b.

📒 Files selected for processing (5)
  • docs/concepts/jobs.md
  • src/core/cli/man/content/jobs_java.md
  • src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/JobProxy.java
  • src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/MeshJobs.java
  • src/runtime/java/mcp-mesh-sdk/src/test/java/io/mcpmesh/MeshJobsTest.java
📝 Walkthrough

Walkthrough

This PR adds Java parity for job lifecycle facades introduced in Python and TypeScript. Three new static methods (cancel, status, await) on MeshJobs allow callers to drive job control using only a jobId, eliminating the need to explicitly construct JobProxy instances. The implementation resolves the registry URL, caches proxies by (registryUrl, jobId), delegates to existing proxy methods, and translates MeshException message patterns into typed exceptions. Full unit test coverage and documentation accompany the feature.

Changes

Job lifecycle facades by jobId

Layer / File(s) Summary
MeshJobs facades implementation and error translation
src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/MeshJobs.java
cancel, status, and await static methods resolve registry URL, retrieve/create cached JobProxy instances, delegate to proxy methods, and translate MeshException message substrings into typed JobNotFoundException and JobTerminalException. Helper translateJobError performs the message-based classification. Error message prefix for missing registry URL is generalized from postEvent-specific wording.
Unit tests for facades and error translation
src/runtime/java/mcp-mesh-sdk/src/test/java/io/mcpmesh/MeshJobsTest.java
New test suite validates null/empty jobId argument checks, confirms exception behavior when registry URL is unset, verifies proxy cache reuse across facade calls to the same (registryUrl, jobId), and tests translateJobError classification of message substrings into typed exceptions while preserving causes.
User-facing documentation for lifecycle facades
docs/concepts/jobs.md, src/core/cli/man/content/jobs_java.md
Conceptual docs and Java-specific guides describe the new static facade methods with code examples, operation-to-method tables, await naming rationale (vs. wait), timeout and terminal-state semantics, and guidance on when to use facades vs. direct proxy methods.
JobProxy.await() JavaDoc clarification
src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/JobProxy.java
Reworded JavaDoc explanation for await naming to focus on avoiding readability confusion with inherited wait overload family, replacing prior Object.wait() constraint phrasing.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • dhyansraj/mcp-mesh#1051: Refactors JobProxy's shared locking for lifecycle calls (cancel, status, await) that are now exposed as static facades in this PR, providing the underlying synchronization layer these new methods depend on.

Poem

🐰 A job without form, just an id so plain,
Now MeshJobs.await() makes calling less vain!
No proxy to build, no registry to find,
Three facades that handle the lifecycle assigned. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding MeshJobs cancel/status/await facades in Java with parity to Python and TypeScript implementations.
Linked Issues check ✅ Passed All coding requirements from issue #1080 are met: three static facades added with correct overloads, cached proxy resolution implemented, typed exceptions translated via substring matching, and comprehensive test coverage added.
Out of Scope Changes check ✅ Passed All changes are directly aligned with PR objectives; no out-of-scope modifications detected. Documentation, JavaDoc, implementation, and tests all focus exclusively on the MeshJobs lifecycle facades.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/1080-java-mesh-jobs-facades

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/concepts/jobs.md (1)

692-693: ⚡ Quick win

Clarify "Java parity follows" wording.

The phrase "Java parity follows" is ambiguous — it could be read as "Java parity will be added later" rather than "Java follows the same pattern". Consider rephrasing for clarity.

✏️ Suggested clarification
-resolution + cached-proxy machinery. All three runtimes ship the
-surface — Python and TypeScript landed in v2.2; Java parity follows.
+resolution + cached-proxy machinery. All three runtimes ship the
+surface with identical semantics.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/concepts/jobs.md` around lines 692 - 693, The phrase "Java parity
follows" in the sentence containing "resolution + cached-proxy machinery. All
three runtimes ship the surface — Python and TypeScript landed in v2.2; Java
parity follows." is ambiguous; update that wording to explicitly state that Java
implements the same surface/pattern rather than implying a future action—e.g.,
replace "Java parity follows" with "Java follows the same pattern" or "Java
provides the same surface" so the meaning is immediately clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/concepts/jobs.md`:
- Around line 805-807: The doc incorrectly claims Object.wait() is final; update
the text to say the Java facade is named MeshJobs.await (not wait) to avoid
readability confusion with the inherited wait overload family, and reference the
existing precedent in JobProxy.await(double); mention MeshJobs.await and
JobProxy.await by name and, if needed, point readers to the JavaDoc comments in
MeshJobs.java and JobProxy.java for the detailed rationale.

---

Nitpick comments:
In `@docs/concepts/jobs.md`:
- Around line 692-693: The phrase "Java parity follows" in the sentence
containing "resolution + cached-proxy machinery. All three runtimes ship the
surface — Python and TypeScript landed in v2.2; Java parity follows." is
ambiguous; update that wording to explicitly state that Java implements the same
surface/pattern rather than implying a future action—e.g., replace "Java parity
follows" with "Java follows the same pattern" or "Java provides the same
surface" so the meaning is immediately clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91d93a86-daaf-4b37-90cd-c831f821b981

📥 Commits

Reviewing files that changed from the base of the PR and between fb225c4 and bfcd36a.

📒 Files selected for processing (5)
  • docs/concepts/jobs.md
  • src/core/cli/man/content/jobs_java.md
  • src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/JobProxy.java
  • src/runtime/java/mcp-mesh-sdk/src/main/java/io/mcpmesh/MeshJobs.java
  • src/runtime/java/mcp-mesh-sdk/src/test/java/io/mcpmesh/MeshJobsTest.java

Comment thread docs/concepts/jobs.md Outdated
Java parity for the Python facades shipped in #1077 and TypeScript
facades in #1079. Three new static methods (with overloads) on
MeshJobs mirror the existing postEvent / subscribeEvents DDDI-clean
shape — callers with only a jobId no longer need to construct a
JobProxy(jobId, registryUrl) and pass MCP_MESH_REGISTRY_URL explicitly:

  public static void cancel(String jobId, String reason)
  public static void cancel(String jobId)
  public static Map<String, Object> status(String jobId)
  public static Object await(String jobId, double timeoutSecs)
  public static Object await(String jobId)

Each: resolveRegistryUrl() → getOrCreateProxy() (LRU-cached) → dispatch
to the existing JobProxy instance method → substring-match translation
to typed exceptions (JobNotFoundException, JobTerminalException) via a
new package-private translateJobError helper.

Java naming nuance: 'await' (not 'wait') because Object.wait() is
final. Matches the existing JobProxy.await() instance method precedent.

What was already in place (no Rust/JNR/JobProxy changes needed):
- FFI: mesh_job_proxy_cancel/status/wait in jobs_ffi.rs
- JNR: declarations in MeshCore.java:648-670
- JobProxy.cancel/status/await instance methods
- JobNotFoundException, JobTerminalException, MeshException classes
- LRU proxy cache infrastructure

Bonus generalization: resolveRegistryUrl's error message prefix
generalized from 'MeshJobs.postEvent:' → 'MeshJobs:'. Now accurate for
all four facades. Mirrors the same generalization the Python BLOCKER
fix made in #1077 and the TS bonus in #1079 — the trilogy is now
consistent across all three runtimes in this respect.

Tests (MeshJobsTest.java): 14 → 27 (+13 new). For each new facade:
arg validation (null/empty jobId), env resolution (MCP_MESH_REGISTRY_URL
missing), cache hit reuse. Plus the cross-facade shared-cache test
asserting cancel/status/await reuse one cached proxy (mirrors Python
W6 from #1077's review fixes).

await timeout: JobProxy.await contract says timeoutSecs <= 0.0 or
non-finite means 'no timeout'. The MeshJobs.await(jobId) no-arg
overload delegates to await(jobId, -1.0).

Docs: docs/concepts/jobs.md 'Lifecycle facades by jobId' section now
has Python | TypeScript | Java tabs. src/core/cli/man/content/jobs_java.md
mirrors the Python/TS variants' facade documentation.

Out of scope (separate work):
- Strongly-typed JobStatus record — returns Map<String, Object> matching
  the JobProxy.status() shape; a record with typed fields is a follow-up
  if usage warrants
- Caller authorization on lifecycle ops — design discussion at #1076

Closes #1080

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@dhyansraj
dhyansraj force-pushed the feature/1080-java-mesh-jobs-facades branch from bfcd36a to b37638b Compare May 22, 2026 17:34
@dhyansraj
dhyansraj merged commit e0399c6 into main May 22, 2026
13 checks passed
@dhyansraj
dhyansraj deleted the feature/1080-java-mesh-jobs-facades branch May 22, 2026 17:40
dhyansraj added a commit that referenced this pull request May 23, 2026
## Summary

**v2.3.0 — Lifecycle facades across the polyglot trilogy + unified
dependency-injection contract.**

v2.2 introduced the MeshJob substrate. v2.3 completes the lifecycle
surface so callers that hold only a `job_id` can drive `cancel` /
`status` / `wait` through DDDI-clean module-level facades — the same
shape `post_event` and `subscribe_events` already had. The DI rules for
`McpMeshTool` and `MeshJob` parameters are unified under a single
positional contract, eliminating a silent wrong-proxy footgun when both
types appeared in the same tool.

## What ships

### Lifecycle facades by `job_id` (Python #1077, TS #1079, Java #1081)

| Operation | Python | TypeScript | Java |
| ------------------------ |
-------------------------------------------------- |
------------------------------------------------ |
--------------------------------------------- |
| Cancel a running job | `await mesh.jobs.cancel(job_id, reason=None)` |
`await mesh.jobs.cancel(jobId, reason?)` | `MeshJobs.cancel(jobId[,
reason])` |
| Read latest job state | `await mesh.jobs.status(job_id)` | `await
mesh.jobs.status(jobId)` | `MeshJobs.status(jobId)` |
| Wait for terminal state | `await mesh.jobs.wait(job_id,
timeout_secs=None)` | `await mesh.jobs.wait(jobId, timeoutSecs?)` |
`MeshJobs.await(jobId[, timeoutSecs])` |

Underlying `JobProxy.cancel/status/wait` was already shipped in v2.2;
this adds the module-level wrappers that resolve the registry URL
internally — no more `JobProxy(jobId, registryUrl)` plumbing in user
code. Typed errors (`JobNotFoundError` / `JobTerminalError`) translate
consistently across all three runtimes. TS adds a typed `JobStatus`
interface exported from `mesh.jobs`.

### Unified positional dependency injection (#1082, Python)

`McpMeshTool` and `MeshJob` parameters now share a **single positional
`dep_index` namespace** in parameter declaration order. Each
`dependencies[i]` strictly pairs with one parameter position; the slot's
type determines what gets constructed. Previously, the two types had
inconsistent injection rules (positional for `McpMeshTool`, by-name for
`MeshJob`), producing wrong-proxy injection when both appeared in the
same tool with the `MeshJob` capability listed first in
`dependencies[]`.

**Behavior change to call out**: users who deliberately wrote `MeshJob`
params out-of-order with their `dependencies[]` array (relying on the
previous by-name resolution) now need to put params in the same order as
deps. The natural same-order case continues to work unchanged.

TypeScript and Java SDK DI paths still follow the orthogonal injection
contract — their port to the unified positional rule is tracked
separately.

### `health_check_ttl` refresh on the user loop (#1073)

`@mesh.agent(health_check=fn, health_check_ttl=N)` now actually
refreshes every N seconds. Previously, the result was stored exactly
once at startup and served forever — a failed check during startup
cached as unhealthy and permanently failed k8s readiness probes. The
refresh loop runs on the user loop (same loop as `lifespan` and tools)
so health checks touching loop-bound resources work without cross-loop
errors. A lifespan-ready signal gates the refresh start so iterations
don't fire during user `__aenter__`.

### FastMCP lifespan documentation correction (#1073)

The v2.2.4 "Loop topology" docs incorrectly showed a FastAPI-style
`app.state.pool` example — FastMCP's lifespan receives a server
instance, not a FastAPI app. Three doc surfaces rewritten to the
canonical module-level globals pattern.

## Mechanical bundle

- `scripts/bump_version.py 2.2.4 → 2.3.0` (419 files updated across 36
categories)
- `helm dependency update helm/mcp-mesh-core`
- `cargo generate-lockfile` (src/runtime/core)
- `RELEASE_NOTES.md` — new v2.3.0 entry with full narrative

## Post-merge

Per the established v2.2.3 / v2.2.4 publish pattern:

1. `/dev` reset to pull the merged main
2. `gh release create v2.3.0 --target main --title "v2.3.0" --notes-file
<v2.3.0 section> --latest` (one-shot)
3. Wait for all 4 publish jobs to land (npm × 7 packages, PyPI, Maven
Central, crates.io)
4. Verify `npm view @mcpmesh/core version` returns `2.3.0`

## Test plan

- [x] `mvn install` BUILD SUCCESS for Java
- [x] Python unit suite: 1010/1010
- [x] uc02_agent_lifecycle: 23/23
- [x] uc21_meshjob (Python): 21/21
- [x] uc22_meshjob_ts: 24/24
- [x] uc23_meshjob_java: 27/27
- [x] `mkdocs build` clean
- [ ] Release workflow fires cleanly across all 4 registries

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

java: MeshJobs cancel/status/await facades (Java parity for #1074)

1 participant