Skip to content

Commit

Permalink
POC: Implement gRPC server as a Servlet
Browse files Browse the repository at this point in the history
  • Loading branch information
dapengzhang0 committed Aug 7, 2018
1 parent 989bc87 commit d967b77
Show file tree
Hide file tree
Showing 19 changed files with 1,424 additions and 2 deletions.
13 changes: 11 additions & 2 deletions core/src/main/java/io/grpc/internal/ServerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;

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

private ServerListener serverListener;

/**
* Construct a server.
*
* @param builder builder with configuration for server
* @param transportServer transport server that will create new incoming transports
* @param rootContext context that callbacks for new RPCs should be derived from
*/
ServerImpl(
public ServerImpl(
AbstractServerImplBuilder<?> builder,
InternalServer transportServer,
Context rootContext) {
Expand Down Expand Up @@ -160,13 +163,19 @@ public ServerImpl start() throws IOException {
checkState(!started, "Already started");
checkState(!shutdown, "Shutting down");
// Start and wait for any port to actually be bound.
transportServer.start(new ServerListenerImpl());
serverListener = new ServerListenerImpl();
transportServer.start(serverListener);
executor = Preconditions.checkNotNull(executorPool.getObject(), "executor");
started = true;
return this;
}
}

@Nullable
public ServerListener getServerListener() {
return serverListener;
}

@Override
public int getPort() {
synchronized (lock) {
Expand Down
64 changes: 64 additions & 0 deletions examples/example-servlet/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
description = "gRPC: Servlet example"
buildscript {
repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }

}
dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' }
}

repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }
mavenLocal()
}

apply plugin: 'war'

sourceCompatibility = 1.8
targetCompatibility = 1.8

def grpcVersion = '1.15.0-SNAPSHOT' // CURRENT_GRPC_VERSION
def protobufVersion = '3.5.1'
def protocVersion = '3.5.1-1'

dependencies {
compile "io.grpc:grpc-stub:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-servlet:${grpcVersion}"

// for container that needs CDI 2
compile "org.jboss.weld.servlet:weld-servlet-core:3.0.4.Final"

compile ("com.google.protobuf:protobuf-java-util:${protobufVersion}") {
exclude group: 'com.google.guava', module: 'guava'
}

providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
// providedCompile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'

// for container that needs CDI 2
compile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
}

apply plugin: 'com.google.protobuf'
protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.13.1" } }
generateProtoTasks {
all()*.plugins { grpc {} }
}
}

apply plugin: 'idea'

// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
sourceSets {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2018 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.servlet.examples.helloworld;

import io.grpc.servlet.ServletAdapter;
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A servlet that hosts a gRPC server.
*/
@WebServlet(urlPatterns = {"/helloworld.Greeter/SayHello"}, asyncSupported = true)
public class HelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

@Inject
private ServletAdapter servletAdapter;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/html");
response.getWriter().println("<p>Hello World!</p>");
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
if (ServletAdapter.isGrpc(request)) {
servletAdapter.doPost(request, response);
} else {
response.setContentType("text/html");
response.getWriter().println("<p>Hello non-gRPC client!</p>");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2018 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.servlet.examples.helloworld;

import io.grpc.stub.StreamObserver;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.servlet.ServerBuilder;
import io.grpc.servlet.ServletAdapter;
import java.util.concurrent.Executors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;

/**
* A ManagedBean that produces an instance of ServletAdapter.
*/
@ApplicationScoped
final class ServletAdapterProvider {
@Produces
private ServletAdapter getServletAdapter() {
return Provider.servletAdapter;
}

private static final class Provider {
static final ServletAdapter servletAdapter = ServletAdapter.Factory.create(
new ServerBuilder().addService(new GreeterImpl()));
}

private static final class GreeterImpl extends GreeterGrpc.GreeterImplBase {
GreeterImpl() {}

@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}
7 changes: 7 additions & 0 deletions examples/example-servlet/src/main/webapp/WEB-INF/beans.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Jetty need this file to enable CDI -->
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<scan></scan>
</beans>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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">
<glassfish-web-app error-url="">
<context-root></context-root>
<class-loader delegate="false"/>
</glassfish-web-app>
9 changes: 9 additions & 0 deletions servlet/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
description = "gRPC: Servlet"
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
compile project(':grpc-core')
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
compileOnly libraries.javax_annotation // java 9, 10 needs it
}
75 changes: 75 additions & 0 deletions servlet/interop-testing/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
description = "gRPC: Servlet testing"
buildscript {
repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/repos/central/data/" }

}
dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' }
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

def protobufVersion = '3.5.1'

apply plugin: 'war'

apply plugin: 'com.google.protobuf'
protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.13.1" } }
generateProtoTasks {
all()*.plugins { grpc {} }
}
}

sourceSets {
main {
java {
srcDirs "${rootDir}/interop-testing/src/main/java"
// include only the necessary source files in interop-testing
fileTree("${rootDir}/interop-testing/src/main/java")
.exclude("io/grpc/testing/integration/Util.java")
.exclude("io/grpc/testing/integration/TestServiceImpl.java")
.each {
exclude new File("${rootDir}/interop-testing/src/main/java").toPath()
.relativize(it.toPath()).toString() }
}

proto { srcDirs "${rootDir}/interop-testing/src/main/proto" }

resources { srcDirs "${rootDir}/interop-testing/src/main/resources" }
}
}

dependencies {
compile project(':grpc-core')
compile project(':grpc-stub')
compile project(':grpc-protobuf')
compile project(':grpc-servlet')

// for container that needs CDI 2
compile "org.jboss.weld.servlet:weld-servlet-core:3.0.4.Final"

compile ("com.google.protobuf:protobuf-java-util:${protobufVersion}") {
exclude group: 'com.google.guava', module: 'guava'
}

providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
// providedCompile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'

// for container that needs CDI 2
compile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'

providedCompile "junit:junit:4.12"
}

compileJava {
options.compilerArgs += [
// Protobuf-generated code produces some warnings.
// https://github.com/google/protobuf/issues/2718
"-Xlint:-cast",
"-XepExcludedPaths:.*/generated/.*/java/.*",
]
}

0 comments on commit d967b77

Please sign in to comment.