Skip to content

Commit 1beb1ea

Browse files
committed
POC: Implement gRPC server as a Servlet
1 parent 989bc87 commit 1beb1ea

File tree

19 files changed

+1420
-2
lines changed

19 files changed

+1420
-2
lines changed

core/src/main/java/io/grpc/internal/ServerImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import java.util.concurrent.TimeUnit;
6060
import java.util.logging.Level;
6161
import java.util.logging.Logger;
62+
import javax.annotation.Nullable;
6263
import javax.annotation.concurrent.GuardedBy;
6364

6465
/**
@@ -114,14 +115,16 @@ public final class ServerImpl extends io.grpc.Server implements Instrumented<Ser
114115
private final Channelz channelz;
115116
private final CallTracer serverCallTracer;
116117

118+
private ServerListener serverListener;
119+
117120
/**
118121
* Construct a server.
119122
*
120123
* @param builder builder with configuration for server
121124
* @param transportServer transport server that will create new incoming transports
122125
* @param rootContext context that callbacks for new RPCs should be derived from
123126
*/
124-
ServerImpl(
127+
public ServerImpl(
125128
AbstractServerImplBuilder<?> builder,
126129
InternalServer transportServer,
127130
Context rootContext) {
@@ -160,13 +163,19 @@ public ServerImpl start() throws IOException {
160163
checkState(!started, "Already started");
161164
checkState(!shutdown, "Shutting down");
162165
// Start and wait for any port to actually be bound.
163-
transportServer.start(new ServerListenerImpl());
166+
serverListener = new ServerListenerImpl();
167+
transportServer.start(serverListener);
164168
executor = Preconditions.checkNotNull(executorPool.getObject(), "executor");
165169
started = true;
166170
return this;
167171
}
168172
}
169173

174+
@Nullable
175+
public ServerListener getServerListener() {
176+
return serverListener;
177+
}
178+
170179
@Override
171180
public int getPort() {
172181
synchronized (lock) {

examples/example-servlet/build.gradle

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
description = "gRPC: Servlet example"
2+
buildscript {
3+
repositories {
4+
maven { // The google mirror is less flaky than mavenCentral()
5+
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }
6+
7+
}
8+
dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' }
9+
}
10+
11+
repositories {
12+
maven { // The google mirror is less flaky than mavenCentral()
13+
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }
14+
mavenLocal()
15+
}
16+
17+
apply plugin: 'war'
18+
19+
sourceCompatibility = 1.8
20+
targetCompatibility = 1.8
21+
22+
def grpcVersion = '1.15.0-SNAPSHOT' // CURRENT_GRPC_VERSION
23+
def protobufVersion = '3.5.1'
24+
def protocVersion = '3.5.1-1'
25+
26+
dependencies {
27+
compile "io.grpc:grpc-stub:${grpcVersion}"
28+
compile "io.grpc:grpc-protobuf:${grpcVersion}"
29+
compile "io.grpc:grpc-servlet:${grpcVersion}"
30+
31+
// for container that needs CDI 2
32+
compile "org.jboss.weld.servlet:weld-servlet-core:3.0.4.Final"
33+
34+
compile ("com.google.protobuf:protobuf-java-util:${protobufVersion}") {
35+
exclude group: 'com.google.guava', module: 'guava'
36+
}
37+
38+
providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
39+
// providedCompile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
40+
41+
// for container that needs CDI 2
42+
compile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
43+
}
44+
45+
apply plugin: 'com.google.protobuf'
46+
protobuf {
47+
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
48+
plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.13.1" } }
49+
generateProtoTasks {
50+
all()*.plugins { grpc {} }
51+
}
52+
}
53+
54+
apply plugin: 'idea'
55+
56+
// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
57+
sourceSets {
58+
main {
59+
java {
60+
srcDirs 'build/generated/source/proto/main/grpc'
61+
srcDirs 'build/generated/source/proto/main/java'
62+
}
63+
}
64+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2018 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.servlet.examples.helloworld;
18+
19+
import io.grpc.servlet.ServletAdapter;
20+
import java.io.IOException;
21+
import javax.inject.Inject;
22+
import javax.servlet.annotation.WebServlet;
23+
import javax.servlet.http.HttpServlet;
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
27+
/**
28+
* A servlet that hosts a gRPC server.
29+
*/
30+
@WebServlet(urlPatterns = {"/helloworld.Greeter/SayHello"}, asyncSupported = true)
31+
public class HelloWorldServlet extends HttpServlet {
32+
private static final long serialVersionUID = 1L;
33+
34+
@Inject
35+
private ServletAdapter servletAdapter;
36+
37+
@Override
38+
protected void doGet(HttpServletRequest request, HttpServletResponse response)
39+
throws IOException {
40+
response.setContentType("text/html");
41+
response.getWriter().println("<p>Hello World!</p>");
42+
}
43+
44+
@Override
45+
protected void doPost(HttpServletRequest request, HttpServletResponse response)
46+
throws IOException {
47+
if (ServletAdapter.isGrpc(request)) {
48+
servletAdapter.doPost(request, response);
49+
} else {
50+
response.setContentType("text/html");
51+
response.getWriter().println("<p>Hello non-gRPC client!</p>");
52+
}
53+
}
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2018 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.servlet.examples.helloworld;
18+
19+
import io.grpc.stub.StreamObserver;
20+
import io.grpc.examples.helloworld.GreeterGrpc;
21+
import io.grpc.examples.helloworld.HelloReply;
22+
import io.grpc.examples.helloworld.HelloRequest;
23+
import io.grpc.servlet.ServerBuilder;
24+
import io.grpc.servlet.ServletAdapter;
25+
import java.util.concurrent.Executors;
26+
import javax.enterprise.context.ApplicationScoped;
27+
import javax.enterprise.inject.Produces;
28+
29+
/**
30+
* A ManagedBean that produces an instance of ServletAdapter.
31+
*/
32+
@ApplicationScoped
33+
final class ServletAdapterProvider {
34+
@Produces
35+
private ServletAdapter getServletAdapter() {
36+
return Provider.servletAdapter;
37+
}
38+
39+
private static final class Provider {
40+
static final ServletAdapter servletAdapter = ServletAdapter.Factory.create(
41+
new ServerBuilder().addService(new GreeterImpl()));
42+
}
43+
44+
private static final class GreeterImpl extends GreeterGrpc.GreeterImplBase {
45+
GreeterImpl() {}
46+
47+
@Override
48+
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
49+
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
50+
responseObserver.onNext(reply);
51+
responseObserver.onCompleted();
52+
}
53+
}
54+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 The gRPC Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
syntax = "proto3";
15+
16+
option java_multiple_files = true;
17+
option java_package = "io.grpc.examples.helloworld";
18+
option java_outer_classname = "HelloWorldProto";
19+
option objc_class_prefix = "HLW";
20+
21+
package helloworld;
22+
23+
// The greeting service definition.
24+
service Greeter {
25+
// Sends a greeting
26+
rpc SayHello (HelloRequest) returns (HelloReply) {}
27+
}
28+
29+
// The request message containing the user's name.
30+
message HelloRequest {
31+
string name = 1;
32+
}
33+
34+
// The response message containing the greetings
35+
message HelloReply {
36+
string message = 1;
37+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- Jetty need this file to enable CDI -->
3+
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
6+
<scan></scan>
7+
</beans>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
3+
<glassfish-web-app error-url="">
4+
<context-root></context-root>
5+
<class-loader delegate="false"/>
6+
</glassfish-web-app>

servlet/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
description = "gRPC: Servlet"
2+
sourceCompatibility = 1.8
3+
targetCompatibility = 1.8
4+
5+
dependencies {
6+
compile project(':grpc-core')
7+
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
8+
compileOnly libraries.javax_annotation // java 9, 10 needs it
9+
}

servlet/interop-testing/build.gradle

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
description = "gRPC: Servlet testing"
2+
buildscript {
3+
repositories {
4+
maven { // The google mirror is less flaky than mavenCentral()
5+
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }
6+
7+
}
8+
dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' }
9+
}
10+
11+
sourceCompatibility = 1.8
12+
targetCompatibility = 1.8
13+
14+
def protobufVersion = '3.5.1'
15+
16+
apply plugin: 'war'
17+
18+
apply plugin: 'com.google.protobuf'
19+
protobuf {
20+
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
21+
plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.13.1" } }
22+
generateProtoTasks {
23+
all()*.plugins { grpc {} }
24+
}
25+
}
26+
27+
sourceSets {
28+
main {
29+
java {
30+
srcDirs "${rootDir}/interop-testing/src/main/java"
31+
// include only the necessary source files in interop-testing
32+
fileTree("${rootDir}/interop-testing/src/main/java")
33+
.exclude("io/grpc/testing/integration/Util.java")
34+
.exclude("io/grpc/testing/integration/TestServiceImpl.java")
35+
.each {
36+
exclude new File("${rootDir}/interop-testing/src/main/java").toPath()
37+
.relativize(it.toPath()).toString() }
38+
}
39+
40+
proto { srcDirs "${rootDir}/interop-testing/src/main/proto" }
41+
42+
resources { srcDirs "${rootDir}/interop-testing/src/main/resources" }
43+
}
44+
}
45+
46+
dependencies {
47+
compile project(':grpc-core')
48+
compile project(':grpc-stub')
49+
compile project(':grpc-protobuf')
50+
compile project(':grpc-servlet')
51+
52+
// for container that needs CDI 2
53+
compile "org.jboss.weld.servlet:weld-servlet-core:3.0.4.Final"
54+
55+
compile ("com.google.protobuf:protobuf-java-util:${protobufVersion}") {
56+
exclude group: 'com.google.guava', module: 'guava'
57+
}
58+
59+
providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
60+
// providedCompile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
61+
62+
// for container that needs CDI 2
63+
compile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
64+
65+
providedCompile "junit:junit:4.12"
66+
}
67+
68+
compileJava {
69+
options.compilerArgs += [
70+
// Protobuf-generated code produces some warnings.
71+
// https://github.com/google/protobuf/issues/2718
72+
"-Xlint:-cast",
73+
"-XepExcludedPaths:.*/generated/.*/java/.*",
74+
]
75+
}

0 commit comments

Comments
 (0)