# Java Client The Java client workspace contains the MXAccess Gateway client library, generated protobuf/gRPC bindings, a Picocli test CLI project, and JUnit tests. ## Layout ```text clients/java/ settings.gradle build.gradle src/main/generated/ zb-mom-ww-mxgateway-client/ zb-mom-ww-mxgateway-cli/ ``` `zb-mom-ww-mxgateway-client` generates Java protobuf and gRPC sources from `../../src/ZB.MOM.WW.MxGateway.Contracts/Protos`. The Gradle protobuf plugin writes those generated sources under `src/main/generated`, which matches the client proto manifest in `../proto/proto-inputs.json`. Do not edit generated files by hand. `zb-mom-ww-mxgateway-client` exposes `MxGatewayClientOptions`, `MxGatewayClient`, `MxGatewaySession`, value/status helpers, typed gateway exceptions, raw generated stubs, and generated protobuf messages for parity tests. `zb-mom-ww-mxgateway-cli` depends on `zb-mom-ww-mxgateway-client` and provides the `mxgw-java` application entry point. The CLI supports version, session, command, event streaming, write, and smoke-test commands with deterministic JSON output. ## Regenerating Protobuf Bindings Run generation from `clients/java` after the shared `.proto` files or Java output path changes: ```powershell gradle :zb-mom-ww-mxgateway-client:generateProto ``` ## Client Usage Create a client with explicit transport and auth options: ```java MxGatewayClientOptions options = MxGatewayClientOptions.builder() .endpoint("localhost:5000") .apiKey(System.getenv("MXGATEWAY_API_KEY")) .plaintext(true) .build(); try (MxGatewayClient client = MxGatewayClient.connect(options); MxGatewaySession session = client.openSession("java-client")) { int serverHandle = session.register("java-client"); int itemHandle = session.addItem(serverHandle, "TestObject.TestInt"); session.advise(serverHandle, itemHandle); session.write(serverHandle, itemHandle, MxValues.int32Value(123), 0); } ``` Use `rawBlockingStub`, `rawFutureStub`, `rawAsyncStub`, `openSessionRaw`, `closeSessionRaw`, `invoke`, and raw session helper methods when tests need the underlying protobuf messages. `MxGatewayCommandException` and `MxAccessException` preserve the raw `MxCommandReply` when the gateway returns a data-bearing MXAccess failure. `MxEventStream` implements `Iterator` and `AutoCloseable`. Closing it cancels the underlying gRPC stream. Canceling or timing out a Java client call only stops the client from waiting; it does not abort an in-flight MXAccess COM call on the worker STA. ## Galaxy Repository Browse The Galaxy Repository service is a separate metadata-only gRPC service exposed by the gateway. It lets clients enumerate the deployed Galaxy object hierarchy and the dynamic attributes on each object so they know which tag references to subscribe to via the MXAccess Gateway service. It uses the same API-key auth as the gateway and requires the `metadata:read` scope. `GalaxyRepositoryClient` mirrors the `MxGatewayClient` pattern (caller-managed or owned channel, `MxGatewayClientOptions`, blocking + async variants). Three RPCs are exposed: ```java MxGatewayClientOptions options = MxGatewayClientOptions.builder() .endpoint("localhost:5000") .apiKey(System.getenv("MXGATEWAY_API_KEY")) .plaintext(true) .build(); try (GalaxyRepositoryClient galaxy = GalaxyRepositoryClient.connect(options)) { boolean ok = galaxy.testConnection(); Optional lastDeploy = galaxy.getLastDeployTime(); List hierarchy = galaxy.discoverHierarchy(); } ``` `getLastDeployTime` returns `Optional.empty()` when the server reports `present=false`. `discoverHierarchy` returns the generated `GalaxyObject` proto messages directly so callers can read all fields (including the nested `GalaxyAttribute` list) without an extra DTO layer. The CLI exposes matching subcommands: `galaxy-test`, `galaxy-deploy-time`, `galaxy-discover`, and `galaxy-watch`. They take the same `--endpoint`, `--api-key-env`, `--plaintext`, `--ca-file`, `--server-name-override`, `--timeout`, and `--json` options as the gateway commands. ```powershell gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-test --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json" gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-deploy-time --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json" gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-discover --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json" ``` ### Watching deploy events `GalaxyRepository.WatchDeployEvents` is a server-streaming RPC: the gateway sends a bootstrap `DeployEvent` immediately on subscribe and then one event each time it observes a new `galaxy.time_of_last_deploy`. The `sequence` field is monotonic per server start; gaps mean the per-subscriber buffer dropped older events because the consumer was too slow. The client exposes both an iterator-style adaptor over the async stub and an observer-callback variant. Both honour the channel-level `streamTimeout`. ```java try (GalaxyRepositoryClient galaxy = GalaxyRepositoryClient.connect(options); DeployEventStream events = galaxy.watchDeployEvents(/* lastSeenDeployTime */ null)) { while (events.hasNext()) { DeployEvent event = events.next(); // event.getSequence(), event.getObservedAt(), // event.getTimeOfLastDeploy() / getTimeOfLastDeployPresent(), // event.getObjectCount(), event.getAttributeCount() } } ``` Pass an `Instant` for `lastSeenDeployTime` to suppress the bootstrap event when the cached deploy time matches what the caller already has. `DeployEventStream` implements `Iterator` and `AutoCloseable`; closing it cancels the underlying gRPC call. For callback delivery (e.g. when the consumer wants to drive a queue or reactive pipeline), use the async variant: ```java DeployEventSubscription subscription = galaxy.watchDeployEventsAsync( lastSeen, new StreamObserver<>() { @Override public void onNext(DeployEvent value) { /* ... */ } @Override public void onError(Throwable t) { /* ... */ } @Override public void onCompleted() { /* ... */ } }); // later: subscription.cancel(); // or subscription.close() ``` The matching CLI subcommand streams events until cancelled (Ctrl+C) and prints one line per event in text mode or one JSON object per event with `--json`: ```powershell gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json" gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --last-seen-deploy-time 2026-04-28T18:30:00Z --limit 5" ``` ## CLI Usage Run the CLI through Gradle: ```powershell gradle :zb-mom-ww-mxgateway-cli:run --args="version --json" gradle :zb-mom-ww-mxgateway-cli:run --args="open-session --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --client-session-name java-cli --json" gradle :zb-mom-ww-mxgateway-cli:run --args="register --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id --client-name java-cli --json" gradle :zb-mom-ww-mxgateway-cli:run --args="add-item --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id --server-handle 1 --item TestObject.TestInt --json" gradle :zb-mom-ww-mxgateway-cli:run --args="advise --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id --server-handle 1 --item-handle 1 --json" gradle :zb-mom-ww-mxgateway-cli:run --args="write --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id --server-handle 1 --item-handle 1 --type int32 --value 123 --json" gradle :zb-mom-ww-mxgateway-cli:run --args="stream-events --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id --limit 1 --json" gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestObject.TestInt --json" ``` The CLI accepts `--api-key`, `--api-key-env`, `--plaintext`, `--ca-file`, `--server-name-override`, `--timeout`, and `--json` on gateway commands. JSON output redacts API keys. Use TLS options for a secured gateway: ```powershell gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item TestObject.TestInt --json" ``` ## Build And Test Run the Java checks from `clients/java`: ```powershell gradle test ``` The build uses the Java 21 Gradle toolchain, compiles generated protobuf/gRPC code, and runs JUnit 5 tests for the client wrapper, shared behavior fixtures, in-process gRPC behavior, stream cancellation, and CLI parser/output behavior. ## Packaging Create local library and CLI artifacts from `clients/java`: ```powershell gradle :zb-mom-ww-mxgateway-client:jar :zb-mom-ww-mxgateway-cli:installDist ``` The library jar is under `zb-mom-ww-mxgateway-client/build/libs`. The installed CLI distribution is under `zb-mom-ww-mxgateway-cli/build/install/zb-mom-ww-mxgateway-cli`. ## Integration Checks Run live checks only when a gateway and MXAccess-backed worker are available: ```powershell $env:MXGATEWAY_INTEGRATION = '1' $env:MXGATEWAY_ENDPOINT = 'localhost:5000' $env:MXGATEWAY_API_KEY = '' $env:MXGATEWAY_TEST_ITEM = 'TestObject.TestInt' gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json" ``` ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) - [Client Proto Generation](../../docs/ClientProtoGeneration.md) - [Java Client Detailed Design](./JavaClientDesign.md) - [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md)