Class ModuleRequestHandler
- All Implemented Interfaces:
CloudRequestHandler
module.request to the released 6.3.0
PanelResponderRegistry; internal, not for module use.
One request names a module, an action, that action's parameters and the actor the gateway
asserts (D-25). The payload is read strictly against the frozen module.request schema, so
a missing, extra or wrongly typed field becomes a correlated invalid_message before any
module code runs.
The payload's module is the module's UltiCloud identifier -- its identify-string,
lower-case (ModuleIds), never its plugin.yml name. The responder is the one PanelResponderRegistry.moduleRequestType(String, String) names: registered under
<module>.<action> or, as in 6.3.0, under <owner name lower-cased>.<action>, and owned by the
module with that identifier. A request can never reach another module's responder, and one that
would is answered module_not_found without dispatching (T-22-99). The responder receives a fresh object holding a copy of
params plus the actor object exactly as received; the plugin neither derives nor
overrides the actor, which the authenticated gateway channel vouches for (T-22-98).
This handler does not consult the capabilities the gateway accepted: the router admits
module.request through its fromPayload:module sentinel, and the only module check
here is the responder's owner. Whether a request for a module whose declared capability the
gateway did not accept is filtered is decided in 22-21.
Each module-owned request is also republished on the module-facing event bus as one
PanelMessageEvent, built on the dispatching thread and delivered on the main thread, with
the fields the v1 inbound path gives it: the type the responder is registered under, the
responder's input and the request envelope as received. A request answered
module_not_found reached no module's responder and publishes nothing.
Exactly one module.response is produced per request, on whichever thread settles the
registry's future, and the dispatching thread is never blocked:
- the responder's result becomes
status: okwith that result asdata, whatever its shape: an object, an array, a primitive, ornullwhen the responder's future completed withnullorJsonNull("answered, no content"); - no responder, or one owned by another module, becomes
module_not_found; - the registry's deadline passing becomes
deadline_exceeded; - every other failure — a responder that throws, completes exceptionally, returns no future
(
nullinstead of a future), or is refused because the registry's queue is full or the registry shut down — becomesmodule_errorcarrying only the root cause's message, cut to the schema's limit; - a result that cannot be written as strict JSON — a
NaNor infinite number, which Gson writes as a bare token no strict parser accepts — becomesmodule_errorwith a fixed message, because the gateway would drop such a frame and the panel would wait for its own deadline; - a reply that cannot be built at all becomes one
internal_error, so the single reply is never lost to a channel or serialiser failure inside the completion callback.
- Since:
- 6.3.1
-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final PatternThe contract's action pattern: lowercase letters, digits, dot, underscore and hyphen.private static final Stringprivate static final Stringprivate static final longThe largest actor user id the contract accepts.private static final intThe contract's bound on a response message.private static final intThe contract's bound on a module identifier.private static final Stringprivate static final Stringprivate final PanelResponderRegistryprivate static final Stringprivate static final com.google.gson.GsonShared Gson used only to hand a result to a non-lenientJsonWriter, which is the one check that both walks the whole value and refuses a non-finite number:Gson#toJson(JsonElement)always writes leniently, and Gson's strict reader accepts a bareNaNas the string"NaN", so serialising and reparsing would prove nothing.private final ShutdownGate.Signal -
Constructor Summary
ConstructorsConstructorDescriptionModuleRequestHandler(PanelResponderRegistry registry) Creates the bridge over the registry the framework already owns, never refused for a shutdown.ModuleRequestHandler(PanelResponderRegistry registry, ShutdownGate.Signal shutdown) -
Method Summary
Modifier and TypeMethodDescriptionvoidhandle(CloudRequest request, CloudChannel channel) Validates onemodule.requestand hands it to its owning module's responder.private static booleanisDeadline(Throwable failure) Whether the registry's own deadline failed this request, rather than a responder.private static booleanisStrictJson(com.google.gson.JsonElement result) Whetherresultcan be written as strict JSON.private static voidpublishPanelMessageEvent(CloudRequest request, String messageType, com.google.gson.JsonObject data) Republishes the request for modules that subscribe toPanelMessageEvent, exactly as the v1 inbound path does: built here on the dispatching thread, delivered on the main thread, carrying the type the responder is registered under, the responder's input (the request's params plus the gateway-asserted actor) and the v2 envelope as received.private static voidreply(CloudChannel channel, String replyTo, com.google.gson.JsonElement result, Throwable failure) Produces the request's single reply from the registry's outcome.private static StringrootCauseMessage(Throwable failure) The deepest cause's message, which is the contract'smodule_errormessage; the framework's own wrapper text around it is not sent.private static voidsendInternalError(CloudChannel channel, String replyTo) Answersinternal_erroronce, and gives up quietly if the channel refuses that too.private static Stringprivate static voidvalidateActor(String id, com.google.gson.JsonObject actor) Checks the gateway-asserted actor against the contract's actor object, exactly as sent.
-
Field Details
-
ACTION
The contract's action pattern: lowercase letters, digits, dot, underscore and hyphen. -
MODULE_IDENTIFIER_MAX_LENGTH
private static final int MODULE_IDENTIFIER_MAX_LENGTHThe contract's bound on a module identifier.- See Also:
-
MAX_USER_ID
private static final long MAX_USER_IDThe largest actor user id the contract accepts.- See Also:
-
MESSAGE_MAX_LENGTH
private static final int MESSAGE_MAX_LENGTHThe contract's bound on a response message.- See Also:
-
RESPONSE_TYPE
- See Also:
-
NO_RESPONDER_MESSAGE
- See Also:
-
DEADLINE_MESSAGE
- See Also:
-
NON_JSON_MESSAGE
- See Also:
-
INTERNAL_ERROR_MESSAGE
- See Also:
-
RESULT_GSON
private static final com.google.gson.Gson RESULT_GSONShared Gson used only to hand a result to a non-lenientJsonWriter, which is the one check that both walks the whole value and refuses a non-finite number:Gson#toJson(JsonElement)always writes leniently, and Gson's strict reader accepts a bareNaNas the string"NaN", so serialising and reparsing would prove nothing. -
registry
-
shutdown
-
-
Constructor Details
-
ModuleRequestHandler
ModuleRequestHandler(PanelResponderRegistry registry) Creates the bridge over the registry the framework already owns, never refused for a shutdown.- Parameters:
registry- the registry holding module responders; never null
-
ModuleRequestHandler
ModuleRequestHandler(PanelResponderRegistry registry, ShutdownGate.Signal shutdown) - Parameters:
registry- the registry holding module responders; never nullshutdown- the shutdown signal (ShutdownGate): a request is handed to its responder only with a permit, which is returned when the responder's work ends (22-23 second verification), soUltiTools#onDisablewaits for a responder that started before it, bounded byPluginInitiationUtils.PANEL_CHANGE_DRAIN_MS
-
-
Method Details
-
handle
Validates onemodule.requestand hands it to its owning module's responder.- Specified by:
handlein interfaceCloudRequestHandler- Parameters:
request- the parsed request whose payload names module, action, params and actorchannel- the reply channel; a reply may be dropped when the connection has closed
-
publishPanelMessageEvent
private static void publishPanelMessageEvent(CloudRequest request, String messageType, com.google.gson.JsonObject data) Republishes the request for modules that subscribe toPanelMessageEvent, exactly as the v1 inbound path does: built here on the dispatching thread, delivered on the main thread, carrying the type the responder is registered under, the responder's input (the request's params plus the gateway-asserted actor) and the v2 envelope as received. Best effort: no failure here may affect the request's single reply. -
validateActor
Checks the gateway-asserted actor against the contract's actor object, exactly as sent. -
reply
private static void reply(CloudChannel channel, String replyTo, com.google.gson.JsonElement result, Throwable failure) Produces the request's single reply from the registry's outcome. -
sendInternalError
Answersinternal_erroronce, and gives up quietly if the channel refuses that too. -
isStrictJson
private static boolean isStrictJson(com.google.gson.JsonElement result) Whetherresultcan be written as strict JSON. A non-lenientJsonWriterrefuses a non-finitedoubleorfloatwithIllegalArgumentException, which is exactly the value the gateway's parser rejects and Gson would otherwise write as the bare tokenNaNorInfinity. -
isDeadline
Whether the registry's own deadline failed this request, rather than a responder. -
rootCauseMessage
The deepest cause's message, which is the contract'smodule_errormessage; the framework's own wrapper text around it is not sent. A blank message falls back to the root cause's class name, and the result is cut to the schema's limit. -
truncate
-