Skip to content

Commit 0f86623

Browse files
committed
- document new features
1 parent b9b3d0d commit 0f86623

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

docs/asciidoc/modules/json-rpc.adoc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,90 @@ Supported engines include:
105105

106106
No additional configuration is required. The generated dispatcher automatically hooks into the installed engine using the `JsonRpcParser` and `JsonRpcDecoder` interfaces, ensuring primitive types are strictly validated and parsed.
107107

108+
=== Middleware Pipeline
109+
110+
Jooby provides a dedicated middleware architecture for JSON-RPC using the `JsonRpcInvoker` and `JsonRpcChain` APIs. This allows you to intercept RPC calls to apply cross-cutting concerns like logging, security, metrics, or tracing.
111+
112+
To create an interceptor, implement the `JsonRpcInvoker` interface.
113+
114+
.JSON-RPC
115+
[source,java,role="primary"]
116+
----
117+
import io.jooby.jsonrpc.*;
118+
import java.util.Optional;
119+
120+
public class LoggingInvoker implements JsonRpcInvoker {
121+
122+
@Override
123+
public Optional<JsonRpcResponse> invoke(Context ctx, JsonRpcRequest request, JsonRpcChain next) {
124+
long start = System.currentTimeMillis();
125+
126+
// Proceed down the chain
127+
Optional<JsonRpcResponse> response = next.proceed(ctx, request);
128+
129+
long took = System.currentTimeMillis() - start;
130+
131+
// Inspect the response
132+
response.ifPresent(res -> {
133+
if (res.getError() != null) {
134+
ctx.getLog().warn("RPC {} failed in {}ms", request.getMethod(), took);
135+
} else {
136+
ctx.getLog().info("RPC {} succeeded in {}ms", request.getMethod(), took);
137+
}
138+
});
139+
140+
return response;
141+
}
142+
}
143+
----
144+
145+
.Kotlin
146+
[source,kotlin,role="secondary"]
147+
----
148+
import io.jooby.jsonrpc.*
149+
import java.util.Optional
150+
151+
class LoggingInvoker : JsonRpcInvoker {
152+
153+
override fun invoke(ctx: Context, request: JsonRpcRequest, next: JsonRpcChain): Optional<JsonRpcResponse> {
154+
val start = System.currentTimeMillis()
155+
156+
// Proceed down the chain
157+
val response = next.proceed(ctx, request)
158+
159+
val took = System.currentTimeMillis() - start
160+
161+
// Inspect the response
162+
response.ifPresent { res ->
163+
if (res.error != null) {
164+
ctx.log.warn("RPC {} failed in {}ms", request.method, took)
165+
} else {
166+
ctx.log.info("RPC {} succeeded in {}ms", request.method, took)
167+
}
168+
}
169+
170+
return response
171+
}
172+
}
173+
----
174+
175+
You register invokers fluently when installing the `JsonRpcModule`. You can chain multiple invokers together, and they will execute in the order they are added.
176+
177+
[source,java]
178+
----
179+
install(new JsonRpcModule(new MovieServiceRpc_())
180+
.invoker(new SecurityInvoker())
181+
.invoker(new LoggingInvoker()));
182+
----
183+
184+
==== Safe Exception Handling
185+
Notice that you **do not** need to wrap `next.proceed()` in a `try-catch` block. The final executor in the JSON-RPC pipeline acts as an ultimate safety net. It catches all unhandled exceptions, protocol failures (like Parse Errors), and routing failures, safely transforming them into a standard `JsonRpcResponse` containing an `ErrorDetail`.
186+
187+
To react to failures in your middleware, simply inspect `response.get().getError() != null`.
188+
189+
==== Notifications and Optional Responses
190+
The invocation pipeline returns an `Optional<JsonRpcResponse>`. This is because the JSON-RPC 2.0 specification explicitly dictates that **Notifications** (requests sent without an `id` member) must not receive a response. For these requests, the chain will safely execute the target method but return `Optional.empty()`.
191+
108192
=== Error Mapping
109193

110194
Jooby seamlessly bridges standard Java application exceptions and HTTP status codes into the JSON-RPC 2.0 format using the `JsonRpcErrorCode` mapping. You do not need to throw custom protocol exceptions for standard failures.

docs/asciidoc/modules/opentelemetry.adoc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,46 @@ import io.jooby.opentelemetry.instrumentation.OtelHikari
284284
}
285285
----
286286

287+
==== JSON-RPC
288+
289+
Provides automatic tracing for your JSON-RPC 2.0 endpoints. By adding the `OtelJsonRcpTracing` middleware to your JSON-RPC pipeline, it generates a dedicated OpenTelemetry span for every RPC invocation.
290+
291+
It automatically records standard semantic attributes (such as `rpc.system`, `rpc.method`, and `rpc.jsonrpc.request_id`). Furthermore, because it hooks directly into the `JsonRpcChain`, it accurately records protocol errors and application failures by inspecting the `JsonRpcResponse` envelope, without relying on thrown exceptions.
292+
293+
.JSON-RPC Integration
294+
[source, java, role = "primary"]
295+
----
296+
import io.jooby.jsonrpc.JsonRpcModule;
297+
import io.jooby.jsonrpc.instrumentation.OtelJsonRcpTracing;
298+
import io.opentelemetry.api.OpenTelemetry;
299+
300+
{
301+
install(new OtelModule());
302+
303+
// Register the JSON-RPC module and attach the tracing middleware
304+
install(new JsonRpcModule(new MovieServiceRpc_())
305+
.invoker(new OtelJsonRcpTracing(require(OpenTelemetry.class)))
306+
);
307+
}
308+
----
309+
310+
.Kotlin
311+
[source, kt, role="secondary"]
312+
----
313+
import io.jooby.jsonrpc.JsonRpcModule
314+
import io.jooby.jsonrpc.instrumentation.OtelJsonRcpTracing
315+
import io.opentelemetry.api.OpenTelemetry
316+
317+
{
318+
install(OtelModule())
319+
320+
// Register the JSON-RPC module and attach the tracing middleware
321+
install(JsonRpcModule(MovieServiceRpc_())
322+
.invoker(OtelJsonRcpTracing(require(OpenTelemetry::class.java)))
323+
)
324+
}
325+
----
326+
287327
==== Log4j2
288328

289329
Seamlessly exports all application logs to your OpenTelemetry backend, automatically correlated with active trace and span IDs using a dynamic appender.

0 commit comments

Comments
 (0)