Skip to content

Commit d133758

Browse files
committed
WIP
1 parent dee2e39 commit d133758

7 files changed

Lines changed: 101 additions & 25 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.jsonrpc;
7+
8+
import org.jspecify.annotations.NonNull;
9+
10+
import io.jooby.Context;
11+
import io.jooby.SneakyThrows;
12+
import io.jooby.jsonrpc.JsonRpcInvoker;
13+
import io.jooby.jsonrpc.JsonRpcRequest;
14+
15+
public class DefaultJsonRpcInvoker implements JsonRpcInvoker {
16+
@Override
17+
public <R> R invoke(
18+
@NonNull Context ctx,
19+
@NonNull JsonRpcRequest request,
20+
SneakyThrows.@NonNull Supplier<R> action) {
21+
try {
22+
return action.get();
23+
} catch (Throwable cause) {
24+
throw SneakyThrows.propagate(cause);
25+
}
26+
}
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.jsonrpc;
7+
8+
import java.util.Objects;
9+
10+
import io.jooby.Context;
11+
import io.jooby.SneakyThrows;
12+
13+
public interface JsonRpcInvoker {
14+
15+
Object invoke(Context ctx, JsonRpcRequest request, SneakyThrows.Supplier<Object> action)
16+
throws Exception;
17+
18+
default JsonRpcInvoker then(JsonRpcInvoker next) {
19+
Objects.requireNonNull(next, "next invoker is required");
20+
return new JsonRpcInvoker() {
21+
@Override
22+
public Object invoke(
23+
Context ctx, JsonRpcRequest request, SneakyThrows.Supplier<Object> action)
24+
throws Exception {
25+
return JsonRpcInvoker.this.invoke(ctx, request, () -> next.invoke(ctx, request, action));
26+
}
27+
};
28+
}
29+
}

modules/jooby-jsonrpc/src/main/java/io/jooby/jsonrpc/JsonRpcModule.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.*;
99

10+
import org.jspecify.annotations.Nullable;
1011
import org.slf4j.Logger;
1112
import org.slf4j.LoggerFactory;
1213

@@ -50,6 +51,7 @@ public class JsonRpcModule implements Extension {
5051
private final Logger log = LoggerFactory.getLogger(JsonRpcService.class);
5152
private final Map<String, JsonRpcService> services = new HashMap<>();
5253
private final String path;
54+
private @Nullable JsonRpcInvoker invoker;
5355

5456
public JsonRpcModule(String path, JsonRpcService service, JsonRpcService... services) {
5557
this.path = path;
@@ -61,6 +63,15 @@ public JsonRpcModule(JsonRpcService service, JsonRpcService... services) {
6163
this("/rpc", service, services);
6264
}
6365

66+
public JsonRpcModule invoker(JsonRpcInvoker invoker) {
67+
if (this.invoker != null) {
68+
this.invoker = invoker.then(this.invoker);
69+
} else {
70+
this.invoker = invoker;
71+
}
72+
return this;
73+
}
74+
6475
private void registry(JsonRpcService service) {
6576
for (var method : service.getMethods()) {
6677
this.services.put(method, service);
@@ -121,20 +132,25 @@ private Object handle(Context ctx) {
121132
try {
122133
var targetService = services.get(fullMethod);
123134
if (targetService != null) {
124-
var result = targetService.execute(ctx, request);
135+
var result =
136+
invoker == null
137+
? targetService.execute(ctx, request)
138+
: invoker.invoke(ctx, request, () -> targetService.execute(ctx, request));
125139
// Spec: If the "id" is missing, it is a notification and no response is returned.
126140
if (request.getId() != null) {
127-
responses.add(JsonRpcResponse.success(request.getId(), result));
141+
if (result instanceof JsonRpcResponse jsonRpcResponse) {
142+
responses.add(jsonRpcResponse);
143+
} else {
144+
responses.add(JsonRpcResponse.success(request.getId(), result));
145+
}
128146
}
129147
} else {
130148
// Spec: -32601 Method not found
131-
if (request.getId() != null) {
132-
responses.add(
133-
JsonRpcResponse.error(
134-
request.getId(),
135-
JsonRpcErrorCode.METHOD_NOT_FOUND,
136-
"Method not found: " + fullMethod));
137-
}
149+
responses.add(
150+
JsonRpcResponse.error(
151+
request.getId(),
152+
JsonRpcErrorCode.METHOD_NOT_FOUND,
153+
"Method not found: " + fullMethod));
138154
}
139155
} catch (JsonRpcException cause) {
140156
log(ctx, request, cause);

modules/jooby-jsonrpc/src/main/java/io/jooby/jsonrpc/JsonRpcRequest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import java.util.Iterator;
1111
import java.util.List;
1212

13+
import org.jspecify.annotations.Nullable;
14+
1315
/**
1416
* Represents a JSON-RPC 2.0 Request object, and simultaneously acts as an iterable container for
1517
* batch requests.
@@ -48,7 +50,7 @@ public class JsonRpcRequest implements Iterable<JsonRpcRequest> {
4850
* An identifier established by the Client that MUST contain a String, Number, or NULL value if
4951
* included. If it is not included it is assumed to be a notification.
5052
*/
51-
private Object id;
53+
private @Nullable Object id;
5254

5355
// --- Batch State ---
5456
private boolean batch;
@@ -80,11 +82,11 @@ public void setParams(Object params) {
8082
this.params = params;
8183
}
8284

83-
public Object getId() {
85+
public @Nullable Object getId() {
8486
return id;
8587
}
8688

87-
public void setId(Object id) {
89+
public void setId(@Nullable Object id) {
8890
this.id = id;
8991
}
9092

modules/jooby-jsonrpc/src/main/java/io/jooby/jsonrpc/JsonRpcResponse.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
public class JsonRpcResponse {
1717

1818
private String jsonrpc = "2.0";
19-
private Object result;
20-
private ErrorDetail error;
21-
private Object id;
19+
private @Nullable Object result;
20+
private @Nullable ErrorDetail error;
21+
private @Nullable Object id;
2222

2323
public JsonRpcResponse() {}
2424

25-
private JsonRpcResponse(Object id, Object result, ErrorDetail error) {
25+
private JsonRpcResponse(
26+
@Nullable Object id, @Nullable Object result, @Nullable ErrorDetail error) {
2627
this.id = id;
2728
this.result = result;
2829
this.error = error;
@@ -47,7 +48,7 @@ public static JsonRpcResponse success(Object id, Object result) {
4748
* @param data Additional data about the error.
4849
* @return A populated JsonRpcResponse.
4950
*/
50-
public static JsonRpcResponse error(Object id, JsonRpcErrorCode code, Object data) {
51+
public static JsonRpcResponse error(@Nullable Object id, JsonRpcErrorCode code, Object data) {
5152
return new JsonRpcResponse(
5253
id, null, new ErrorDetail(code.getCode(), code.getMessage(), data(data)));
5354
}
@@ -67,27 +68,27 @@ public void setJsonrpc(String jsonrpc) {
6768
this.jsonrpc = jsonrpc;
6869
}
6970

70-
public Object getResult() {
71+
public @Nullable Object getResult() {
7172
return result;
7273
}
7374

74-
public void setResult(Object result) {
75+
public void setResult(@Nullable Object result) {
7576
this.result = result;
7677
}
7778

7879
public @Nullable ErrorDetail getError() {
7980
return error;
8081
}
8182

82-
public void setError(ErrorDetail error) {
83+
public void setError(@Nullable ErrorDetail error) {
8384
this.error = error;
8485
}
8586

8687
public @Nullable Object getId() {
8788
return id;
8889
}
8990

90-
public void setId(Object id) {
91+
public void setId(@Nullable Object id) {
9192
this.id = id;
9293
}
9394

modules/jooby-mcp/src/main/java/io/jooby/internal/mcp/DefaultMcpInvoker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package io.jooby.internal.mcp;
77

8+
import org.jspecify.annotations.NonNull;
89
import org.slf4j.LoggerFactory;
910

1011
import io.jooby.Jooby;
@@ -24,7 +25,7 @@ public DefaultMcpInvoker(Jooby application) {
2425

2526
@SuppressWarnings("unchecked")
2627
@Override
27-
public <R> R invoke(McpOperation operation, SneakyThrows.Supplier<R> action) {
28+
public <R> R invoke(@NonNull McpOperation operation, SneakyThrows.@NonNull Supplier<R> action) {
2829
try {
2930
return action.get();
3031
} catch (McpError mcpError) {

modules/jooby-mcp/src/main/java/io/jooby/mcp/McpInvoker.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
package io.jooby.mcp;
77

8+
import java.util.Objects;
9+
810
import io.jooby.SneakyThrows;
911

1012
/**
@@ -69,9 +71,7 @@ public interface McpInvoker {
6971
* @return A composed invoker.
7072
*/
7173
default McpInvoker then(McpInvoker next) {
72-
if (next == null) {
73-
return this;
74-
}
74+
Objects.requireNonNull(next, "next invoker is required");
7575
return new McpInvoker() {
7676
@Override
7777
public <R> R invoke(McpOperation operation, SneakyThrows.Supplier<R> action) {

0 commit comments

Comments
 (0)