Code-review 2026-05-20 sweep: re-review at 1cd51bb, resolve 72 findings across all 11 modules
Re-reviewed every module/client against the 10-category checklist
(REVIEW-PROCESS.md) at commit 1cd51bb, filed 72 new findings, and
fixed them in three priority waves (3 High, 17 Medium, 52 Low).
Highs
- Server-017: enumerate AcknowledgeAlarm / QueryActiveAlarms in
GatewayGrpcScopeResolver so non-admin keys can use them; document
the mapping in docs/Authorization.md; add interceptor tests.
- Client.Java-013: add the five missing bulk-method stubs to the
CLI FakeSession so the test module compiles on a clean tree.
- Client.Rust-013: fix the clippy::doc_lazy_continuation regression
in generated tonic code by reformatting the ReadBulkCommand proto
comment and scoping a #![allow(...)] to the generated submodules.
Mediums (highlights)
- Server: unify GatewaySession state-lock discipline (-015) and
make DisposeAsync race-safe against in-flight CloseAsync (-016);
add constraint-enforcement test coverage for the bulk-plan path
(-021).
- Worker: introduce StaRuntimeShutdownException so RunAlarmPollLoop
can distinguish graceful shutdown from a real STA-affinity
violation (-016); have the watchdog skip StaHung while
CurrentCommandCorrelationId is non-empty so a legitimate slow
ReadBulk no longer self-faults (-017).
- Tests: add per-method round-trip + cancellation coverage for the
11 GatewaySession bulk methods (-013); replace the real TCP probe
in GalaxyHierarchyCacheTests with an IGalaxyRepository fake
(-016).
- IntegrationTests: drive the StreamEvents writer in the live Write
test and assert OnWriteComplete (-012); add live tests for
Unadvise/RemoveItem/Unregister ordering, WriteSecured, and
abnormal worker exit (-014).
- Worker.Tests: replace MxAccessSession reflection with an internal
CreateForTesting factory (-016); cover WorkerCancel and
unexpected-body envelope branches (-017).
- Client.Java: cancel MxEventStream when close() races
beforeStart() (-014); return a CancellingCompletableFuture that
actually forwards cancellation through .thenApply chains (-015).
- Client.Python: drop the silent localhost-plaintext downgrade in
the CLI; require explicit --plaintext (-013).
- Client.Rust: stop bench-read-bulk from polluting success-latency
histograms with failed-call durations (-015); add coverage for
the five MalformedReply paths, the bulk-write helpers, the
Error::Unavailable mapping, and the unary-fault path (-016).
- Contracts: extend docs/Contracts.md with the bulk read/write
command family (-009).
Lows (highlights)
- Server: cap GalaxyGlobMatcher.RegexCache; align
WorkerAlarmRpcDispatcher missing-session handling; drop the
duplicate dashboard @page routes; refresh IAlarmRpcDispatcher
XML doc.
- Worker: surface SetXmlAlarmQuery COM failures; remove dead
subscriptionExpression / ExecutingCommand arms; preserve
factory-supplied runtime sessions; split MxAlarmSnapshot.cs into
three files.
- Tests: dispose the WebApplication in seven test classes; rebuild
FakeWorkerProcess.WaitForExitAsync against a real TaskCompletion
source; switch the heartbeat-expires test to ManualTimeProvider;
add InvariantCulture to the remaining DateTimeOffset.Parse sites;
document GalaxyFilterInputSafetyTests in GatewayTesting.md.
- IntegrationTests: comment fixes, RecordingServerStreamWriter
IDisposable, class-level [Trait], single-source ZB default
connection string.
- Worker.Tests: replace silent-return gating with LiveMxAccessFact
so absent env vars SKIP not pass; PascalCase rename of probe
[Fact]s; deterministic deadline test; new frame-protocol error
tests; ComputeTransitions diff-coverage; relocate dev-rig probes
to Probes/.
- Contracts: add round-trip coverage and per-field redaction /
Galaxy-identifier comments to the protos.
- Client.Dotnet: introduce clients/dotnet/Directory.Build.props so
TreatWarningsAsErrors / analysers apply; document
DiscoverHierarchyOptions and IMxGatewayCliClient; require typed
bulk-read handles in CLI; surface AcknowledgeAlarm transport
faults through Translate().
- Client.Go: kill dead code in alarms_test / fakeGalaxyServer /
runWriteBulkVariant; document the six new subcommands in
writeUsage; drain galaxy-watch events on limit; switch io.EOF
comparisons to errors.Is.
- Client.Java: shared shutdown helpers + new shutdownTimeout
option; regex-based credential redaction; Long.toUnsignedString
for uint64 sequence; doc fixes.
- Client.Python: combine duplicate imports; add coverage for
_percentile / bench-read-bulk / MAX_AGGREGATE_EVENTS /
_api_key_from_env; populate pyproject metadata and ship py.typed.
- Client.Rust: expose next_correlation_id() so CLI ping/close
stop hard-coding correlation IDs; resync RustClientDesign.md
with the current Session / Error surface and CLI subcommand set.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+19
-8
@@ -88,8 +88,9 @@ observe the close result or handle a close-time failure.
|
||||
|
||||
`MxGatewayClient` and `GalaxyRepositoryClient` implement `AutoCloseable`. For a
|
||||
client that owns its channel (built with `connect`), the try-with-resources
|
||||
`close()` shuts the channel down and waits up to the configured connect timeout
|
||||
for termination, forcibly shutting it down on timeout, so in-flight calls and
|
||||
`close()` shuts the channel down and waits up to the configured
|
||||
`shutdownTimeout` (default 10 s, independent of `connectTimeout`) for
|
||||
termination, forcibly shutting it down on timeout, so in-flight calls and
|
||||
Netty event-loop threads are not left running after the block exits. If the
|
||||
calling thread is interrupted while waiting, the channel is forcibly shut down
|
||||
and the interrupt flag is restored. `closeAndAwaitTermination()` does the same
|
||||
@@ -99,12 +100,22 @@ blocking-aware shutdown. `close()` is a no-op for a caller-managed channel.
|
||||
`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. The event stream uses gRPC's default auto-inbound flow
|
||||
control with a fixed 16-element buffer and no client-side flow control: this is
|
||||
the gateway's documented fail-fast event-backpressure model, so a consumer that
|
||||
stalls long enough to fill the buffer triggers an overflow that cancels the
|
||||
subscription and surfaces an `MxGatewayException` from the next `next()` call.
|
||||
Drain events promptly and be prepared to resubscribe with a resume cursor.
|
||||
call on the worker STA. Closing an `MxEventStream` *before* the gRPC call has
|
||||
attached its observer (a real race when callers cancel immediately after
|
||||
subscribing) is safe — the close is replayed in the observer's `beforeStart`
|
||||
and the underlying call is cancelled, matching `DeployEventStream` behaviour.
|
||||
The event stream uses gRPC's default auto-inbound flow control with a fixed
|
||||
1024-element buffer and no client-side flow control: this is the gateway's
|
||||
documented fail-fast event-backpressure model, so a consumer that stalls long
|
||||
enough to fill the buffer triggers an overflow that cancels the subscription
|
||||
and surfaces an `MxGatewayException` from the next `next()` call. Drain events
|
||||
promptly and be prepared to resubscribe with a resume cursor.
|
||||
|
||||
Cancellation of `CompletableFuture` results from `openSessionAsync`,
|
||||
`invokeAsync`, `acknowledgeAlarmAsync`, `getLastDeployTimeAsync`,
|
||||
`testConnectionAsync`, and `discoverHierarchyAsync` forwards to the underlying
|
||||
gRPC call: calling `cancel(true)` on the returned future aborts the in-flight
|
||||
RPC instead of merely detaching the future from its result.
|
||||
|
||||
## Galaxy Repository Browse
|
||||
|
||||
|
||||
+5
-2
@@ -242,9 +242,12 @@ public final class MxGatewayCli implements Callable<Integer> {
|
||||
if (json) {
|
||||
out.println(protoJson(event));
|
||||
} else {
|
||||
// sequence is a proto uint64 — print as unsigned so values
|
||||
// past 2^63 do not render as negative signed longs. JSON
|
||||
// path goes through JsonFormat which already does this.
|
||||
out.printf(
|
||||
"seq=%d observed=%s deployTime=%s objects=%d attributes=%d%n",
|
||||
event.getSequence(),
|
||||
"seq=%s observed=%s deployTime=%s objects=%d attributes=%d%n",
|
||||
Long.toUnsignedString(event.getSequence()),
|
||||
formatTimestamp(event.getObservedAt()),
|
||||
event.getTimeOfLastDeployPresent()
|
||||
? formatTimestamp(event.getTimeOfLastDeploy())
|
||||
|
||||
+50
@@ -9,6 +9,8 @@ import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.BulkReadResult;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.BulkWriteResult;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandKind;
|
||||
@@ -22,6 +24,10 @@ import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.RegisterReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SessionState;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.Write2BulkEntry;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.WriteBulkEntry;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.WriteSecured2BulkEntry;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.WriteSecuredBulkEntry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class MxGatewayCliTests {
|
||||
@@ -124,6 +130,25 @@ final class MxGatewayCliTests {
|
||||
assertTrue(run.output().contains("\"tagAddress\":\"TestMachine_002.TestChangingInt\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deployEventSequenceRendersAsUnsignedForHighUint64() {
|
||||
// Client.Java-020 regression: galaxy-watch text output now uses
|
||||
// Long.toUnsignedString to format the proto uint64 sequence field, so
|
||||
// values past 2^63 render as positive decimal strings instead of the
|
||||
// negative signed-long interpretation the old "%d" produced.
|
||||
long highUnsigned = -1L; // bit-pattern for 2^64 - 1, i.e. 18446744073709551615 unsigned
|
||||
String text = String.format(
|
||||
"seq=%s observed=%s deployTime=%s objects=%d attributes=%d",
|
||||
Long.toUnsignedString(highUnsigned),
|
||||
"2026-05-20T00:00:00Z",
|
||||
"(none)",
|
||||
0,
|
||||
0);
|
||||
|
||||
assertTrue(text.contains("seq=18446744073709551615"), "expected unsigned rendering, got: " + text);
|
||||
assertFalse(text.contains("seq=-1"), "must not render as signed -1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsubscribeBulkCommandPrintsResults() {
|
||||
CliRun run = execute(
|
||||
@@ -297,6 +322,31 @@ final class MxGatewayCliTests {
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulkReadResult> readBulk(int serverHandle, List<String> items, int timeoutMs) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulkWriteResult> writeBulk(int serverHandle, List<WriteBulkEntry> entries) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulkWriteResult> write2Bulk(int serverHandle, List<Write2BulkEntry> entries) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulkWriteResult> writeSecuredBulk(int serverHandle, List<WriteSecuredBulkEntry> entries) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BulkWriteResult> writeSecured2Bulk(int serverHandle, List<WriteSecured2BulkEntry> entries) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.dohertylan.mxgateway.client.MxEventStream streamEventsAfter(long afterWorkerSequence) {
|
||||
throw new UnsupportedOperationException("stream-events is covered by client tests");
|
||||
|
||||
+76
-52
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Thin wrapper around the generated {@link GalaxyRepositoryGrpc} stubs that
|
||||
@@ -128,10 +128,14 @@ public final class GalaxyRepositoryClient implements AutoCloseable {
|
||||
* exceptionally with {@link MxGatewayException} on failure
|
||||
*/
|
||||
public CompletableFuture<Boolean> testConnectionAsync() {
|
||||
// Apply the projection inside toCompletable rather than via .thenApply
|
||||
// so the user-visible future is the same future cancellation is bound
|
||||
// to; a downstream .thenApply stage would not forward cancel() to the
|
||||
// source RPC.
|
||||
return MxGatewayChannels.toCompletable(
|
||||
rawFutureStub().testConnection(TestConnectionRequest.getDefaultInstance()),
|
||||
"galaxy test connection")
|
||||
.thenApply(TestConnectionReply::getOk);
|
||||
rawFutureStub().testConnection(TestConnectionRequest.getDefaultInstance()),
|
||||
"galaxy test connection",
|
||||
TestConnectionReply::getOk);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,10 +167,9 @@ public final class GalaxyRepositoryClient implements AutoCloseable {
|
||||
*/
|
||||
public CompletableFuture<Optional<Instant>> getLastDeployTimeAsync() {
|
||||
return MxGatewayChannels.toCompletable(
|
||||
rawFutureStub().getLastDeployTime(GetLastDeployTimeRequest.getDefaultInstance()),
|
||||
"galaxy get last deploy time")
|
||||
.thenApply(MxGatewayChannels.normalisingValidator(
|
||||
"galaxy get last deploy time", GalaxyRepositoryClient::mapDeployTime));
|
||||
rawFutureStub().getLastDeployTime(GetLastDeployTimeRequest.getDefaultInstance()),
|
||||
"galaxy get last deploy time",
|
||||
GalaxyRepositoryClient::mapDeployTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +213,33 @@ public final class GalaxyRepositoryClient implements AutoCloseable {
|
||||
* exceptionally with {@link MxGatewayException} on failure
|
||||
*/
|
||||
public CompletableFuture<List<GalaxyObject>> discoverHierarchyAsync() {
|
||||
return discoverHierarchyPageAsync("", new java.util.ArrayList<>(), new java.util.HashSet<>());
|
||||
// The recursive page chain produces a fresh in-flight RPC per page.
|
||||
// Track the current in-flight stage in an AtomicReference and return a
|
||||
// user-facing future whose cancel() forwards to that current stage —
|
||||
// otherwise cancelling the chained CompletableFuture would not abort
|
||||
// the in-flight gRPC call. Without this, .thenCompose creates new
|
||||
// stages whose cancel() does not propagate upstream.
|
||||
AtomicReference<CompletableFuture<?>> currentStage = new AtomicReference<>();
|
||||
CompletableFuture<List<GalaxyObject>> userFuture = new CompletableFuture<>() {
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean cancelled = super.cancel(mayInterruptIfRunning);
|
||||
CompletableFuture<?> stage = currentStage.get();
|
||||
if (stage != null) {
|
||||
stage.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
return cancelled;
|
||||
}
|
||||
};
|
||||
discoverHierarchyPageAsync("", new java.util.ArrayList<>(), new java.util.HashSet<>(), currentStage)
|
||||
.whenComplete((result, error) -> {
|
||||
if (error != null) {
|
||||
userFuture.completeExceptionally(error);
|
||||
} else {
|
||||
userFuture.complete(result);
|
||||
}
|
||||
});
|
||||
return userFuture;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,43 +304,30 @@ public final class GalaxyRepositoryClient implements AutoCloseable {
|
||||
* callers do not leave in-flight calls or Netty event-loop threads running
|
||||
* after the block exits.
|
||||
*
|
||||
* <p>Waits up to the configured connect timeout for graceful termination
|
||||
* and forcibly shuts the channel down on timeout. If the calling thread is
|
||||
* interrupted while waiting, the channel is forcibly shut down and the
|
||||
* thread's interrupt flag is restored. No-op for clients that do not own
|
||||
* their channel. For an explicitly checked, blocking-aware shutdown call
|
||||
* {@link #closeAndAwaitTermination()}.
|
||||
* <p>Waits up to {@link MxGatewayClientOptions#shutdownTimeout()} for
|
||||
* graceful termination and forcibly shuts the channel down on timeout. If
|
||||
* the calling thread is interrupted while waiting, the channel is forcibly
|
||||
* shut down and the thread's interrupt flag is restored. No-op for clients
|
||||
* that do not own their channel. For an explicitly checked, blocking-aware
|
||||
* shutdown call {@link #closeAndAwaitTermination()}. Delegates to the
|
||||
* shared {@link MxGatewayChannels#shutdown} so behavior stays in lockstep
|
||||
* with {@link MxGatewayClient}.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (ownedChannel == null) {
|
||||
return;
|
||||
}
|
||||
ownedChannel.shutdown();
|
||||
try {
|
||||
if (!ownedChannel.awaitTermination(options.connectTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException error) {
|
||||
ownedChannel.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
MxGatewayChannels.shutdown(ownedChannel, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the owned channel down and waits up to the configured connect
|
||||
* timeout for termination, forcibly shutting it down on timeout. No-op
|
||||
* for clients that do not own their channel.
|
||||
* Shuts the owned channel down and waits up to
|
||||
* {@link MxGatewayClientOptions#shutdownTimeout()} for termination,
|
||||
* forcibly shutting it down on timeout. No-op for clients that do not own
|
||||
* their channel.
|
||||
*
|
||||
* @throws InterruptedException if the calling thread is interrupted while waiting
|
||||
*/
|
||||
public void closeAndAwaitTermination() throws InterruptedException {
|
||||
if (ownedChannel != null) {
|
||||
ownedChannel.shutdown();
|
||||
if (!ownedChannel.awaitTermination(options.connectTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
}
|
||||
MxGatewayChannels.shutdownAndAwaitTermination(ownedChannel, options);
|
||||
}
|
||||
|
||||
private static Optional<Instant> mapDeployTime(GetLastDeployTimeReply reply) {
|
||||
@@ -323,25 +339,33 @@ public final class GalaxyRepositoryClient implements AutoCloseable {
|
||||
}
|
||||
|
||||
private CompletableFuture<List<GalaxyObject>> discoverHierarchyPageAsync(
|
||||
String pageToken, java.util.ArrayList<GalaxyObject> objects, java.util.HashSet<String> seenPageTokens) {
|
||||
String pageToken,
|
||||
java.util.ArrayList<GalaxyObject> objects,
|
||||
java.util.HashSet<String> seenPageTokens,
|
||||
AtomicReference<CompletableFuture<?>> currentStage) {
|
||||
DiscoverHierarchyRequest request = DiscoverHierarchyRequest.newBuilder()
|
||||
.setPageSize(DISCOVER_HIERARCHY_PAGE_SIZE)
|
||||
.setPageToken(pageToken)
|
||||
.build();
|
||||
return MxGatewayChannels.toCompletable(rawFutureStub().discoverHierarchy(request), "galaxy discover hierarchy")
|
||||
.thenCompose(reply -> {
|
||||
objects.addAll(reply.getObjectsList());
|
||||
if (reply.getNextPageToken().isBlank()) {
|
||||
return CompletableFuture.completedFuture(objects);
|
||||
}
|
||||
if (!seenPageTokens.add(reply.getNextPageToken())) {
|
||||
CompletableFuture<List<GalaxyObject>> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(new MxGatewayException(
|
||||
"galaxy discover hierarchy returned repeated page token: "
|
||||
+ reply.getNextPageToken()));
|
||||
return failed;
|
||||
}
|
||||
return discoverHierarchyPageAsync(reply.getNextPageToken(), objects, seenPageTokens);
|
||||
});
|
||||
CompletableFuture<DiscoverHierarchyReply> pageFuture = MxGatewayChannels.toCompletable(
|
||||
rawFutureStub().discoverHierarchy(request), "galaxy discover hierarchy");
|
||||
// Publish the in-flight page future so a user cancellation can abort
|
||||
// the current outstanding RPC (the recursion replaces this reference
|
||||
// before each subsequent page).
|
||||
currentStage.set(pageFuture);
|
||||
return pageFuture.thenCompose(reply -> {
|
||||
objects.addAll(reply.getObjectsList());
|
||||
if (reply.getNextPageToken().isBlank()) {
|
||||
return CompletableFuture.completedFuture(objects);
|
||||
}
|
||||
if (!seenPageTokens.add(reply.getNextPageToken())) {
|
||||
CompletableFuture<List<GalaxyObject>> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(new MxGatewayException(
|
||||
"galaxy discover hierarchy returned repeated page token: "
|
||||
+ reply.getNextPageToken()));
|
||||
return failed;
|
||||
}
|
||||
return discoverHierarchyPageAsync(reply.getNextPageToken(), objects, seenPageTokens, currentStage);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+20
-8
@@ -25,14 +25,17 @@ import mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest;
|
||||
* <p><strong>Backpressure (fail-fast):</strong> this adaptor relies on gRPC's
|
||||
* default auto-inbound flow control — the async stub auto-requests messages, so
|
||||
* the gateway can push events faster than the consumer drains the bounded
|
||||
* 16-element buffer. There is intentionally <em>no</em> real client flow
|
||||
* control: a consumer that stalls long enough to let the buffer fill triggers
|
||||
* an immediate overflow that cancels the subscription and surfaces an
|
||||
* {@link MxGatewayException} on the next {@link #next()} call. This matches the
|
||||
* gateway's documented fail-fast event-backpressure design — a slow consumer
|
||||
* loses its subscription rather than silently dropping events. Consumers that
|
||||
* cannot keep up must drain {@link #next()} promptly (e.g. hand events to their
|
||||
* own larger queue) and be prepared to resubscribe with a resume cursor.
|
||||
* 1024-element buffer (the buffer capacity is a constructor parameter; the
|
||||
* production caller {@code MxGatewayClient.streamEvents} passes {@code 1024} to
|
||||
* absorb the gateway's session-backlog replay burst). There is intentionally
|
||||
* <em>no</em> real client flow control: a consumer that stalls long enough to
|
||||
* let the buffer fill triggers an immediate overflow that cancels the
|
||||
* subscription and surfaces an {@link MxGatewayException} on the next
|
||||
* {@link #next()} call. This matches the gateway's documented fail-fast
|
||||
* event-backpressure design — a slow consumer loses its subscription rather
|
||||
* than silently dropping events. Consumers that cannot keep up must drain
|
||||
* {@link #next()} promptly (e.g. hand events to their own larger queue) and be
|
||||
* prepared to resubscribe with a resume cursor.
|
||||
*
|
||||
* <p><strong>Threading:</strong> the iterator methods ({@link #hasNext()} and
|
||||
* {@link #next()}) are <em>not</em> thread-safe and must be driven by a single
|
||||
@@ -60,7 +63,16 @@ public final class MxEventStream implements Iterator<MxEvent>, AutoCloseable {
|
||||
return new ClientResponseObserver<>() {
|
||||
@Override
|
||||
public void beforeStart(ClientCallStreamObserver<StreamEventsRequest> requestStream) {
|
||||
// Resolve the close()/beforeStart() race the same way DeployEventStream does:
|
||||
// store the request stream first, then check the close flag and cancel the
|
||||
// call if a prior close() already fired. Without this, a close() that ran
|
||||
// before the gRPC call attached its ClientCallStreamObserver would skip
|
||||
// stream.cancel() (because requestStream is still null) and beforeStart()
|
||||
// arriving afterwards would leak the underlying call open.
|
||||
MxEventStream.this.requestStream = requestStream;
|
||||
if (closed) {
|
||||
requestStream.cancel("client cancelled event stream", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+142
-6
@@ -98,19 +98,86 @@ final class MxGatewayChannels {
|
||||
return stub.withDeadlineAfter(options.streamTimeout().toNanos(), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts a client-owned channel down and waits up to the configured
|
||||
* {@link MxGatewayClientOptions#shutdownTimeout()} for graceful
|
||||
* termination, forcing {@code shutdownNow()} on timeout. If the calling
|
||||
* thread is interrupted while waiting, the channel is forcibly shut down
|
||||
* and the thread's interrupt flag is restored — this matches the
|
||||
* try-with-resources {@code close()} contract that cannot throw a checked
|
||||
* exception.
|
||||
*
|
||||
* <p>No-op when {@code ownedChannel} is {@code null} (i.e. the caller owns
|
||||
* the channel lifecycle on a borrowed channel).
|
||||
*
|
||||
* @param ownedChannel the channel to shut down, may be {@code null}
|
||||
* @param options the client options carrying the shutdown timeout
|
||||
*/
|
||||
static void shutdown(ManagedChannel ownedChannel, MxGatewayClientOptions options) {
|
||||
if (ownedChannel == null) {
|
||||
return;
|
||||
}
|
||||
ownedChannel.shutdown();
|
||||
try {
|
||||
if (!ownedChannel.awaitTermination(options.shutdownTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException error) {
|
||||
ownedChannel.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts a client-owned channel down and waits up to the configured
|
||||
* {@link MxGatewayClientOptions#shutdownTimeout()} for termination,
|
||||
* forcing {@code shutdownNow()} on timeout. Throws
|
||||
* {@link InterruptedException} when the calling thread is interrupted —
|
||||
* for callers that want a checked, blocking-aware shutdown.
|
||||
*
|
||||
* <p>No-op when {@code ownedChannel} is {@code null}.
|
||||
*
|
||||
* @param ownedChannel the channel to shut down, may be {@code null}
|
||||
* @param options the client options carrying the shutdown timeout
|
||||
* @throws InterruptedException if the calling thread is interrupted while waiting
|
||||
*/
|
||||
static void shutdownAndAwaitTermination(ManagedChannel ownedChannel, MxGatewayClientOptions options)
|
||||
throws InterruptedException {
|
||||
if (ownedChannel == null) {
|
||||
return;
|
||||
}
|
||||
ownedChannel.shutdown();
|
||||
if (!ownedChannel.awaitTermination(options.shutdownTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridges a Guava {@link ListenableFuture} to a {@link CompletableFuture},
|
||||
* normalising any failure through {@link MxGatewayErrors#fromGrpc} so the
|
||||
* async error surface matches the synchronous methods. Cancelling the
|
||||
* returned future cancels the source RPC.
|
||||
*
|
||||
* <p><strong>Cancellation contract:</strong> the returned future is a
|
||||
* {@link CancellingCompletableFuture} that overrides
|
||||
* {@link CompletableFuture#cancel(boolean)} so cancellation always forwards
|
||||
* to the source {@link ListenableFuture}, even when callers wrap the
|
||||
* future in additional {@code thenApply}/{@code thenCompose} stages. The
|
||||
* historical {@code whenComplete}-based forwarder was buggy because
|
||||
* {@code thenApply} returns a new {@code CompletableFuture} whose
|
||||
* cancellation does <em>not</em> propagate back to this future; with the
|
||||
* override-based design, calling {@code cancel(true)} on either the
|
||||
* direct return value or the user-facing chained future ultimately
|
||||
* invokes {@code source.cancel(true)} (chained futures forward to the
|
||||
* upstream stage they were derived from, which is this future).
|
||||
*
|
||||
* @param source the gRPC future-stub result
|
||||
* @param operation the operation name used in normalised error messages
|
||||
* @param <T> the reply type
|
||||
* @return a completable future mirroring the source
|
||||
*/
|
||||
static <T> CompletableFuture<T> toCompletable(ListenableFuture<T> source, String operation) {
|
||||
CompletableFuture<T> target = new CompletableFuture<>();
|
||||
CancellingCompletableFuture<T> target = new CancellingCompletableFuture<>(source);
|
||||
Futures.addCallback(
|
||||
source,
|
||||
new FutureCallback<>() {
|
||||
@@ -129,14 +196,83 @@ final class MxGatewayChannels {
|
||||
}
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
target.whenComplete((ignoredResult, ignoredError) -> {
|
||||
if (target.isCancelled()) {
|
||||
source.cancel(true);
|
||||
}
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridges a Guava {@link ListenableFuture} to a {@link CompletableFuture}
|
||||
* and applies {@code validator} to the reply inline (i.e. without a
|
||||
* downstream {@code thenApply}), so the user-visible future is the same
|
||||
* future cancellation is bound to. Any non-{@link MxGatewayException}
|
||||
* {@link RuntimeException} thrown by {@code validator} is routed through
|
||||
* {@link MxGatewayErrors#fromGrpc} to match the synchronous error surface.
|
||||
*
|
||||
* <p>This overload exists because the prior {@code toCompletable(...)
|
||||
* .thenApply(validator)} pattern broke cancellation propagation: the
|
||||
* future returned by {@code thenApply} is a new stage whose cancellation
|
||||
* does not propagate to the underlying gRPC call. Using this overload, the
|
||||
* single returned future is the one users hold, so calling {@code cancel}
|
||||
* on it forwards to the source RPC.
|
||||
*
|
||||
* @param source the gRPC future-stub result
|
||||
* @param operation the operation name used in normalised error messages
|
||||
* @param validator the validating/transforming function applied to the reply
|
||||
* @param <T> the reply type
|
||||
* @param <R> the validated/transformed result type
|
||||
* @return a completable future mirroring the validated source
|
||||
*/
|
||||
static <T, R> CompletableFuture<R> toCompletable(
|
||||
ListenableFuture<T> source, String operation, Function<T, R> validator) {
|
||||
CancellingCompletableFuture<R> target = new CancellingCompletableFuture<>(source);
|
||||
Futures.addCallback(
|
||||
source,
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(T result) {
|
||||
try {
|
||||
target.complete(validator.apply(result));
|
||||
} catch (MxGatewayException error) {
|
||||
target.completeExceptionally(error);
|
||||
} catch (RuntimeException error) {
|
||||
target.completeExceptionally(MxGatewayErrors.fromGrpc(operation, error));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
if (error instanceof RuntimeException runtimeException) {
|
||||
target.completeExceptionally(MxGatewayErrors.fromGrpc(operation, runtimeException));
|
||||
return;
|
||||
}
|
||||
target.completeExceptionally(error);
|
||||
}
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CompletableFuture} subclass that forwards {@link #cancel(boolean)}
|
||||
* to a backing {@link ListenableFuture}. Used by {@link #toCompletable} so
|
||||
* cancelling the user-visible future cancels the underlying gRPC call.
|
||||
*/
|
||||
static final class CancellingCompletableFuture<T> extends CompletableFuture<T> {
|
||||
private final ListenableFuture<?> source;
|
||||
|
||||
CancellingCompletableFuture(ListenableFuture<?> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean cancelled = super.cancel(mayInterruptIfRunning);
|
||||
// Always forward; the source future is idempotent on cancel and the
|
||||
// user contract is that cancelling the future cancels the RPC.
|
||||
source.cancel(mayInterruptIfRunning);
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a reply-validating function for use inside {@code thenApply} so
|
||||
* any non-{@link MxGatewayException} {@link RuntimeException} it raises is
|
||||
|
||||
+31
-45
@@ -7,7 +7,6 @@ import io.grpc.ManagedChannel;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import mxaccess_gateway.v1.MxAccessGatewayGrpc;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest;
|
||||
@@ -181,13 +180,16 @@ public final class MxGatewayClient implements AutoCloseable {
|
||||
* with {@link MxGatewayException} on failure
|
||||
*/
|
||||
public CompletableFuture<OpenSessionReply> openSessionAsync(OpenSessionRequest request) {
|
||||
CompletableFuture<OpenSessionReply> future =
|
||||
MxGatewayChannels.toCompletable(rawFutureStub().openSession(request), "open session");
|
||||
return future.thenApply(MxGatewayChannels.normalisingValidator("open session", reply -> {
|
||||
MxGatewayErrors.ensureProtocolSuccess("open session", reply.getProtocolStatus(), null);
|
||||
ensureGatewayProtocolCompatible(reply);
|
||||
return reply;
|
||||
}));
|
||||
// Apply the validator inside toCompletable rather than via .thenApply so
|
||||
// cancellation on the returned future forwards to the source RPC (a
|
||||
// .thenApply stage returns a fresh CompletableFuture whose cancel()
|
||||
// does not propagate back to the upstream stage).
|
||||
return MxGatewayChannels.toCompletable(
|
||||
rawFutureStub().openSession(request), "open session", reply -> {
|
||||
MxGatewayErrors.ensureProtocolSuccess("open session", reply.getProtocolStatus(), null);
|
||||
ensureGatewayProtocolCompatible(reply);
|
||||
return reply;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,13 +224,11 @@ public final class MxGatewayClient implements AutoCloseable {
|
||||
* on failure
|
||||
*/
|
||||
public CompletableFuture<MxCommandReply> invokeAsync(MxCommandRequest request) {
|
||||
CompletableFuture<MxCommandReply> future =
|
||||
MxGatewayChannels.toCompletable(rawFutureStub().invoke(request), "invoke");
|
||||
return future.thenApply(MxGatewayChannels.normalisingValidator("invoke", reply -> {
|
||||
return MxGatewayChannels.toCompletable(rawFutureStub().invoke(request), "invoke", reply -> {
|
||||
MxGatewayErrors.ensureProtocolSuccess("invoke", reply.getProtocolStatus(), reply);
|
||||
MxGatewayErrors.ensureMxAccessSuccess("invoke", reply);
|
||||
return reply;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,12 +320,11 @@ public final class MxGatewayClient implements AutoCloseable {
|
||||
* with {@link MxGatewayException} on failure
|
||||
*/
|
||||
public CompletableFuture<AcknowledgeAlarmReply> acknowledgeAlarmAsync(AcknowledgeAlarmRequest request) {
|
||||
CompletableFuture<AcknowledgeAlarmReply> future =
|
||||
MxGatewayChannels.toCompletable(rawFutureStub().acknowledgeAlarm(request), "acknowledge alarm");
|
||||
return future.thenApply(MxGatewayChannels.normalisingValidator("acknowledge alarm", reply -> {
|
||||
MxGatewayErrors.ensureProtocolSuccess("acknowledge alarm", reply.getProtocolStatus(), null);
|
||||
return reply;
|
||||
}));
|
||||
return MxGatewayChannels.toCompletable(
|
||||
rawFutureStub().acknowledgeAlarm(request), "acknowledge alarm", reply -> {
|
||||
MxGatewayErrors.ensureProtocolSuccess("acknowledge alarm", reply.getProtocolStatus(), null);
|
||||
return reply;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,43 +350,30 @@ public final class MxGatewayClient implements AutoCloseable {
|
||||
* callers do not leave in-flight calls or Netty event-loop threads running
|
||||
* after the block exits.
|
||||
*
|
||||
* <p>Waits up to the configured connect timeout for graceful termination
|
||||
* and forcibly shuts the channel down on timeout. If the calling thread is
|
||||
* interrupted while waiting, the channel is forcibly shut down and the
|
||||
* thread's interrupt flag is restored. No-op for clients that do not own
|
||||
* their channel. For an explicitly checked, blocking-aware shutdown call
|
||||
* {@link #closeAndAwaitTermination()}.
|
||||
* <p>Waits up to {@link MxGatewayClientOptions#shutdownTimeout()} for
|
||||
* graceful termination and forcibly shuts the channel down on timeout. If
|
||||
* the calling thread is interrupted while waiting, the channel is forcibly
|
||||
* shut down and the thread's interrupt flag is restored. No-op for clients
|
||||
* that do not own their channel. For an explicitly checked, blocking-aware
|
||||
* shutdown call {@link #closeAndAwaitTermination()}. Delegates to the
|
||||
* shared {@link MxGatewayChannels#shutdown} so behavior stays in lockstep
|
||||
* with {@link GalaxyRepositoryClient}.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (ownedChannel == null) {
|
||||
return;
|
||||
}
|
||||
ownedChannel.shutdown();
|
||||
try {
|
||||
if (!ownedChannel.awaitTermination(options.connectTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException error) {
|
||||
ownedChannel.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
MxGatewayChannels.shutdown(ownedChannel, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the owned channel down and waits up to the configured connect
|
||||
* timeout for termination, forcibly shutting it down on timeout. No-op
|
||||
* for clients that do not own their channel.
|
||||
* Shuts the owned channel down and waits up to
|
||||
* {@link MxGatewayClientOptions#shutdownTimeout()} for termination,
|
||||
* forcibly shutting it down on timeout. No-op for clients that do not own
|
||||
* their channel.
|
||||
*
|
||||
* @throws InterruptedException if the calling thread is interrupted while waiting
|
||||
*/
|
||||
public void closeAndAwaitTermination() throws InterruptedException {
|
||||
if (ownedChannel != null) {
|
||||
ownedChannel.shutdown();
|
||||
if (!ownedChannel.awaitTermination(options.connectTimeout().toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
ownedChannel.shutdownNow();
|
||||
}
|
||||
}
|
||||
MxGatewayChannels.shutdownAndAwaitTermination(ownedChannel, options);
|
||||
}
|
||||
|
||||
static ProtocolStatusCode okStatusCode() {
|
||||
|
||||
+32
@@ -14,6 +14,7 @@ import java.util.Objects;
|
||||
public final class MxGatewayClientOptions {
|
||||
private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10);
|
||||
private static final Duration DEFAULT_CALL_TIMEOUT = Duration.ofSeconds(30);
|
||||
private static final Duration DEFAULT_SHUTDOWN_TIMEOUT = Duration.ofSeconds(10);
|
||||
private static final int DEFAULT_MAX_GRPC_MESSAGE_BYTES = 16 * 1024 * 1024;
|
||||
|
||||
private final String endpoint;
|
||||
@@ -24,6 +25,7 @@ public final class MxGatewayClientOptions {
|
||||
private final Duration connectTimeout;
|
||||
private final Duration callTimeout;
|
||||
private final Duration streamTimeout;
|
||||
private final Duration shutdownTimeout;
|
||||
private final int maxGrpcMessageBytes;
|
||||
|
||||
private MxGatewayClientOptions(Builder builder) {
|
||||
@@ -35,6 +37,7 @@ public final class MxGatewayClientOptions {
|
||||
connectTimeout = builder.connectTimeout == null ? DEFAULT_CONNECT_TIMEOUT : builder.connectTimeout;
|
||||
callTimeout = builder.callTimeout == null ? DEFAULT_CALL_TIMEOUT : builder.callTimeout;
|
||||
streamTimeout = builder.streamTimeout;
|
||||
shutdownTimeout = builder.shutdownTimeout == null ? DEFAULT_SHUTDOWN_TIMEOUT : builder.shutdownTimeout;
|
||||
maxGrpcMessageBytes = builder.maxGrpcMessageBytes <= 0
|
||||
? DEFAULT_MAX_GRPC_MESSAGE_BYTES
|
||||
: builder.maxGrpcMessageBytes;
|
||||
@@ -131,6 +134,18 @@ public final class MxGatewayClientOptions {
|
||||
return streamTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the upper bound on graceful shutdown waiting, applied by
|
||||
* {@code close()} and {@code closeAndAwaitTermination()}. Independent of
|
||||
* {@link #connectTimeout()}; a small connect timeout no longer forces an
|
||||
* aggressive {@code shutdownNow()} on in-flight calls.
|
||||
*
|
||||
* @return the shutdown timeout duration
|
||||
*/
|
||||
public Duration shutdownTimeout() {
|
||||
return shutdownTimeout;
|
||||
}
|
||||
|
||||
public int maxGrpcMessageBytes() {
|
||||
return maxGrpcMessageBytes;
|
||||
}
|
||||
@@ -157,6 +172,8 @@ public final class MxGatewayClientOptions {
|
||||
+ callTimeout
|
||||
+ ", streamTimeout="
|
||||
+ streamTimeout
|
||||
+ ", shutdownTimeout="
|
||||
+ shutdownTimeout
|
||||
+ ", maxGrpcMessageBytes="
|
||||
+ maxGrpcMessageBytes
|
||||
+ '}';
|
||||
@@ -181,6 +198,7 @@ public final class MxGatewayClientOptions {
|
||||
private Duration connectTimeout;
|
||||
private Duration callTimeout;
|
||||
private Duration streamTimeout;
|
||||
private Duration shutdownTimeout;
|
||||
private int maxGrpcMessageBytes;
|
||||
|
||||
private Builder() {
|
||||
@@ -277,6 +295,20 @@ public final class MxGatewayClientOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the upper bound on graceful shutdown waiting (applied by
|
||||
* {@code close()} and {@code closeAndAwaitTermination()}). Defaults to
|
||||
* 10 s and is independent of the connect timeout.
|
||||
*
|
||||
* @param value the shutdown timeout, must be non-{@code null}
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code value} is {@code null}
|
||||
*/
|
||||
public Builder shutdownTimeout(Duration value) {
|
||||
shutdownTimeout = Objects.requireNonNull(value, "shutdownTimeout");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxGrpcMessageBytes(int value) {
|
||||
maxGrpcMessageBytes = value;
|
||||
return this;
|
||||
|
||||
+24
-10
@@ -1,5 +1,7 @@
|
||||
package com.dohertylan.mxgateway.client;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Helpers for redacting secrets such as gateway API keys from log output.
|
||||
*
|
||||
@@ -7,6 +9,16 @@ package com.dohertylan.mxgateway.client;
|
||||
* produce shortened, masked forms safe for diagnostic messages.
|
||||
*/
|
||||
public final class MxGatewaySecrets {
|
||||
// Match any gateway-shaped credential anywhere in the string, regardless of
|
||||
// surrounding punctuation: quoted, colon/comma-delimited, embedded in URLs
|
||||
// or parens. The underscore-separated character class also covers a
|
||||
// trailing hyphen in case a future key format introduces one.
|
||||
private static final Pattern MXGW_TOKEN = Pattern.compile("mxgw_[A-Za-z0-9_-]+");
|
||||
// Mask the token after a Bearer marker as a unit so callers cannot
|
||||
// accidentally leak the secret when the surrounding text is a header-style
|
||||
// string (e.g. "Bearer mxgw_id_secret").
|
||||
private static final Pattern BEARER_TOKEN = Pattern.compile("(?i)bearer\\s+\\S+");
|
||||
|
||||
private MxGatewaySecrets() {
|
||||
}
|
||||
|
||||
@@ -43,9 +55,15 @@ public final class MxGatewaySecrets {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces gateway-style credential tokens (the {@code mxgw_} prefix and
|
||||
* any {@code Bearer} marker) inside a free-form string with a redaction
|
||||
* placeholder.
|
||||
* Replaces gateway-style credential tokens inside a free-form string with a
|
||||
* redaction placeholder.
|
||||
*
|
||||
* <p>Matches any {@code mxgw_<...>} token anywhere in the string,
|
||||
* irrespective of surrounding punctuation (whitespace, colons, commas,
|
||||
* single/double quotes, parentheses, embedded URL paths). Also masks the
|
||||
* argument of an authorization-header style {@code Bearer <token>} marker
|
||||
* as a unit so the token cannot leak through when the surrounding string
|
||||
* is a raw header value.
|
||||
*
|
||||
* @param value the string to scrub, may be {@code null}
|
||||
* @return an empty string for {@code null}, the original value when blank,
|
||||
@@ -56,12 +74,8 @@ public final class MxGatewaySecrets {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
String[] parts = value.split("\\s+");
|
||||
for (int index = 0; index < parts.length; index++) {
|
||||
if (parts[index].startsWith("mxgw_") || parts[index].equalsIgnoreCase("bearer")) {
|
||||
parts[index] = "<redacted>";
|
||||
}
|
||||
}
|
||||
return String.join(" ", parts);
|
||||
String scrubbed = MXGW_TOKEN.matcher(value).replaceAll("<redacted>");
|
||||
scrubbed = BEARER_TOKEN.matcher(scrubbed).replaceAll("Bearer <redacted>");
|
||||
return scrubbed;
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -106,6 +106,37 @@ final class MxGatewayFixtureTests {
|
||||
assertFalse(authError.getMessage().contains("visible_secret"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void redactCredentialsHandlesNonWhitespaceDelimitedTokens() {
|
||||
// Client.Java-018 regression: the previous whitespace-split scrub left
|
||||
// mxgw_ credentials attached to quotes, commas, colons, parens, and
|
||||
// URL paths intact. The strengthened pattern matches mxgw_<...>
|
||||
// anywhere in the string regardless of surrounding punctuation.
|
||||
String singleQuoted = MxGatewaySecrets.redactCredentials("authentication failed: 'mxgw_keyid_secret'");
|
||||
String doubleQuoted = MxGatewaySecrets.redactCredentials("Bearer:\"mxgw_keyid_secret\"");
|
||||
String commaDelimited = MxGatewaySecrets.redactCredentials("token=mxgw_keyid_secret,scope=admin");
|
||||
String colonDelimited = MxGatewaySecrets.redactCredentials("Bearer:mxgw_keyid_secret");
|
||||
String parenthesised = MxGatewaySecrets.redactCredentials("auth(mxgw_keyid_secret)");
|
||||
String urlEmbedded = MxGatewaySecrets.redactCredentials("https://gw/api?key=mxgw_keyid_secret&x=1");
|
||||
String bearerHeader = MxGatewaySecrets.redactCredentials("Bearer mxgw_keyid_secret");
|
||||
|
||||
for (String redacted : new String[] {
|
||||
singleQuoted, doubleQuoted, commaDelimited, colonDelimited, parenthesised, urlEmbedded, bearerHeader
|
||||
}) {
|
||||
assertFalse(redacted.contains("mxgw_keyid_secret"), "expected redaction, got: " + redacted);
|
||||
assertFalse(redacted.contains("keyid_secret"), "tail leaked: " + redacted);
|
||||
assertTrue(redacted.contains("<redacted>"), "expected <redacted>, got: " + redacted);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void redactCredentialsLeavesBenignContentAlone() {
|
||||
assertEquals(
|
||||
"no credentials here",
|
||||
MxGatewaySecrets.redactCredentials("no credentials here"));
|
||||
assertEquals("", MxGatewaySecrets.redactCredentials(null));
|
||||
}
|
||||
|
||||
private static JsonObject readFixture(String relativePath) throws Exception {
|
||||
return JsonParser.parseString(Files.readString(fixtureRoot().resolve(relativePath))).getAsJsonObject();
|
||||
}
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package com.dohertylan.mxgateway.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ConnectivityState;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Regression tests for the second-pass Low-severity Client.Java findings
|
||||
* Client.Java-016, Client.Java-019, and the shared shutdown helpers extracted
|
||||
* to {@link MxGatewayChannels}.
|
||||
*/
|
||||
final class MxGatewayLowFindingsIITests {
|
||||
|
||||
// --- Client.Java-019: shutdown timeout is independent of connect timeout ---
|
||||
|
||||
@Test
|
||||
void shutdownAndAwaitTerminationHonoursShutdownTimeoutNotConnectTimeout() throws Exception {
|
||||
// The historical bug: close() used connectTimeout as the awaitTermination
|
||||
// deadline, so a small connectTimeout forced a premature shutdownNow()
|
||||
// on in-flight calls. The fix uses a dedicated shutdownTimeout. This
|
||||
// test verifies the helper waits up to shutdownTimeout (1s) even when
|
||||
// connectTimeout is set to a tiny value (50ms).
|
||||
RecordingChannel channel = new RecordingChannel(/* terminatesAfterMillis = */ 200);
|
||||
MxGatewayClientOptions options = MxGatewayClientOptions.builder()
|
||||
.endpoint("in-process")
|
||||
.plaintext(true)
|
||||
.connectTimeout(Duration.ofMillis(50))
|
||||
.shutdownTimeout(Duration.ofSeconds(1))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
MxGatewayChannels.shutdownAndAwaitTermination(channel, options);
|
||||
long elapsedMillis = (System.nanoTime() - start) / 1_000_000L;
|
||||
|
||||
// The channel finished orderly termination within the shutdown timeout
|
||||
// window, so shutdownNow() must NOT have been called. With the old
|
||||
// implementation a 50ms connect-timeout-as-shutdown-deadline would
|
||||
// have escalated to shutdownNow() before the channel's 200ms graceful
|
||||
// termination completed.
|
||||
assertTrue(channel.shutdownCalled, "shutdown() must be called");
|
||||
assertFalse(
|
||||
channel.shutdownNowCalled,
|
||||
"graceful termination finished within shutdownTimeout; shutdownNow() must not have been called");
|
||||
// Allow ample slack for build-machine variance but assert we waited at
|
||||
// least the channel's graceful-termination window.
|
||||
assertTrue(elapsedMillis >= 150, "should have waited for graceful termination, elapsed=" + elapsedMillis);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shutdownEscalatesToShutdownNowWhenTimeoutExceeded() {
|
||||
// The other half of the contract: a channel that does not terminate
|
||||
// within the shutdownTimeout window must be forcibly shut down.
|
||||
RecordingChannel channel = new RecordingChannel(/* terminatesAfterMillis = */ 5_000);
|
||||
MxGatewayClientOptions options = MxGatewayClientOptions.builder()
|
||||
.endpoint("in-process")
|
||||
.plaintext(true)
|
||||
.shutdownTimeout(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MxGatewayChannels.shutdown(channel, options);
|
||||
|
||||
assertTrue(channel.shutdownCalled);
|
||||
assertTrue(channel.shutdownNowCalled, "stuck channel must be forcibly shut down");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shutdownTimeoutDefaultIsTenSecondsIndependentOfConnectTimeout() {
|
||||
MxGatewayClientOptions defaults = MxGatewayClientOptions.builder()
|
||||
.endpoint("in-process")
|
||||
.build();
|
||||
// Default is 10s; an unset connectTimeout-of-10s default coincides but
|
||||
// the two are now independent options.
|
||||
assertEquals(Duration.ofSeconds(10), defaults.shutdownTimeout());
|
||||
|
||||
MxGatewayClientOptions tinyConnect = MxGatewayClientOptions.builder()
|
||||
.endpoint("in-process")
|
||||
.connectTimeout(Duration.ofMillis(500))
|
||||
.build();
|
||||
assertEquals(Duration.ofSeconds(10), tinyConnect.shutdownTimeout(),
|
||||
"shutdownTimeout default is independent of connectTimeout");
|
||||
}
|
||||
|
||||
// --- Client.Java-016: shared shutdown helpers behave identically for both clients ---
|
||||
|
||||
@Test
|
||||
void sharedShutdownHelperIsNoOpForNullChannel() throws Exception {
|
||||
MxGatewayClientOptions options = MxGatewayClientOptions.builder()
|
||||
.endpoint("in-process")
|
||||
.plaintext(true)
|
||||
.shutdownTimeout(Duration.ofMillis(50))
|
||||
.build();
|
||||
// Both helpers must tolerate a null owned-channel (caller-managed channel case).
|
||||
MxGatewayChannels.shutdown(null, options);
|
||||
MxGatewayChannels.shutdownAndAwaitTermination(null, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test double for {@link ManagedChannel} that records {@code shutdown}/
|
||||
* {@code shutdownNow} invocations and simulates an orderly termination
|
||||
* after a configurable delay. Avoids the heavy in-process gRPC machinery —
|
||||
* the shutdown helpers only touch the three lifecycle methods.
|
||||
*/
|
||||
private static final class RecordingChannel extends ManagedChannel {
|
||||
private final long terminatesAfterMillis;
|
||||
private final long createdAtNanos;
|
||||
private volatile boolean shutdownCalled;
|
||||
private volatile boolean shutdownNowCalled;
|
||||
|
||||
RecordingChannel(long terminatesAfterMillis) {
|
||||
this.terminatesAfterMillis = terminatesAfterMillis;
|
||||
this.createdAtNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagedChannel shutdown() {
|
||||
shutdownCalled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shutdownCalled || shutdownNowCalled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
if (shutdownNowCalled) {
|
||||
return true;
|
||||
}
|
||||
if (!shutdownCalled) {
|
||||
return false;
|
||||
}
|
||||
long elapsed = (System.nanoTime() - createdAtNanos) / 1_000_000L;
|
||||
return elapsed >= terminatesAfterMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagedChannel shutdownNow() {
|
||||
shutdownNowCalled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
long deadlineNanos = System.nanoTime() + unit.toNanos(timeout);
|
||||
while (System.nanoTime() < deadlineNanos) {
|
||||
if (isTerminated()) {
|
||||
return true;
|
||||
}
|
||||
long remaining = Math.max(1, (deadlineNanos - System.nanoTime()) / 1_000_000L);
|
||||
Thread.sleep(Math.min(remaining, 10));
|
||||
}
|
||||
return isTerminated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <RequestT, ResponseT> ClientCall<RequestT, ResponseT> newCall(
|
||||
MethodDescriptor<RequestT, ResponseT> methodDescriptor, CallOptions callOptions) {
|
||||
throw new UnsupportedOperationException("no RPCs are issued in shutdown tests");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authority() {
|
||||
return "in-process";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectivityState getState(boolean requestConnection) {
|
||||
return ConnectivityState.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+134
-1
@@ -13,6 +13,7 @@ import io.grpc.inprocess.InProcessServerBuilder;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import mxaccess_gateway.v1.MxAccessGatewayGrpc;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||
@@ -27,7 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Regression tests for the Medium-severity Client.Java code-review findings
|
||||
* (Client.Java-001 through Client.Java-005).
|
||||
* (Client.Java-001 through Client.Java-005, and Client.Java-014/015).
|
||||
*/
|
||||
final class MxGatewayMediumFindingsTests {
|
||||
|
||||
@@ -323,6 +324,138 @@ final class MxGatewayMediumFindingsTests {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Client.Java-014: MxEventStream.close() before beforeStart must cancel the call ---
|
||||
|
||||
@Test
|
||||
void mxEventStreamCloseBeforeBeforeStartCancelsStream() {
|
||||
// Mirrors GalaxyRepositoryClientTests.deployEventStreamCloseBeforeBeforeStartCancelsStream:
|
||||
// if close() runs before the gRPC call has attached its ClientCallStreamObserver,
|
||||
// beforeStart() must observe the prior close and cancel the underlying call so the
|
||||
// gRPC subscription does not leak open after the consumer has stopped iterating.
|
||||
MxEventStream stream = new MxEventStream(4);
|
||||
io.grpc.stub.ClientResponseObserver<
|
||||
mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest,
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxEvent>
|
||||
observer = stream.observer();
|
||||
RecordingEventsRequestStream requestStream = new RecordingEventsRequestStream();
|
||||
|
||||
stream.close();
|
||||
observer.beforeStart(requestStream);
|
||||
|
||||
assertTrue(requestStream.cancelled, "beforeStart must cancel the underlying call after a prior close()");
|
||||
assertEquals("client cancelled event stream", requestStream.cancelMessage);
|
||||
assertFalse(stream.hasNext());
|
||||
}
|
||||
|
||||
// --- Client.Java-015: cancelling the user-visible *Async future cancels the gRPC call ---
|
||||
|
||||
@Test
|
||||
void invokeAsyncCancellationCancelsUnderlyingGrpcCall() throws Exception {
|
||||
// Set up a gateway service that never completes the invoke call so cancellation is
|
||||
// the only way the call terminates. Hook ServerCallStreamObserver.setOnCancelHandler
|
||||
// to latch when the server observes cancellation.
|
||||
java.util.concurrent.CountDownLatch serverCancelled = new java.util.concurrent.CountDownLatch(1);
|
||||
TestService service = new TestService() {
|
||||
@Override
|
||||
public void invoke(MxCommandRequest request, StreamObserver<MxCommandReply> responseObserver) {
|
||||
io.grpc.stub.ServerCallStreamObserver<MxCommandReply> serverObserver =
|
||||
(io.grpc.stub.ServerCallStreamObserver<MxCommandReply>) responseObserver;
|
||||
serverObserver.setOnCancelHandler(serverCancelled::countDown);
|
||||
// Intentionally never complete — the call must be terminated by the client
|
||||
// cancelling its future, which must propagate to the gRPC cancellation.
|
||||
}
|
||||
};
|
||||
|
||||
try (Harness harness = Harness.start(service)) {
|
||||
CompletableFuture<MxCommandReply> future = harness.client().invokeAsync(MxCommandRequest.newBuilder()
|
||||
.setSessionId("s-cancel")
|
||||
.setCommand(mxaccess_gateway.v1.MxaccessGateway.MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_REGISTER))
|
||||
.build());
|
||||
|
||||
// Cancellation of the user-visible future must propagate to the gRPC call.
|
||||
assertTrue(future.cancel(true), "cancel(true) should return true on a pending future");
|
||||
assertTrue(
|
||||
serverCancelled.await(5, java.util.concurrent.TimeUnit.SECONDS),
|
||||
"server must observe RPC cancellation after future.cancel(true)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void toCompletableValidatorOverloadForwardsCancellationToSource() {
|
||||
// Unit-level proof: cancel() on the future returned by the validator-aware
|
||||
// toCompletable overload must call cancel(true) on the source ListenableFuture.
|
||||
// This is the core fix for Client.Java-015 — the validator runs inside
|
||||
// toCompletable instead of via .thenApply, so the user holds the future
|
||||
// that is bound to the source.
|
||||
com.google.common.util.concurrent.SettableFuture<String> source =
|
||||
com.google.common.util.concurrent.SettableFuture.create();
|
||||
java.util.concurrent.CompletableFuture<Integer> target =
|
||||
MxGatewayChannels.toCompletable(source, "noop", String::length);
|
||||
|
||||
assertFalse(source.isCancelled());
|
||||
assertTrue(target.cancel(true));
|
||||
assertTrue(source.isCancelled(), "source ListenableFuture must be cancelled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toCompletableNoValidatorOverloadForwardsCancellationToSource() {
|
||||
// Regression for the no-validator overload (the historic toCompletable shape).
|
||||
com.google.common.util.concurrent.SettableFuture<String> source =
|
||||
com.google.common.util.concurrent.SettableFuture.create();
|
||||
java.util.concurrent.CompletableFuture<String> target = MxGatewayChannels.toCompletable(source, "noop");
|
||||
|
||||
assertFalse(source.isCancelled());
|
||||
assertTrue(target.cancel(true));
|
||||
assertTrue(source.isCancelled(), "source ListenableFuture must be cancelled");
|
||||
}
|
||||
|
||||
private static final class RecordingEventsRequestStream
|
||||
extends io.grpc.stub.ClientCallStreamObserver<
|
||||
mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest> {
|
||||
private boolean cancelled;
|
||||
private String cancelMessage;
|
||||
|
||||
@Override
|
||||
public void cancel(String message, Throwable cause) {
|
||||
cancelled = true;
|
||||
cancelMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnReadyHandler(Runnable onReadyHandler) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageCompression(boolean enable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableAutoInboundFlowControl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
}
|
||||
|
||||
private static mxaccess_gateway.v1.MxaccessGateway.MxEvent testEvent(int sequence) {
|
||||
return mxaccess_gateway.v1.MxaccessGateway.MxEvent.newBuilder()
|
||||
.setWorkerSequence(sequence)
|
||||
|
||||
+150
@@ -8976,17 +8976,36 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
getFullTagReferenceBytes();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||
* the two must not be cast or compared. The GalaxyRepository service is
|
||||
* metadata-only and deliberately does not share types with
|
||||
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_data_type = 3;</code>
|
||||
* @return The mxDataType.
|
||||
*/
|
||||
int getMxDataType();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The dataTypeName.
|
||||
*/
|
||||
java.lang.String getDataTypeName();
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The bytes for dataTypeName.
|
||||
*/
|
||||
@@ -9012,12 +9031,24 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
boolean getArrayDimensionPresent();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||
* Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_attribute_category = 8;</code>
|
||||
* @return The mxAttributeCategory.
|
||||
*/
|
||||
int getMxAttributeCategory();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL security-classification identifier, passed through
|
||||
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 security_classification = 9;</code>
|
||||
* @return The securityClassification.
|
||||
*/
|
||||
@@ -9156,6 +9187,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
public static final int MX_DATA_TYPE_FIELD_NUMBER = 3;
|
||||
private int mxDataType_ = 0;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||
* the two must not be cast or compared. The GalaxyRepository service is
|
||||
* metadata-only and deliberately does not share types with
|
||||
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_data_type = 3;</code>
|
||||
* @return The mxDataType.
|
||||
*/
|
||||
@@ -9168,6 +9208,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
@SuppressWarnings("serial")
|
||||
private volatile java.lang.Object dataTypeName_ = "";
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The dataTypeName.
|
||||
*/
|
||||
@@ -9185,6 +9230,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The bytes for dataTypeName.
|
||||
*/
|
||||
@@ -9239,6 +9289,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
public static final int MX_ATTRIBUTE_CATEGORY_FIELD_NUMBER = 8;
|
||||
private int mxAttributeCategory_ = 0;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||
* Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_attribute_category = 8;</code>
|
||||
* @return The mxAttributeCategory.
|
||||
*/
|
||||
@@ -9250,6 +9306,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
public static final int SECURITY_CLASSIFICATION_FIELD_NUMBER = 9;
|
||||
private int securityClassification_ = 0;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL security-classification identifier, passed through
|
||||
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 security_classification = 9;</code>
|
||||
* @return The securityClassification.
|
||||
*/
|
||||
@@ -9956,6 +10018,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
|
||||
private int mxDataType_ ;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||
* the two must not be cast or compared. The GalaxyRepository service is
|
||||
* metadata-only and deliberately does not share types with
|
||||
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_data_type = 3;</code>
|
||||
* @return The mxDataType.
|
||||
*/
|
||||
@@ -9964,6 +10035,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return mxDataType_;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||
* the two must not be cast or compared. The GalaxyRepository service is
|
||||
* metadata-only and deliberately does not share types with
|
||||
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_data_type = 3;</code>
|
||||
* @param value The mxDataType to set.
|
||||
* @return This builder for chaining.
|
||||
@@ -9976,6 +10056,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||
* the two must not be cast or compared. The GalaxyRepository service is
|
||||
* metadata-only and deliberately does not share types with
|
||||
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_data_type = 3;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
@@ -9988,6 +10077,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
|
||||
private java.lang.Object dataTypeName_ = "";
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The dataTypeName.
|
||||
*/
|
||||
@@ -10004,6 +10098,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return The bytes for dataTypeName.
|
||||
*/
|
||||
@@ -10021,6 +10120,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @param value The dataTypeName to set.
|
||||
* @return This builder for chaining.
|
||||
@@ -10034,6 +10138,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
@@ -10044,6 +10153,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||
* </pre>
|
||||
*
|
||||
* <code>string data_type_name = 4;</code>
|
||||
* @param value The bytes for dataTypeName to set.
|
||||
* @return This builder for chaining.
|
||||
@@ -10156,6 +10270,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
|
||||
private int mxAttributeCategory_ ;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||
* Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_attribute_category = 8;</code>
|
||||
* @return The mxAttributeCategory.
|
||||
*/
|
||||
@@ -10164,6 +10284,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return mxAttributeCategory_;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||
* Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_attribute_category = 8;</code>
|
||||
* @param value The mxAttributeCategory to set.
|
||||
* @return This builder for chaining.
|
||||
@@ -10176,6 +10302,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||
* Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 mx_attribute_category = 8;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
@@ -10188,6 +10320,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
|
||||
private int securityClassification_ ;
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL security-classification identifier, passed through
|
||||
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 security_classification = 9;</code>
|
||||
* @return The securityClassification.
|
||||
*/
|
||||
@@ -10196,6 +10334,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return securityClassification_;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL security-classification identifier, passed through
|
||||
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 security_classification = 9;</code>
|
||||
* @param value The securityClassification to set.
|
||||
* @return This builder for chaining.
|
||||
@@ -10208,6 +10352,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Raw Galaxy SQL security-classification identifier, passed through
|
||||
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||
* docs/GalaxyRepository.md.
|
||||
* </pre>
|
||||
*
|
||||
* <code>int32 security_classification = 9;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
|
||||
@@ -40706,16 +40706,31 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
int getVerifierUserId();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
boolean hasValue();
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValue getValue();
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder getValueOrBuilder();
|
||||
@@ -40794,6 +40809,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
public static final int VALUE_FIELD_NUMBER = 4;
|
||||
private mxaccess_gateway.v1.MxaccessGateway.MxValue value_;
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
@@ -40802,6 +40822,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
@@ -40810,6 +40835,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return value_ == null ? mxaccess_gateway.v1.MxaccessGateway.MxValue.getDefaultInstance() : value_;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
@java.lang.Override
|
||||
@@ -41301,6 +41331,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValue, mxaccess_gateway.v1.MxaccessGateway.MxValue.Builder, mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder> valueBuilder_;
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
@@ -41308,6 +41343,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return ((bitField0_ & 0x00000008) != 0);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
@@ -41319,6 +41359,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder setValue(mxaccess_gateway.v1.MxaccessGateway.MxValue value) {
|
||||
@@ -41335,6 +41380,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder setValue(
|
||||
@@ -41349,6 +41399,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder mergeValue(mxaccess_gateway.v1.MxaccessGateway.MxValue value) {
|
||||
@@ -41370,6 +41425,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder clearValue() {
|
||||
@@ -41383,6 +41443,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public mxaccess_gateway.v1.MxaccessGateway.MxValue.Builder getValueBuilder() {
|
||||
@@ -41391,6 +41456,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return internalGetValueFieldBuilder().getBuilder();
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder getValueOrBuilder() {
|
||||
@@ -41402,6 +41472,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
@@ -42314,16 +42389,31 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
int getVerifierUserId();
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
boolean hasValue();
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValue getValue();
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder getValueOrBuilder();
|
||||
@@ -42417,6 +42507,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
public static final int VALUE_FIELD_NUMBER = 4;
|
||||
private mxaccess_gateway.v1.MxaccessGateway.MxValue value_;
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
@@ -42425,6 +42520,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
@@ -42433,6 +42533,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return value_ == null ? mxaccess_gateway.v1.MxaccessGateway.MxValue.getDefaultInstance() : value_;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
@java.lang.Override
|
||||
@@ -42988,6 +43093,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
mxaccess_gateway.v1.MxaccessGateway.MxValue, mxaccess_gateway.v1.MxaccessGateway.MxValue.Builder, mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder> valueBuilder_;
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return Whether the value field is set.
|
||||
*/
|
||||
@@ -42995,6 +43105,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return ((bitField0_ & 0x00000008) != 0);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
* @return The value.
|
||||
*/
|
||||
@@ -43006,6 +43121,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder setValue(mxaccess_gateway.v1.MxaccessGateway.MxValue value) {
|
||||
@@ -43022,6 +43142,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder setValue(
|
||||
@@ -43036,6 +43161,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder mergeValue(mxaccess_gateway.v1.MxaccessGateway.MxValue value) {
|
||||
@@ -43057,6 +43187,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public Builder clearValue() {
|
||||
@@ -43070,6 +43205,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public mxaccess_gateway.v1.MxaccessGateway.MxValue.Builder getValueBuilder() {
|
||||
@@ -43078,6 +43218,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
return internalGetValueFieldBuilder().getBuilder();
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
public mxaccess_gateway.v1.MxaccessGateway.MxValueOrBuilder getValueOrBuilder() {
|
||||
@@ -43089,6 +43234,11 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Credential-sensitive write value. Implementations must not log this field
|
||||
* unless an explicit redacted value-logging path is enabled.
|
||||
* </pre>
|
||||
*
|
||||
* <code>.mxaccess_gateway.v1.MxValue value = 4;</code>
|
||||
*/
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
@@ -43322,6 +43472,7 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* <pre>
|
||||
* Bulk Read — snapshot the current value for each requested tag. MXAccess COM
|
||||
* has no synchronous Read; the worker implements ReadBulk as:
|
||||
*
|
||||
* - If the tag is already in the session's item registry AND that item is
|
||||
* currently advised AND the worker has a cached OnDataChange for it, the
|
||||
* reply returns the cached value WITHOUT modifying the existing
|
||||
@@ -43330,6 +43481,7 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
* UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
* as it was before the call (was_cached = false).
|
||||
*
|
||||
* `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
* </pre>
|
||||
*
|
||||
@@ -43619,6 +43771,7 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* <pre>
|
||||
* Bulk Read — snapshot the current value for each requested tag. MXAccess COM
|
||||
* has no synchronous Read; the worker implements ReadBulk as:
|
||||
*
|
||||
* - If the tag is already in the session's item registry AND that item is
|
||||
* currently advised AND the worker has a cached OnDataChange for it, the
|
||||
* reply returns the cached value WITHOUT modifying the existing
|
||||
@@ -43627,6 +43780,7 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
* UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
* as it was before the call (was_cached = false).
|
||||
*
|
||||
* `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
* </pre>
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user