Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

servlet: Implement gRPC server as a Servlet #4738

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a0c92df
POC: Implement gRPC server as a Servlet
dapengzhang0 Jul 31, 2018
8719377
better-logging
dapengzhang0 Aug 12, 2018
46d00ec
get rid of public ServerImpl
dapengzhang0 Aug 13, 2018
4fa6271
rename files
dapengzhang0 Aug 14, 2018
e793c7c
fix serverListener NPE
dapengzhang0 Aug 14, 2018
393fcf6
fix onSendingBytes()
dapengzhang0 Aug 15, 2018
706a3c9
favor ConcurrentLinkedQueue for library
dapengzhang0 Aug 16, 2018
5e11674
refactor ServletAdapterImpl to ServletAdapter
dapengzhang0 Aug 17, 2018
1776917
not to use CDI for simplicity
dapengzhang0 Aug 17, 2018
bb3a720
add GrpcServlet(ServletServerBuilder serverBuilder)
dapengzhang0 Oct 15, 2018
4c7cd4f
add UndertowInteropTest and UndertowAbstractTest
dapengzhang0 Nov 2, 2018
af7f6a3
fix codecov
dapengzhang0 Nov 15, 2018
2fc04fc
minor enhancements
dapengzhang0 Nov 16, 2018
6fb4fc2
Merge branch 'master' of https://github.com/grpc/grpc-java into servl…
dapengzhang0 Jan 2, 2019
8b78b6d
update example gradle files
dapengzhang0 Jan 3, 2019
680872b
Merge branch 'master' of https://github.com/grpc/grpc-java into servl…
dapengzhang0 Jan 7, 2019
b3473df
Merge branch 'master' of https://github.com/grpc/grpc-java into servl…
dapengzhang0 Jun 13, 2019
3728866
revert core/src/test/java/io/grpc/internal/AbstractTransportTest.java
dapengzhang0 Jun 13, 2019
58fce63
ignore some transport tests
dapengzhang0 Jun 13, 2019
5b02064
cleanup GrpcServlet constructor
dapengzhang0 Jun 13, 2019
d59f81e
better way to get authority
dapengzhang0 Jun 13, 2019
5b7496e
comment SPSC
dapengzhang0 Jul 5, 2019
e98e4f8
attributes.toBuilder()
dapengzhang0 Jul 5, 2019
6aa26e4
move writeState and WriteListener
dapengzhang0 Jul 5, 2019
c3dfaba
Merge branch 'master' of https://github.com/grpc/grpc-java into servl…
dapengzhang0 Jul 8, 2019
44ea5f3
revert AbstractTransportTest reformat
dapengzhang0 Jul 8, 2019
ef5f879
temp
dapengzhang0 Jul 14, 2019
47e3e95
fix some of review comments
dapengzhang0 Jul 17, 2019
c5fc2bd
create util methods
dapengzhang0 Aug 7, 2019
63e8cb2
minor cleanup
dapengzhang0 Aug 7, 2019
986a885
Merge branch 'master' of https://github.com/grpc/grpc-java into servl…
dapengzhang0 Aug 11, 2019
3fd4ca8
factor out write path so that easy to replace with other implementation
dapengzhang0 Aug 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ $ VERSION_FILES=(
examples/example-gauth/pom.xml
examples/example-kotlin/build.gradle
examples/example-kotlin/android/helloworld/app/build.gradle
examples/example-servlet/build.gradle
examples/example-tls/build.gradle
examples/example-tls/pom.xml
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ protected void setTracingEnabled(boolean value) {
}

@Override
public final Server build() {
public Server build() {
ServerImpl server = new ServerImpl(
this,
buildTransportServer(getTracerFactories()),
Expand Down
47 changes: 47 additions & 0 deletions examples/example-servlet/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
plugins {
// ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier gradle versions
id 'com.google.protobuf' version '0.8.5'
// Generate IntelliJ IDEA's .idea & .iml project files
id 'idea'
id 'war'
}

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

sourceCompatibility = 1.8
targetCompatibility = 1.8

def grpcVersion = '1.19.0-SNAPSHOT' // CURRENT_GRPC_VERSION
def protocVersion = '3.5.1-1'

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

dapengzhang0 marked this conversation as resolved.
Show resolved Hide resolved

providedCompile "javax.annotation:javax.annotation-api:1.2",
"javax.servlet:javax.servlet-api:4.0.1"
}

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

// 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'
}
}
}
8 changes: 8 additions & 0 deletions examples/example-servlet/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/repos/central/data/"
}
gradlePluginPortal()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.ServletAdapter;
import io.grpc.servlet.ServletServerBuilder;
import java.io.IOException;
import java.util.concurrent.Executors;
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 over HTTP/2 and shares the resource URI for the normal servlet
* clients over HTTP/1.0+.
*
* <p>For creating a servlet that solely serves gRPC services, do not follow this example, simply
* extend or register a {@link io.grpc.servlet.GrpcServlet} instead.
*/
@WebServlet(urlPatterns = {"/helloworld.Greeter/SayHello"}, asyncSupported = true)
public class HelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private final ServletAdapter servletAdapter = ServletAdapter.Factory.create(
new ServletServerBuilder().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();
}
}

@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>");
}
}

@Override
public void destroy() {
servletAdapter.destroy();
super.destroy();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2015 The gRPC Authors
dapengzhang0 marked this conversation as resolved.
Show resolved Hide resolved
//
// 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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Need this file for deployment to GlassFish -->
<!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="">
<class-loader delegate="false"/>
</glassfish-web-app>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Need this file for deployment to WildFly -->
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.jboss.com/xml/ns/javaee
http://www.jboss.org/j2ee/schema/jboss-web_5_1.xsd">
<context-root>/</context-root>
</jboss-web>
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,8 @@ public ServerStreamTracer newServerStreamTracer(String fullMethodName, Metadata

protected static final Empty EMPTY = Empty.getDefaultInstance();

private void startServer() {
AbstractServerImplBuilder<?> builder = getServerBuilder();
private void configBuilder(@Nullable AbstractServerImplBuilder<?> builder) {
if (builder == null) {
server = null;
return;
}
testServiceExecutor = Executors.newScheduledThreadPool(2);
Expand All @@ -229,6 +227,13 @@ private void startServer() {
serverStatsRecorder,
GrpcUtil.STOPWATCH_SUPPLIER,
true, true, true, false /* real-time metrics */));
}

protected void startServer(@Nullable AbstractServerImplBuilder<?> builder) {
if (builder == null) {
server = null;
return;
}
try {
server = builder.build().start();
} catch (IOException ex) {
Expand Down Expand Up @@ -280,7 +285,9 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
*/
@Before
public void setUp() {
startServer();
AbstractServerImplBuilder<?> builder = getServerBuilder();
configBuilder(builder);
startServer(builder);
channel = createChannel();

blockingStub =
Expand Down
40 changes: 40 additions & 0 deletions netty/src/test/java/io/grpc/netty/InternalNettyTestAccessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.netty;

import com.google.common.annotations.VisibleForTesting;
import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.TransportTracer;
import javax.annotation.CheckReturnValue;

/**
* Internal Accessor for tests.
*/
@VisibleForTesting
public final class InternalNettyTestAccessor {
dapengzhang0 marked this conversation as resolved.
Show resolved Hide resolved
private InternalNettyTestAccessor() {}

public static void setTransportTracerFactory(
NettyChannelBuilder builder, TransportTracer.Factory factory) {
builder.setTransportTracerFactory(factory);
}

@CheckReturnValue
public static ClientTransportFactory buildTransportFactory(NettyChannelBuilder builder) {
return builder.buildTransportFactory();
}
}
22 changes: 22 additions & 0 deletions servlet/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
description = "gRPC: Servlet"
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
compile project(':grpc-core')
compileOnly 'javax.servlet:javax.servlet-api:4.0.1',
libraries.javax_annotation // java 9, 10 needs it

testCompile project(':grpc-stub'),
project(':grpc-protobuf'),
project(':grpc-servlet'),
project(':grpc-netty'),
project(':grpc-testing'),
project(':grpc-auth'),
project(':grpc-interop-testing'),
project(':grpc-core').sourceSets.test.output,
project(':grpc-netty').sourceSets.test.output,
libraries.junit,
'io.undertow:undertow-servlet:2.0.15.Final',
'org.apache.tomcat.embed:tomcat-embed-core:9.0.12'
}
86 changes: 86 additions & 0 deletions servlet/src/main/java/io/grpc/servlet/GrpcServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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;

import com.google.common.annotations.VisibleForTesting;
import io.grpc.BindableService;
import java.io.IOException;
import java.util.List;
import java.util.function.Supplier;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A simple servlet backed by a gRPC server. Must set {@code asyncSupported} to true. The {@code
* /contextRoot/urlPattern} must match the gRPC services' path, which is
* "/full-service-name/short-method-name".
*
* <p>The API is unstable. The authors would like to know more about the real usecases. Users are
* welcome to provide feedback by commenting on
* <a href=https://github.com/grpc/grpc-java/issues/5066>the tracking issue</a>.
*/
@io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/5066")
public class GrpcServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private final ServletAdapter servletAdapter;

@VisibleForTesting
GrpcServlet(ServletAdapter servletAdapter) {
this.servletAdapter = servletAdapter;
}

/**
* Instantiate the servlet with the given serverBuilder.
*/
public GrpcServlet(ServletServerBuilder serverBuilder) {
this(ServletAdapter.Factory.create(serverBuilder));
}

/**
* Instantiate the servlet serving the given list of gRPC services.
*/
public GrpcServlet(List<BindableService> grpcServices) {
this(
((Supplier<ServletServerBuilder>)
() -> {
dapengzhang0 marked this conversation as resolved.
Show resolved Hide resolved
ServletServerBuilder serverBuilder = new ServletServerBuilder();
grpcServices.forEach(service -> serverBuilder.addService(service));
return serverBuilder;
})
.get());
}

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
servletAdapter.doGet(request, response);
}

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
servletAdapter.doPost(request, response);
}

@Override
public void destroy() {
servletAdapter.destroy();
super.destroy();
}
}