Class AgentProtocol
CloudChannel.
Handshake (D-12, D-13, contract SEMANTICS 10). onOpen() sends hello
as the connection's first frame; nothing else is sent until a welcome answers it. A
welcome counts only when it replies to that hello, matches the frozen schema and keeps every limit
inside the contract bounds (S-51); otherwise it is answered with error invalid_message,
the socket is closed with 1000 and the normal backoff follows. A valid welcome naming another
server id than the linked one stops the connection for good, credential kept (contract answer 21,
SEMANTICS 16). Only a valid, matching welcome makes the connection established: the backoff
counters reset, the heartbeat starts (AgentConnection.markEstablished()) and
channel() opens.
Reliable events (D-11, D-15, contract answers 3 and 4). After the welcome every
unacknowledged outbox entry is sent again with its own envelope id and timestamp, then a pending
drop count is reported once as outbox.overflow and cleared only when that frame was handed
to the socket. An ack { ids } removes exactly those ids from the outbox.
Capabilities (contract SEMANTICS 8). An outbound frame whose capability the welcome did
not accept is not sent; an inbound request whose capability the welcome did not accept, or whose
operator switch is off, is refused with capability_not_accepted by the
AgentMessageRouter. The welcome's accepted list is intersected with what the hello
declared, so a gateway cannot widen it.
Inbound errors (SEMANTICS 6, 14). An error with replyTo reaches every
reply listener and logs one WARNING per error code per minute; an
error rate_limited without replyTo is logged at FINE, at most once a minute at
INFO. An error is never answered. No log line carries a payload, a message from the gateway or
a credential (S-34).
Threads and locks. Inbound frames arrive on the agent scheduler thread. Every frame this
class hands to the socket, from any thread, is serialised on one private lock, which is held only
while the frame is handed over (AgentConnection.send(String) never blocks) and never while
calling a listener, the router or a connection method that calls out. In the session's lock order
this lock sits below the session monitor and above the AgentConnection monitor and the
CloudOutbox and CommandDedupeSet leaf monitors.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static interfaceLifecycle callbacks of one connection; never invoked while this protocol's lock is held.private final classThe live channel: the framed channel, with the outbound capability gate in front.private static final classOne validated welcome. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final CloudChannelprivate final LongSupplierprivate booleanprivate final AgentConnectionprivate final CommandDedupeSetprivate static final Patternprivate booleanprivate final Stringprivate final FramedChannelprivate longprivate Stringprivate final List<AgentProtocol.Listener> private CloudLimitsprivate final Objectprivate final BiConsumer<Level, String> (package private) static final intThe schema bound on the ids of oneack.(package private) static final intThe schema bound on the number of capabilities in hello and welcome.(package private) static final intThe schema bound on the length of one capability name.private static final long(package private) static final StringThe routing sentinelmodule.requestcarries instead of a capability name.(package private) static final longHow often one notice of a kind may be logged above FINE.private final CloudOutboxprivate final List<Consumer<CloudRequest>> private final AgentMessageRouterprivate final Supplier<com.google.gson.JsonObject> private static final String[]private boolean -
Constructor Summary
ConstructorsConstructorDescriptionAgentProtocol(AgentConnection connection, CloudOutbox outbox, CommandDedupeSet dedupe, String expectedServerId, Supplier<List<String>> capabilities, Supplier<com.google.gson.JsonObject> settings, Predicate<String> capabilityEnabled, Function<String, String> refusalForCapability, LongSupplier clock) The production protocol of one connection attempt, logging throughPanelConnectionLog.AgentProtocol(AgentConnection connection, CloudOutbox outbox, CommandDedupeSet dedupe, String expectedServerId, Supplier<List<String>> capabilities, Supplier<com.google.gson.JsonObject> settings, Predicate<String> capabilityEnabled, Function<String, String> refusalForCapability, LongSupplier clock, BiConsumer<Level, String> log) The full constructor, with the log sink tests capture. -
Method Summary
Modifier and TypeMethodDescription(package private) voidaddLifecycleListener(AgentProtocol.Listener listener) (package private) voidaddReplyListener(Consumer<CloudRequest> listener) private voidapplyLimits(CloudLimits accepted) Under the lock: the welcome's limits reach the outbox and the command-dedupe set (D-13).(package private) booleanWhether a hello built now would declare another capability set than this connection's hello did (22-22 fix): the set is negotiated only at the handshake, so such a change needs a new connection.(package private) CloudChannelchannel()private boolean(package private) longprivate booleanThe framed channel's socket hand-off: serialised, and only while established.private com.google.gson.JsonObjectprivate booleaninboundAllowed(String capability) The router's gate: accepted at the handshake and switched on by the operator.(package private) boolean(package private) CloudLimitslimits()private voidUnder the lock: logsmessageatlevelat most once perNOTICE_INTERVAL_MSper key, otherwise at FINE.private void(package private) voidonClose()The connection closed: the channel stops sending and lifecycle listeners are told once.private void(package private) voidonOpen()The upgrade was accepted: sendshello, the connection's first frame.(package private) voidOne inbound text frame other than the heartbeat reply.private voidprivate booleanoutboundAllowed(String type) (package private) voidreconnectForNewHello(String reason) Closes this connection normally (1000) and lets the connection retry after its normal backoff, so the next hello declares the current capability set (22-22 fix).private voidrefuseBeforeWelcome(InvalidFrameException invalid, List<Runnable> after) A frame that fails the envelope check before welcome.private voidUnder the lock: oneoutbox.overflow, cleared only once handed to the socket (D-15).private voidUnder the lock: every unacknowledged entry, with its own id and timestamp (D-11).(package private) AgentMessageRouterrouter()private voidprivate booleansendEntry(CloudOutbox.Entry entry) Under the lock.private voidsendErrorUnlocked(String replyTo, String code, String message) Under the lock: an error frame sent even before welcome (the refused welcome's answer).(package private) booleansendReliable(CloudOutbox.Entry entry) Sends one reliable event that was just added to the outbox, if the connection is established and its capability accepted; otherwise it waits in the outbox for the next welcome.
-
Field Details
-
MODULE_CAPABILITY_SENTINEL
The routing sentinelmodule.requestcarries instead of a capability name.- See Also:
-
NOTICE_INTERVAL_MS
static final long NOTICE_INTERVAL_MSHow often one notice of a kind may be logged above FINE.- See Also:
-
MAX_CAPABILITIES
static final int MAX_CAPABILITIESThe schema bound on the number of capabilities in hello and welcome.- See Also:
-
MAX_CAPABILITY_CHARS
static final int MAX_CAPABILITY_CHARSThe schema bound on the length of one capability name.- See Also:
-
MAX_ACK_IDS
static final int MAX_ACK_IDSThe schema bound on the ids of oneack.- See Also:
-
MAX_TIMESTAMP
private static final long MAX_TIMESTAMP- See Also:
-
ERROR_CODE
-
WELCOME_KEYS
-
lock
-
connection
-
outbox
-
dedupe
-
expectedServerId
-
capabilities
-
settings
-
capabilityEnabled
-
clock
-
log
-
router
-
framed
-
channel
-
replyListeners
-
lifecycleListeners
-
helloId
-
declared
-
welcomeSeen
private boolean welcomeSeen -
closed
private boolean closed -
lastNotice
-
established
private volatile boolean established -
limits
-
accepted
-
gatewayClockOffsetMillis
private volatile long gatewayClockOffsetMillis
-
-
Constructor Details
-
AgentProtocol
AgentProtocol(AgentConnection connection, CloudOutbox outbox, CommandDedupeSet dedupe, String expectedServerId, Supplier<List<String>> capabilities, Supplier<com.google.gson.JsonObject> settings, Predicate<String> capabilityEnabled, Function<String, String> refusalForCapability, LongSupplier clock) The production protocol of one connection attempt, logging throughPanelConnectionLog.- Parameters:
connection- the attempt's connection; frames are handed to itssendoutbox- the session's reliable-event buffer, ornullwhen it has nonededupe- the session's command-id set, ornullwhen it has noneexpectedServerId- the linked server id the welcome must namecapabilities- the capabilities the hello declaressettings- the operator settings the hello reports (powerActionsEnabled)capabilityEnabled- the operator switch of a contract capability (S-66); also asked forMODULE_CAPABILITY_SENTINELrefusalForCapability- safe refusal text naming the switch's configuration keyclock- epoch milliseconds
-
AgentProtocol
AgentProtocol(AgentConnection connection, CloudOutbox outbox, CommandDedupeSet dedupe, String expectedServerId, Supplier<List<String>> capabilities, Supplier<com.google.gson.JsonObject> settings, Predicate<String> capabilityEnabled, Function<String, String> refusalForCapability, LongSupplier clock, BiConsumer<Level, String> log) The full constructor, with the log sink tests capture.
-
-
Method Details
-
router
AgentMessageRouter router()- Returns:
- the router of this connection; track handlers are registered on it (22-21)
-
channel
CloudChannel channel()- Returns:
- the live channel: open only between a valid welcome and the close
-
gatewayClockOffsetMillis
long gatewayClockOffsetMillis()- Returns:
- the welcome's
serverTimeminus the local time at its receipt; 0 before welcome
-
isEstablished
boolean isEstablished()- Returns:
- whether a valid, matching welcome answered the hello and the connection is not closed
-
limits
CloudLimits limits()- Returns:
- the accepted limits, or the contract defaults before welcome
-
acceptedCapabilities
- Returns:
- the capabilities the welcome accepted (within those the hello declared); empty before
-
capabilitiesChangedSinceHello
boolean capabilitiesChangedSinceHello()Whether a hello built now would declare another capability set than this connection's hello did (22-22 fix): the set is negotiated only at the handshake, so such a change needs a new connection. Builds the set exactly asonOpen()does.- Returns:
falsebefore the hello was sent, or when the set is unchanged
-
reconnectForNewHello
Closes this connection normally (1000) and lets the connection retry after its normal backoff, so the next hello declares the current capability set (22-22 fix). Asynchronous; a no-op unless the socket is open.- Parameters:
reason- the reason for the retry line
-
addReplyListener
- Parameters:
listener- receives every inbounderrorthat carries areplyTo, on the agent thread; it must not block
-
addLifecycleListener
- Parameters:
listener- receives this connection's establishment and close; a listener added after the connection was established is told at once
-
onOpen
void onOpen()The upgrade was accepted: sendshello, the connection's first frame. Once only. -
onText
One inbound text frame other than the heartbeat reply.- Parameters:
text- the frame, never logged
-
onClose
void onClose()The connection closed: the channel stops sending and lifecycle listeners are told once. -
sendReliable
Sends one reliable event that was just added to the outbox, if the connection is established and its capability accepted; otherwise it waits in the outbox for the next welcome.- Parameters:
entry- the new outbox entry, sent with its own envelope id- Returns:
- whether it was handed to the socket
-
onWelcome
-
refuseBeforeWelcome
A frame that fails the envelope check before welcome. Only a welcome may arrive then, so the frame is treated as a broken welcome (for example the vendoredmissing-replyTo.json): it is answered witherror invalid_message(replyTo the frame's id when that id is a readable ULID, otherwise none) and the attempt is abandoned through the same path as a welcome that breaks the contract, instead of waiting for the welcome deadline. -
applyLimits
Under the lock: the welcome's limits reach the outbox and the command-dedupe set (D-13). -
resendUnacked
private void resendUnacked()Under the lock: every unacknowledged entry, with its own id and timestamp (D-11). -
reportOverflow
private void reportOverflow()Under the lock: oneoutbox.overflow, cleared only once handed to the socket (D-15). -
sendEntry
Under the lock. -
fits
-
onAck
-
ackIds
-
onError
-
notice
Under the lock: logsmessageatlevelat most once perNOTICE_INTERVAL_MSper key, otherwise at FINE. -
sendErrorUnlocked
Under the lock: an error frame sent even before welcome (the refused welcome's answer). -
inboundAllowed
The router's gate: accepted at the handshake and switched on by the operator. -
outboundAllowed
-
handOff
The framed channel's socket hand-off: serialised, and only while established. -
helloCapabilities
-
helloSettings
private com.google.gson.JsonObject helloSettings() -
safely
-