Skip to content

Commit ef7540f

Browse files
committed
- add asciidoc
1 parent 86f51c0 commit ef7540f

File tree

5 files changed

+127
-21
lines changed

5 files changed

+127
-21
lines changed

docs/asciidoc/gRPC.adoc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
== gRPC
2+
3+
The `jooby-grpc` module provides first-class, native support for https://grpc.io/[gRPC].
4+
5+
Unlike traditional setups that require spinning up a separate gRPC server on a different port (often forcing a specific transport like Netty), this module embeds the `grpc-java` engine directly into Jooby.
6+
7+
By using a custom native bridge, it allows you to run strictly-typed gRPC services alongside your standard REST API routes on the **exact same port**. It bypasses the standard HTTP/1.1 pipeline in favor of a highly optimized, native interceptor tailored for HTTP/2 multiplexing, reactive backpressure, and zero-copy byte framing. It works natively across Undertow, Netty, and Jetty.
8+
9+
=== Dependency
10+
11+
[source, xml, role="primary"]
12+
.Maven
13+
----
14+
<dependency>
15+
<groupId>io.jooby</groupId>
16+
<artifactId>jooby-grpc</artifactId>
17+
<version>${jooby.version}</version>
18+
</dependency>
19+
----
20+
21+
[source, gradle, role="secondary"]
22+
.Gradle
23+
----
24+
implementation 'io.jooby:jooby-grpc:${jooby.version}'
25+
----
26+
27+
=== Usage
28+
29+
gRPC strictly requires HTTP/2. Before installing the module, ensure your application is configured to use a supported server with HTTP/2 enabled.
30+
31+
[source, java]
32+
----
33+
import io.jooby.Jooby;
34+
import io.jooby.ServerOptions;
35+
import io.jooby.grpc.GrpcModule;
36+
37+
public class App extends Jooby {
38+
{
39+
setServerOptions(new ServerOptions().setHttp2(true)); // <1>
40+
41+
install(new GrpcModule( // <2>
42+
new GreeterService()
43+
));
44+
45+
get("/api/health", ctx -> "OK"); // <3>
46+
}
47+
48+
public static void main(String[] args) {
49+
runApp(args, App::new);
50+
}
51+
}
52+
----
53+
<1> Enable HTTP/2 on your server.
54+
<2> Install the module and explicitly register your services.
55+
<3> Standard REST routes still work on the exact same port!
56+
57+
=== Dependency Injection
58+
59+
If your gRPC services require external dependencies (like database repositories), you can register the service classes instead of pre-instantiated objects. The module will automatically provision them using your active Dependency Injection framework (e.g., Guice, Spring).
60+
61+
[source, java]
62+
----
63+
import io.jooby.Jooby;
64+
import io.jooby.di.GuiceModule;
65+
import io.jooby.grpc.GrpcModule;
66+
67+
public class App extends Jooby {
68+
{
69+
install(new GuiceModule());
70+
71+
install(new GrpcModule(
72+
GreeterService.class // <1>
73+
));
74+
}
75+
}
76+
----
77+
<1> Pass the class references. The DI framework will instantiate them.
78+
79+
WARNING: gRPC services are registered as **Singletons**. Ensure your service implementations are thread-safe and do not hold request-scoped state in instance variables. Heavy blocking operations will safely run on background workers, protecting the native server's I/O event loops.
80+
81+
=== Server Reflection
82+
83+
If you want to use tools like `grpcurl` or Postman to interact with your services without providing the `.proto` files, you can easily enable gRPC Server Reflection.
84+
85+
Include the `grpc-services` dependency in your build, and register the v1 reflection service alongside your own:
86+
87+
[source, java]
88+
----
89+
import io.grpc.protobuf.services.ProtoReflectionServiceV1;
90+
91+
public class App extends Jooby {
92+
{
93+
install(new GrpcModule(
94+
new GreeterService(),
95+
ProtoReflectionServiceV1.newInstance() // <1>
96+
));
97+
}
98+
}
99+
----
100+
<1> Enables the modern `v1` reflection protocol for maximum compatibility with gRPC clients.
101+
102+
=== Routing & Fallbacks
103+
104+
The gRPC module intercepts requests natively before they reach Jooby's standard router.
105+
106+
If a client attempts to call a gRPC method that does not exist, the request gracefully falls through to the standard Jooby router, returning a native `404 Not Found` (which gRPC clients will automatically translate to a Status `12 UNIMPLEMENTED`).
107+
108+
If you misconfigure your server (e.g., attempting to run gRPC over HTTP/1.1), the fallback route will catch the request and throw an `IllegalStateException` to help you identify the missing configuration immediately.

jooby/src/main/java/io/jooby/GrpcProcessor.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,21 @@
1313
/**
1414
* Core Service Provider Interface (SPI) for the gRPC extension.
1515
*
16-
* <p>This processor acts as the bridge between the native HTTP/2 web servers (Undertow, Netty,
17-
* Jetty) and the embedded gRPC engine. It is designed to intercept and process gRPC exchanges at
18-
* the lowest possible network level, completely bypassing Jooby's standard HTTP/1.1 routing
19-
* pipeline. This architecture ensures strict HTTP/2 specification compliance, zero-copy buffering,
20-
* and reactive backpressure.
16+
* <p>This processor acts as the bridge between the native HTTP/2 web servers and the embedded gRPC
17+
* engine. It is designed to intercept and process gRPC exchanges at the lowest possible network
18+
* level, completely bypassing Jooby's standard HTTP/1.1 routing pipeline. This architecture ensures
19+
* strict HTTP/2 specification compliance, zero-copy buffering, and reactive backpressure.
2120
*/
2221
public interface GrpcProcessor {
2322

2423
/**
2524
* Checks if the given URI path exactly matches a registered gRPC method.
2625
*
27-
* <p>Native server interceptors (Undertow, Netty, Jetty) use this method as a lightweight,
28-
* fail-fast guard. If this returns {@code true}, the server will hijack the request and upgrade
29-
* it to a native gRPC stream. If {@code false}, the request safely falls through to the standard
30-
* Jooby router (typically resulting in a standard HTTP 404 Not Found, which gRPC clients
31-
* gracefully translate to Status 12 UNIMPLEMENTED).
26+
* <p>Native server interceptors use this method as a lightweight, fail-fast guard. If this
27+
* returns {@code true}, the server will hijack the request and upgrade it to a native gRPC
28+
* stream. If {@code false}, the request safely falls through to the standard Jooby router
29+
* (typically resulting in a standard HTTP 404 Not Found, which gRPC clients gracefully translate
30+
* to Status 12 UNIMPLEMENTED).
3231
*
3332
* @param path The incoming request path (e.g., {@code /fully.qualified.Service/MethodName}).
3433
* @return {@code true} if the path is mapped to an active gRPC service; {@code false} otherwise.

jooby/src/main/java/io/jooby/ServerOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public int getPort() {
290290
* @return True when SSL is enabled. Either bc the secure port, httpsOnly or SSL options are set.
291291
*/
292292
public boolean isSSLEnabled() {
293-
return securePort != null || ssl != null || httpsOnly;
293+
return securePort != null || ssl != null || http2 == Boolean.TRUE || httpsOnly;
294294
}
295295

296296
/**

modules/jooby-grpc/src/main/java/io/jooby/grpc/GrpcModule.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
* <p>This module allows you to run strictly-typed gRPC services alongside standard Jooby HTTP
2424
* routes on the exact same port. It completely bypasses standard HTTP/1.1 pipelines in favor of a
2525
* highly optimized, reactive, native interceptor tailored for HTTP/2 multiplexing and trailing
26-
* headers. *
26+
* headers.
2727
*
2828
* <h3>Usage</h3>
2929
*
3030
* <p>gRPC requires HTTP/2. Ensure your Jooby application is configured to use a supported server
31-
* (Undertow, Netty, or Jetty) with HTTP/2 enabled. *
31+
* with HTTP/2 enabled.
3232
*
3333
* <pre>{@code
3434
* import io.jooby.Jooby;
@@ -43,13 +43,11 @@
4343
* }
4444
* }</pre>
4545
*
46-
* *
47-
*
4846
* <h3>Dependency Injection</h3>
4947
*
5048
* <p>If your gRPC services require external dependencies (like repositories or configuration), you
5149
* can register the service classes instead of instances. The module will automatically provision
52-
* them using Jooby's DI registry (e.g., Guice, Spring) during the application startup phase. *
50+
* them using Jooby's DI registry (e.g., Guice, Spring) during the application startup phase.
5351
*
5452
* <pre>{@code
5553
* public class App extends Jooby {
@@ -65,7 +63,7 @@
6563
*
6664
* <p><strong>Note:</strong> gRPC services are inherently registered as Singletons. Ensure your
6765
* service implementations are thread-safe and do not hold request-scoped state in instance
68-
* variables. *
66+
* variables.
6967
*
7068
* <h3>Logging</h3>
7169
*
@@ -159,8 +157,9 @@ public void install(@NonNull Jooby app) throws Exception {
159157

160158
/**
161159
* Internal helper to register a service with the gRPC builder, extract its method descriptors,
162-
* and map a fail-fast route in the Jooby router. * @param app The target Jooby application.
160+
* and map a fail-fast route in the Jooby router.
163161
*
162+
* @param app The target Jooby application.
164163
* @param server The in-process server builder.
165164
* @param registry The method descriptor registry.
166165
* @param service The provisioned gRPC service to bind.
@@ -183,11 +182,11 @@ private static void bindService(
183182
routePath,
184183
ctx -> {
185184
throw new IllegalStateException(
186-
"gRPC request reached the standard HTTP router for path: "
185+
"gRPC request reached the standard HTTP router for: "
187186
+ routePath
188187
+ ". "
189188
+ "This means the native gRPC server interceptor was bypassed. "
190-
+ "Ensure you are running Jetty, Netty, or Undertow with HTTP/2 enabled, "
189+
+ "Ensure you are running with HTTP/2 enabled, "
191190
+ "and that the GrpcProcessor SPI is correctly loaded.");
192191
});
193192
}

tests/src/test/java/io/jooby/test/Http2Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class Http2Test {
4242
@ServerTest
4343
public void h2body(ServerTestRunner runner) {
4444
runner
45-
.options(new ServerOptions().setHttp2(true).setSecurePort(8443))
45+
.options(new ServerOptions().setHttp2(true))
4646
.define(
4747
app -> {
4848
app.install(new JacksonModule());

0 commit comments

Comments
 (0)