37cb3b0df8
All five client CLIs now share one credential contract for `authenticate-user`: flags `--password` / `--password-env` (Go: `-password` / `-password-env`) with default env `MXGATEWAY_VERIFY_PASSWORD`, resolution flag-then-env, and a resolved credential that is missing *or empty* is a usage error naming the flag and the variable. The value is never echoed and never reaches the wire. Go and Java previously sent an empty credential when the variable was unset, turning a misconfigured environment into a real MXAccess authentication attempt. Go now returns the guard error before dialing; Java throws a picocli ParameterException instead of falling back to "". Python's `--password-env` gained the canonical default and its UsageError names the resolved variable. Rust treats an empty flag or env value as missing, with the resolution extracted into a testable `resolve_verify_user_password`. .NET adopts the canonical flags and keeps `--verify-user-password`, `--verify-user-password-env`, and MXGATEWAY_VERIFY_USER_PASSWORD as deprecated aliases for one release. Docs same commit: CrossLanguageSmokeMatrix.md gains the credential contract and the per-CLI subcommand-coverage table (the documented-not-fixed half of the finding); all five READMEs name the canonical variable and the fail-fast rule, and the .NET README carries the deprecation note. Tracking flipped to Done in both remediation registers with a change-log row. No .proto changed; no generated code regenerated.
481 lines
22 KiB
Markdown
481 lines
22 KiB
Markdown
# 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);
|
|
}
|
|
```
|
|
|
|
The gateway can auto-generate its own self-signed certificate (it has no PKI), so
|
|
the client is **lenient by default**: a TLS connection (`plaintext(false)`) with
|
|
no `caCertificatePath` accepts whatever certificate the gateway presents (via
|
|
grpc-netty-shaded's `InsecureTrustManagerFactory`). To verify instead, set
|
|
`caCertificatePath` to pin a CA, or set `requireCertificateValidation(true)` to
|
|
verify against the JVM trust store without pinning. Use `serverNameOverride` /
|
|
`--server-name-override` when the dialed host differs from the certificate SAN.
|
|
See
|
|
[Gateway Configuration](../../docs/GatewayConfiguration.md#automatic-self-signed-certificate).
|
|
|
|
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<MxEvent>` 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. It is a **single-consumer** surface: drive
|
|
`hasNext()`/`next()` (or `nextItem()`) from one thread only.
|
|
|
|
### Reconnect-replay gap signal
|
|
|
|
When you resume a stream with `streamEventsAfter(afterWorkerSequence)` and the
|
|
requested cursor predates the oldest event the gateway still retains, the
|
|
gateway emits a single **replay-gap sentinel** at the head of the stream: an
|
|
`MxEvent` with its `replay_gap` field set, `family` unspecified, and the body
|
|
oneof unset. It means "you missed events — discard cached state and
|
|
re-snapshot": the events in the open interval `(requested_after_sequence,
|
|
oldest_available_sequence)` were evicted and cannot be replayed. The gateway
|
|
never synthesizes this signal from anything else, and the client never swallows
|
|
it.
|
|
|
|
Use `nextItem()` to branch on it as a distinct typed item; `next()` still
|
|
returns the sentinel as a plain `MxEvent` (test `event.hasReplayGap()`). After a
|
|
gap, re-snapshot, then resume without another gap by requesting
|
|
`oldestAvailableSequence - 1` as the next `afterWorkerSequence`:
|
|
|
|
```java
|
|
try (MxEventStream events = session.streamEventsAfter(lastSeenSequence)) {
|
|
while (events.hasNext()) {
|
|
MxEventStreamItem item = events.nextItem();
|
|
if (item.isReplayGap()) {
|
|
long resumeFrom = item.replayGap().getOldestAvailableSequence() - 1;
|
|
// discard cached per-item state, re-snapshot, then resume from resumeFrom
|
|
continue;
|
|
}
|
|
MxEvent event = item.event();
|
|
// normal event handling
|
|
}
|
|
}
|
|
```
|
|
|
|
For alarms, `MxGatewayClient` exposes `queryActiveAlarms` (one-shot snapshot),
|
|
`streamAlarms` (returns an `MxGatewayAlarmFeedSubscription` whose iterator
|
|
yields alarm-feed messages from the gateway's central monitor), and
|
|
`acknowledgeAlarm` (ack by full alarm reference with an optional comment and
|
|
ack target). Close the subscription to cancel the underlying gRPC stream.
|
|
|
|
## Write Semantics And Common Pitfalls
|
|
|
|
These are MXAccess parity behaviors that surprise new callers. The gateway
|
|
forwards them unchanged — it does not paper over them.
|
|
|
|
### Typed single-item command helpers
|
|
|
|
`MxGatewaySession` exposes typed helpers for the parity-critical single-item
|
|
commands, so you do not need to build raw `MxCommand` messages:
|
|
|
|
- `adviseSupervisory(serverHandle, itemHandle)` (and `adviseSupervisoryRaw`)
|
|
- `writeSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value)`
|
|
and `writeSecured2(..., timestampValue)` (plus `*Raw` variants)
|
|
- `authenticateUser(serverHandle, verifyUser, verifyUserPassword)` → user id
|
|
- `archestrAUserToId(serverHandle, userIdGuid)` → user id
|
|
- `addBufferedItem(serverHandle, itemDefinition, itemContext)` → item handle
|
|
- `setBufferedUpdateInterval(serverHandle, updateIntervalMs)`
|
|
- `suspend(serverHandle, itemHandle)` / `activate(serverHandle, itemHandle)` →
|
|
the reply's `MxStatusProxy`
|
|
|
|
All of them run the same MXAccess reply validation as the bulk helpers (protocol
|
|
status plus HRESULT/`MxStatusProxy` check) via the shared `invoke` path, so an
|
|
MXAccess COM-side failure surfaces as `MxAccessException`.
|
|
|
|
**Secret redaction.** Credentials passed to `authenticateUser` (and the
|
|
credential-sensitive values passed to `writeSecured`/`writeSecured2`) travel
|
|
only in the request. They never appear in logs, exception messages, or
|
|
`toString()`: gateway status text is scrubbed through `MxGatewaySecrets`, and
|
|
MXAccess failures carry only the reply (never the request). Do not log the
|
|
credentials yourself.
|
|
|
|
### Attributing a write to a user without `authenticateUser`
|
|
|
|
MXAccess only stamps a plain `write`/`write2` with a Galaxy user id when the
|
|
item carries an active *supervisory* advise. If you are **not** using the
|
|
verified/secured path (`authenticateUser` → `writeSecured`/`writeSecured2`) but
|
|
still need the write attributed to a user id, first advise the item supervisory
|
|
and then pass that user id on the write. Without the supervisory advise the
|
|
`userId` on a plain write is ignored.
|
|
|
|
```java
|
|
session.adviseSupervisory(serverHandle, itemHandle);
|
|
session.write(serverHandle, itemHandle, value, userId);
|
|
```
|
|
|
|
**MXAccess parity:** `writeSecured` failing before a prior `authenticateUser` +
|
|
`adviseSupervisory`, or before a value-bearing body, is correct behavior — the
|
|
native failure is surfaced, not papered over.
|
|
|
|
The CLI exposes `advise-supervisory`, `write-secured`, and `authenticate-user`
|
|
(credential via `--password` or the variable named by `--password-env`, default
|
|
`MXGATEWAY_VERIFY_PASSWORD`, never echoed), and `write` / `write2` take
|
|
`--user-id`. The credential is required: a missing or empty resolved value is a
|
|
picocli usage error naming the option and the variable, so the CLI fails before
|
|
connecting instead of authenticating with an empty password.
|
|
`MXGATEWAY_VERIFY_PASSWORD` is the canonical variable across all five client
|
|
CLIs — see [Cross-Language Smoke Matrix](../../docs/CrossLanguageSmokeMatrix.md).
|
|
|
|
### Array writes replace the whole array
|
|
|
|
A write to an array attribute **replaces the entire array**; it is not an
|
|
element-wise patch. To change a subset of elements, send the full array with
|
|
the unchanged elements included. For example, to change 2 elements of a
|
|
20-element array, build the `MxValue` from all 20 values (the 18 unchanged plus
|
|
the 2 new ones). Sending only the 2 changed values overwrites the attribute
|
|
with a 2-element array.
|
|
|
|
When only a few indices need changing and the rest should be reset to the
|
|
element type's default, use `writeArrayElements` instead of building the full
|
|
array manually:
|
|
|
|
```java
|
|
session.writeArrayElements(
|
|
serverHandle, itemHandle,
|
|
MxDataType.MX_DATA_TYPE_INTEGER,
|
|
20, // totalLength
|
|
Map.of(
|
|
2, MxValues.int32Value(42),
|
|
7, MxValues.int32Value(99)),
|
|
userId);
|
|
```
|
|
|
|
The gateway expands the sparse descriptor into a full `totalLength`-element
|
|
array before forwarding to the worker. Indices not listed in the map are
|
|
written as the element type's default — this is a **reset**, not a preserve;
|
|
current values at those positions are discarded. `totalLength` is required and
|
|
must match the declared length of the array attribute. Bare-name array items
|
|
(`Area001.Pump001.Speed`) are auto-normalized to the `[]` form across the whole
|
|
add family — `AddItem`, `AddItem2`, `AddItemBulk`, and `AddBufferedItem` — so the
|
|
array attribute accepts the write.
|
|
|
|
## 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<Instant> lastDeploy = galaxy.getLastDeployTime();
|
|
List<GalaxyObject> 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-connection`,
|
|
`galaxy-last-deploy`, `galaxy-discover`, `galaxy-browse`, and `galaxy-watch`.
|
|
The short names `galaxy-test` and `galaxy-deploy-time` remain as deprecated
|
|
aliases for `galaxy-test-connection` and `galaxy-last-deploy` so existing
|
|
scripts keep working. 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-connection --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-last-deploy --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"
|
|
```
|
|
|
|
`galaxy-browse` walks the hierarchy via `BrowseChildren`. Without `--parent` it
|
|
returns the root nodes and eagerly expands `--depth` further levels; with
|
|
`--parent <gobject-id>` it returns exactly one level of children for that
|
|
parent. The filter flags (`--category-ids`, `--template-contains`,
|
|
`--tag-name-glob`, `--alarm-bearing-only`, `--historized-only`,
|
|
`--include-attributes`) match `galaxy-discover`. The `--json` node shape is the
|
|
cross-client browse surface: the flattened object fields plus a
|
|
`hasChildrenHint` flag and a nested `children` array.
|
|
|
|
```powershell
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-browse --depth 1 --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
|
```
|
|
|
|
### Browsing lazily
|
|
|
|
For UI trees or OPC UA bridges, use `browseChildrenRaw` to walk one level at a
|
|
time instead of loading the full hierarchy with `discoverHierarchy`. Pass a
|
|
default request for root objects; subsequent calls set `parentGobjectId`,
|
|
`parentTagName`, or `parentContainedPath`. Filter fields match
|
|
`DiscoverHierarchy`. Each response pairs `getChildrenList()` with
|
|
`getChildHasChildrenList()` so you know which nodes to expand. See
|
|
[Galaxy Repository](../../docs/GalaxyRepository.md#browsechildren) for full
|
|
request and filter semantics. For most callers the high-level
|
|
`browse()`/`LazyBrowseNode` walker below is the preferred surface;
|
|
`browseChildrenRaw` exposes the single underlying RPC when you need direct
|
|
control of paging.
|
|
|
|
```java
|
|
BrowseChildrenReply reply = galaxy.browseChildrenRaw(
|
|
BrowseChildrenRequest.newBuilder().build());
|
|
|
|
List<GalaxyObject> children = reply.getChildrenList();
|
|
List<Boolean> hasChildren = reply.getChildHasChildrenList();
|
|
for (int i = 0; i < children.size(); i++) {
|
|
System.out.printf("%s expand=%b%n", children.get(i).getTagName(), hasChildren.get(i));
|
|
}
|
|
```
|
|
|
|
#### High-level walker
|
|
|
|
For UI trees, the client provides a `LazyBrowseNode` walker that handles
|
|
sibling pagination and the `child_has_children` hint for you:
|
|
|
|
```java
|
|
MxGatewayClientOptions options = MxGatewayClientOptions.builder()
|
|
.endpoint("localhost:5000")
|
|
.apiKey(System.getenv("MXGATEWAY_API_KEY"))
|
|
.plaintext(true)
|
|
.build();
|
|
|
|
try (GalaxyRepositoryClient galaxy = GalaxyRepositoryClient.connect(options)) {
|
|
List<LazyBrowseNode> roots = galaxy.browse();
|
|
for (LazyBrowseNode root : roots) {
|
|
if (root.hasChildrenHint()) {
|
|
root.expand();
|
|
}
|
|
for (LazyBrowseNode child : root.getChildren()) {
|
|
String kind = child.hasChildrenHint() ? "has children" : "leaf";
|
|
System.out.println(child.getObject().getTagName() + " (" + kind + ")");
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`expand` is idempotent — calling it twice fires only one RPC,
|
|
and is safe under concurrent callers. To refresh after a Galaxy redeploy, call
|
|
`browse` again from the root.
|
|
|
|
### 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<DeployEvent>` 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="ping --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --message hello --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="register --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <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 <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 <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 <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="advise-supervisory --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="authenticate-user --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --verify-user operator --password-env MXGATEWAY_VERIFY_PASSWORD --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="write-secured --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --current-user-id 100 --verifier-user-id 100 --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="stream-events --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --limit 1 --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="stream-alarms --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --filter-prefix Galaxy --limit 1 --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="acknowledge-alarm --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --reference \"\\Galaxy\Area001.Pump001.PumpFault\" --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`, `--require-certificate-validation`, `--timeout`, and
|
|
`--json` on gateway commands. JSON output redacts API keys. TLS is lenient by
|
|
default (the certificate is not verified unless you pin a CA with `--ca-file`);
|
|
pass `--require-certificate-validation` to verify the server certificate against
|
|
the JVM trust store without pinning.
|
|
|
|
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 17 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 = '<gateway-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"
|
|
```
|
|
|
|
## Installing from the Gitea Maven repository
|
|
|
|
The client publishes to the internal Gitea Maven repository at
|
|
`https://gitea.dohertylan.com/api/packages/dohertj2/maven`.
|
|
|
|
In your consumer project's `build.gradle`:
|
|
|
|
````groovy
|
|
repositories {
|
|
maven {
|
|
url 'https://gitea.dohertylan.com/api/packages/dohertj2/maven'
|
|
credentials {
|
|
username = System.getenv('GITEA_USERNAME')
|
|
password = System.getenv('GITEA_TOKEN')
|
|
}
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation 'com.zb.mom.ww.mxgateway:zb-mom-ww-mxgateway-client:0.1.2'
|
|
}
|
|
````
|
|
|
|
To publish a new version from this repo:
|
|
|
|
````bash
|
|
export GITEA_USERNAME=dohertj2
|
|
export GITEA_TOKEN=<your-gitea-token>
|
|
gradle :zb-mom-ww-mxgateway-client:publish
|
|
````
|
|
|
|
## 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)
|