88import java .util .*;
99
1010import org .jspecify .annotations .Nullable ;
11- import org .slf4j .Logger ;
12- import org .slf4j .LoggerFactory ;
1311
1412import io .jooby .*;
1513import io .jooby .exception .MissingValueException ;
1917import io .jooby .jsonrpc .instrumentation .OtelJsonRcpTracing ;
2018
2119/**
22- * Global Tier 1 Dispatcher for JSON-RPC 2.0 requests .
20+ * Jooby Extension module for integrating JSON-RPC 2.0 capabilities .
2321 *
24- * <p>This dispatcher acts as the central entry point for all JSON-RPC traffic. It manages the
25- * lifecycle of a request by:
22+ * <p>This module acts as the central configuration point for setting up a JSON-RPC endpoint. It
23+ * registers the target {@link JsonRpcService} instances, configures the route path, maps standard
24+ * framework exceptions to JSON-RPC error codes, and installs the underlying request handler into
25+ * the Jooby application.
26+ *
27+ * <h3>Middleware Pipeline (Invoker / Chain API)</h3>
28+ *
29+ * <p>This module allows you to configure a pipeline of interceptors using the {@link
30+ * JsonRpcInvoker} API. By adding invokers, you create a {@link JsonRpcChain} that wraps the final
31+ * method execution. This is the standard way to apply cross-cutting concerns to your RPC endpoints,
32+ * such as:
2633 *
2734 * <ul>
28- * <li>Parsing the incoming body into a {@link JsonRpcRequest} (supporting both single and batch
29- * shapes).
30- * <li>Iterating through registered {@link JsonRpcService} instances to find a matching namespace.
31- * <li>Handling <strong>Notifications</strong> (requests without an {@code id}) by suppressing
32- * responses.
33- * <li>Unifying batch results into a single JSON array or a single object response as per the
34- * spec.
35+ * <li>Logging request payloads and execution times.
36+ * <li>Enforcing security and authorization rules.
37+ * <li>Gathering metrics and OpenTelemetry tracing.
3538 * </ul>
3639 *
37- * <p>*
38- *
39- * <p>Usage:
40+ * <h3>Usage:</h3>
4041 *
4142 * <pre>{@code
43+ * {
4244 * install(new Jackson3Module());
43- *
4445 * install(new JsonRpcJackson3Module());
45- *
46- * install (new JsonRpcModule(new MyServiceRpc_ ()));
47- *
46+ * * install(new JsonRpcModule(new MyServiceRpc_())
47+ * .invoker (new MyJsonRpcMiddleware ()));
48+ * }
4849 * }</pre>
4950 *
5051 * @author Edgar Espina
5152 * @since 4.0.17
5253 */
5354public class JsonRpcModule implements Extension {
54- private final Logger log = LoggerFactory .getLogger (JsonRpcService .class );
5555 private final Map <String , JsonRpcService > services = new HashMap <>();
5656 private final String path ;
5757 private @ Nullable JsonRpcInvoker invoker ;
5858 private @ Nullable OtelJsonRcpTracing head ;
5959
60+ /**
61+ * Creates a new JSON-RPC module at a custom HTTP path.
62+ *
63+ * @param path The HTTP path where the JSON-RPC endpoint will be mounted (e.g., {@code
64+ * "/api/rpc"}).
65+ * @param service The primary {@link JsonRpcService} containing the RPC methods to expose.
66+ * @param services Additional {@link JsonRpcService} instances to expose on the same endpoint.
67+ */
6068 public JsonRpcModule (String path , JsonRpcService service , JsonRpcService ... services ) {
6169 this .path = path ;
6270 registry (service );
6371 Arrays .stream (services ).forEach (this ::registry );
6472 }
6573
74+ /**
75+ * Creates a new JSON-RPC module mounted at the default {@code "/rpc"} HTTP path.
76+ *
77+ * @param service The primary {@link JsonRpcService} containing the RPC methods to expose.
78+ * @param services Additional {@link JsonRpcService} instances to expose on the same endpoint.
79+ */
6680 public JsonRpcModule (JsonRpcService service , JsonRpcService ... services ) {
6781 this ("/rpc" , service , services );
6882 }
6983
84+ /**
85+ * Adds a {@link JsonRpcInvoker} middleware to the execution pipeline.
86+ *
87+ * <p>Middlewares are composed together to form a {@link JsonRpcChain}. When multiple invokers are
88+ * registered, they wrap around each other, meaning the first added invoker will execute first.
89+ *
90+ * <p><strong>Tracing Priority:</strong> If the provided invoker is an instance of {@link
91+ * OtelJsonRcpTracing}, it is automatically promoted to the absolute head of the pipeline. This
92+ * guarantees that OpenTelemetry spans encompass all other middlewares and the final execution.
93+ *
94+ * @param invoker The middleware interceptor to add to the pipeline.
95+ * @return This module instance for fluent configuration chaining.
96+ */
7097 public JsonRpcModule invoker (JsonRpcInvoker invoker ) {
7198 if (invoker instanceof OtelJsonRcpTracing otel ) {
7299 // otel goes first:
@@ -88,10 +115,15 @@ private void registry(JsonRpcService service) {
88115 }
89116
90117 /**
91- * Installs the JSON-RPC handler at the default {@code /rpc} endpoint.
118+ * Installs the JSON-RPC handler into the Jooby application.
119+ *
120+ * <p>This method is invoked automatically by Jooby during application startup. It resolves the
121+ * final middleware chain, registers the HTTP POST route at the configured path, and sets up
122+ * default exception mappings for standard Jooby routing errors (like missing or mismatched
123+ * parameters).
92124 *
93125 * @param app The Jooby application instance.
94- * @throws Exception If registration fails.
126+ * @throws Exception If route registration or configuration fails.
95127 */
96128 @ Override
97129 public void install (Jooby app ) throws Exception {
0 commit comments