mirror of https://github.com/grpc/grpc-java.git
Add native support for Protobuf Lite
Lite already worked by using the protobuf project, but would bring in extra dependencies that are not intended to work with lite. Although protobuf is not yet providing a lite package on Maven Central, we will be able to swap to it once it is available. There isn't any new original code in the Java portion, except for a new overload in ProtoUtils that accepts Message instead of MessageLite. Depending on Message in ProtoUtils allows us to support extra features out-of-the-box without any changes to the generated code. For example, JSON encoding could be supported in this way if Marshaller is enhanced. However, now codegen must be aware of Lite in order to choose with Util class to use. That is new code.
This commit is contained in:
parent
1ff6ecbb21
commit
99a6d8de27
|
@ -1,2 +1,3 @@
|
|||
TestService.java.txt binary
|
||||
TestServiceLite.java.txt binary
|
||||
TestServiceNano.java.txt binary
|
||||
|
|
|
@ -127,6 +127,11 @@ sourceSets {
|
|||
}
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
// Protobuf-generated Lite produces quite a few warnings.
|
||||
it.options.compilerArgs.removeAll(["-Xlint:unchecked", "-Xlint:rawtypes"])
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
if (project.hasProperty('protoc')) {
|
||||
|
@ -223,10 +228,11 @@ artifacts {
|
|||
}
|
||||
}
|
||||
|
||||
test.dependsOn('testGolden', 'testNanoGolden')
|
||||
test.dependsOn('testGolden', 'testLiteGolden', 'testNanoGolden')
|
||||
|
||||
def configureTestTask(Task task, String suffix, String extraPackage) {
|
||||
task.dependsOn "generateTest${suffix}Proto"
|
||||
def configureTestTask(Task task, String suffix, String dep,
|
||||
String extraPackage) {
|
||||
task.dependsOn "generateTest${dep}Proto"
|
||||
if (osdetector.os != 'windows') {
|
||||
task.executable "diff"
|
||||
} else {
|
||||
|
@ -234,11 +240,13 @@ def configureTestTask(Task task, String suffix, String extraPackage) {
|
|||
}
|
||||
// File isn't found on Windows if last slash is forward-slash
|
||||
def slash = System.getProperty("file.separator")
|
||||
task.args "$buildDir/generated/source/proto/test${suffix}/grpc/io/grpc/testing/integration${extraPackage}${slash}TestServiceGrpc.java",
|
||||
task.args "$buildDir/generated/source/proto/test${dep}/grpc/io/grpc/testing/integration${extraPackage}${slash}TestServiceGrpc.java",
|
||||
"$projectDir/src/test/golden/TestService${suffix}.java.txt"
|
||||
}
|
||||
|
||||
task testGolden(type: Exec)
|
||||
task testLiteGolden(type: Exec)
|
||||
task testNanoGolden(type: Exec)
|
||||
configureTestTask(testGolden, '', '')
|
||||
configureTestTask(testNanoGolden, 'Nano', '/nano')
|
||||
configureTestTask(testGolden, '', '', '')
|
||||
configureTestTask(testLiteGolden, 'Lite', '', '/lite')
|
||||
configureTestTask(testNanoGolden, 'Nano', 'Nano', '/nano')
|
||||
|
|
|
@ -83,7 +83,7 @@ static inline string MessageFullJavaName(bool nano, const Descriptor* desc) {
|
|||
|
||||
static void PrintMethodFields(
|
||||
const ServiceDescriptor* service, map<string, string>* vars, Printer* p,
|
||||
bool generate_nano) {
|
||||
ProtoFlavor flavor) {
|
||||
p->Print("// Static method descriptors that strictly reflect the proto.\n");
|
||||
(*vars)["service_name"] = service->name();
|
||||
for (int i = 0; i < service->method_count(); ++i) {
|
||||
|
@ -91,9 +91,9 @@ static void PrintMethodFields(
|
|||
(*vars)["arg_in_id"] = to_string(2 * i);
|
||||
(*vars)["arg_out_id"] = to_string(2 * i + 1);
|
||||
(*vars)["method_name"] = method->name();
|
||||
(*vars)["input_type"] = MessageFullJavaName(generate_nano,
|
||||
(*vars)["input_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
|
||||
method->input_type());
|
||||
(*vars)["output_type"] = MessageFullJavaName(generate_nano,
|
||||
(*vars)["output_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
|
||||
method->output_type());
|
||||
(*vars)["method_field_name"] = MethodPropertiesFieldName(method);
|
||||
bool client_streaming = method->client_streaming();
|
||||
|
@ -112,7 +112,7 @@ static void PrintMethodFields(
|
|||
}
|
||||
}
|
||||
|
||||
if (generate_nano) {
|
||||
if (flavor == ProtoFlavor::NANO) {
|
||||
// TODO(zsurocking): we're creating two NanoFactories for each method right now.
|
||||
// We could instead create static NanoFactories and reuse them if some methods
|
||||
// share the same request or response messages.
|
||||
|
@ -133,6 +133,11 @@ static void PrintMethodFields(
|
|||
" new NanoFactory<$output_type$>(ARG_OUT_$method_field_name$))\n"
|
||||
" );\n");
|
||||
} else {
|
||||
if (flavor == ProtoFlavor::LITE) {
|
||||
(*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils";
|
||||
} else {
|
||||
(*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
|
||||
}
|
||||
p->Print(
|
||||
*vars,
|
||||
"@$ExperimentalApi$\n"
|
||||
|
@ -148,7 +153,7 @@ static void PrintMethodFields(
|
|||
}
|
||||
p->Print("\n");
|
||||
|
||||
if (generate_nano) {
|
||||
if (flavor == ProtoFlavor::NANO) {
|
||||
p->Print(
|
||||
"private static final class NanoFactory<T extends com.google.protobuf.nano.MessageNano>\n"
|
||||
" implements io.grpc.protobuf.nano.MessageNanoFactory<T> {\n"
|
||||
|
@ -162,6 +167,7 @@ static void PrintMethodFields(
|
|||
" public T newInstance() {\n"
|
||||
" Object o;\n"
|
||||
" switch (id) {\n");
|
||||
bool generate_nano = true;
|
||||
for (int i = 0; i < service->method_count(); ++i) {
|
||||
const MethodDescriptor* method = service->method(i);
|
||||
(*vars)["input_type"] = MessageFullJavaName(generate_nano,
|
||||
|
@ -639,7 +645,7 @@ static void PrintBindServiceMethod(const ServiceDescriptor* service,
|
|||
static void PrintService(const ServiceDescriptor* service,
|
||||
map<string, string>* vars,
|
||||
Printer* p,
|
||||
bool generate_nano) {
|
||||
ProtoFlavor flavor) {
|
||||
(*vars)["service_name"] = service->name();
|
||||
(*vars)["file_name"] = service->file()->name();
|
||||
(*vars)["service_class_name"] = ServiceClassName(service);
|
||||
|
@ -659,7 +665,7 @@ static void PrintService(const ServiceDescriptor* service,
|
|||
"public static final String SERVICE_NAME = "
|
||||
"\"$Package$$service_name$\";\n\n");
|
||||
|
||||
PrintMethodFields(service, vars, p, generate_nano);
|
||||
PrintMethodFields(service, vars, p, flavor);
|
||||
|
||||
p->Print(
|
||||
*vars,
|
||||
|
@ -691,6 +697,7 @@ static void PrintService(const ServiceDescriptor* service,
|
|||
p->Outdent();
|
||||
p->Print("}\n\n");
|
||||
|
||||
bool generate_nano = flavor == ProtoFlavor::NANO;
|
||||
PrintStub(service, vars, p, ASYNC_INTERFACE, generate_nano);
|
||||
PrintStub(service, vars, p, BLOCKING_CLIENT_INTERFACE, generate_nano);
|
||||
PrintStub(service, vars, p, FUTURE_CLIENT_INTERFACE, generate_nano);
|
||||
|
@ -736,7 +743,7 @@ void PrintImports(Printer* p, bool generate_nano) {
|
|||
|
||||
void GenerateService(const ServiceDescriptor* service,
|
||||
google::protobuf::io::ZeroCopyOutputStream* out,
|
||||
bool generate_nano) {
|
||||
ProtoFlavor flavor) {
|
||||
// All non-generated classes must be referred by fully qualified names to
|
||||
// avoid collision with generated classes.
|
||||
map<string, string> vars;
|
||||
|
@ -753,7 +760,6 @@ void GenerateService(const ServiceDescriptor* service,
|
|||
vars["ImmutableList"] = "com.google.common.collect.ImmutableList";
|
||||
vars["Collection"] = "java.util.Collection";
|
||||
vars["MethodDescriptor"] = "io.grpc.MethodDescriptor";
|
||||
vars["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
|
||||
vars["NanoUtils"] = "io.grpc.protobuf.nano.NanoUtils";
|
||||
vars["StreamObserver"] = "io.grpc.stub.StreamObserver";
|
||||
vars["Iterator"] = "java.util.Iterator";
|
||||
|
@ -766,20 +772,21 @@ void GenerateService(const ServiceDescriptor* service,
|
|||
vars["ExperimentalApi"] = "io.grpc.ExperimentalApi";
|
||||
|
||||
Printer printer(out, '$');
|
||||
string package_name = ServiceJavaPackage(service->file(), generate_nano);
|
||||
string package_name = ServiceJavaPackage(service->file(),
|
||||
flavor == ProtoFlavor::NANO);
|
||||
if (!package_name.empty()) {
|
||||
printer.Print(
|
||||
"package $package_name$;\n\n",
|
||||
"package_name", package_name);
|
||||
}
|
||||
PrintImports(&printer, generate_nano);
|
||||
PrintImports(&printer, flavor == ProtoFlavor::NANO);
|
||||
|
||||
// Package string is used to fully qualify method names.
|
||||
vars["Package"] = service->file()->package();
|
||||
if (!vars["Package"].empty()) {
|
||||
vars["Package"].append(".");
|
||||
}
|
||||
PrintService(service, &vars, &printer, generate_nano);
|
||||
PrintService(service, &vars, &printer, flavor);
|
||||
}
|
||||
|
||||
string ServiceJavaPackage(const FileDescriptor* file, bool nano) {
|
||||
|
|
|
@ -38,6 +38,10 @@ using namespace std;
|
|||
|
||||
namespace java_grpc_generator {
|
||||
|
||||
enum ProtoFlavor {
|
||||
NORMAL, LITE, NANO
|
||||
};
|
||||
|
||||
// Returns the package name of the gRPC services defined in the given file.
|
||||
string ServiceJavaPackage(const google::protobuf::FileDescriptor* file, bool nano);
|
||||
|
||||
|
@ -48,7 +52,7 @@ string ServiceClassName(const google::protobuf::ServiceDescriptor* service);
|
|||
// Writes the generated service interface into the given ZeroCopyOutputStream
|
||||
void GenerateService(const google::protobuf::ServiceDescriptor* service,
|
||||
google::protobuf::io::ZeroCopyOutputStream* out,
|
||||
bool generate_nano);
|
||||
ProtoFlavor flavor);
|
||||
|
||||
} // namespace java_grpc_generator
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <google/protobuf/compiler/code_generator.h>
|
||||
#include <google/protobuf/compiler/plugin.h>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/descriptor.pb.h>
|
||||
#include <google/protobuf/io/zero_copy_stream.h>
|
||||
|
||||
static string JavaPackageToDir(const string& package_name) {
|
||||
|
@ -34,14 +35,20 @@ class JavaGrpcGenerator : public google::protobuf::compiler::CodeGenerator {
|
|||
vector<pair<string, string> > options;
|
||||
google::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
|
||||
|
||||
bool generate_nano = false;
|
||||
java_grpc_generator::ProtoFlavor flavor =
|
||||
java_grpc_generator::ProtoFlavor::NORMAL;
|
||||
if (file->options().optimize_for() ==
|
||||
google::protobuf::FileOptions::LITE_RUNTIME) {
|
||||
flavor = java_grpc_generator::ProtoFlavor::LITE;
|
||||
}
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
if (options[i].first == "nano" && options[i].second == "true") {
|
||||
generate_nano = true;
|
||||
flavor = java_grpc_generator::ProtoFlavor::NANO;
|
||||
}
|
||||
}
|
||||
|
||||
string package_name = java_grpc_generator::ServiceJavaPackage(file, generate_nano);
|
||||
string package_name = java_grpc_generator::ServiceJavaPackage(
|
||||
file, flavor == java_grpc_generator::ProtoFlavor::NANO);
|
||||
string package_filename = JavaPackageToDir(package_name);
|
||||
for (int i = 0; i < file->service_count(); ++i) {
|
||||
const google::protobuf::ServiceDescriptor* service = file->service(i);
|
||||
|
@ -49,7 +56,7 @@ class JavaGrpcGenerator : public google::protobuf::compiler::CodeGenerator {
|
|||
+ java_grpc_generator::ServiceClassName(service) + ".java";
|
||||
std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output(
|
||||
context->Open(filename));
|
||||
java_grpc_generator::GenerateService(service, output.get(), generate_nano);
|
||||
java_grpc_generator::GenerateService(service, output.get(), flavor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
package io.grpc.testing.integration.lite;
|
||||
|
||||
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
|
||||
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
|
||||
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
|
||||
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
|
||||
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
|
||||
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
|
||||
import static io.grpc.stub.ClientCalls.futureUnaryCall;
|
||||
import static io.grpc.MethodDescriptor.generateFullMethodName;
|
||||
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
|
||||
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
|
||||
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
|
||||
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
|
||||
|
||||
@javax.annotation.Generated(
|
||||
value = "by gRPC proto compiler",
|
||||
comments = "Source: test_lite.proto")
|
||||
public class TestServiceGrpc {
|
||||
|
||||
private TestServiceGrpc() {}
|
||||
|
||||
public static final String SERVICE_NAME = "grpc.testing.lite.TestService";
|
||||
|
||||
// Static method descriptors that strictly reflect the proto.
|
||||
@io.grpc.ExperimentalApi
|
||||
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.SimpleRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.SimpleResponse> METHOD_UNARY_CALL =
|
||||
io.grpc.MethodDescriptor.create(
|
||||
io.grpc.MethodDescriptor.MethodType.UNARY,
|
||||
generateFullMethodName(
|
||||
"grpc.testing.lite.TestService", "UnaryCall"),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.SimpleRequest.getDefaultInstance()),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.SimpleResponse.getDefaultInstance()));
|
||||
@io.grpc.ExperimentalApi
|
||||
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_STREAMING_OUTPUT_CALL =
|
||||
io.grpc.MethodDescriptor.create(
|
||||
io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING,
|
||||
generateFullMethodName(
|
||||
"grpc.testing.lite.TestService", "StreamingOutputCall"),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
|
||||
@io.grpc.ExperimentalApi
|
||||
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> METHOD_STREAMING_INPUT_CALL =
|
||||
io.grpc.MethodDescriptor.create(
|
||||
io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING,
|
||||
generateFullMethodName(
|
||||
"grpc.testing.lite.TestService", "StreamingInputCall"),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest.getDefaultInstance()),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse.getDefaultInstance()));
|
||||
@io.grpc.ExperimentalApi
|
||||
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_FULL_BIDI_CALL =
|
||||
io.grpc.MethodDescriptor.create(
|
||||
io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING,
|
||||
generateFullMethodName(
|
||||
"grpc.testing.lite.TestService", "FullBidiCall"),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
|
||||
@io.grpc.ExperimentalApi
|
||||
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_HALF_BIDI_CALL =
|
||||
io.grpc.MethodDescriptor.create(
|
||||
io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING,
|
||||
generateFullMethodName(
|
||||
"grpc.testing.lite.TestService", "HalfBidiCall"),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
|
||||
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
|
||||
|
||||
public static TestServiceStub newStub(io.grpc.Channel channel) {
|
||||
return new TestServiceStub(channel);
|
||||
}
|
||||
|
||||
public static TestServiceBlockingStub newBlockingStub(
|
||||
io.grpc.Channel channel) {
|
||||
return new TestServiceBlockingStub(channel);
|
||||
}
|
||||
|
||||
public static TestServiceFutureStub newFutureStub(
|
||||
io.grpc.Channel channel) {
|
||||
return new TestServiceFutureStub(channel);
|
||||
}
|
||||
|
||||
public static interface TestService {
|
||||
|
||||
public void unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request,
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse> responseObserver);
|
||||
|
||||
public void streamingOutputCall(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request,
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
|
||||
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest> streamingInputCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> responseObserver);
|
||||
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> fullBidiCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
|
||||
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> halfBidiCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
|
||||
}
|
||||
|
||||
public static interface TestServiceBlockingClient {
|
||||
|
||||
public io.grpc.testing.integration.lite.TestLite.SimpleResponse unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request);
|
||||
|
||||
public java.util.Iterator<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> streamingOutputCall(
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request);
|
||||
}
|
||||
|
||||
public static interface TestServiceFutureClient {
|
||||
|
||||
public com.google.common.util.concurrent.ListenableFuture<io.grpc.testing.integration.lite.TestLite.SimpleResponse> unaryCall(
|
||||
io.grpc.testing.integration.lite.TestLite.SimpleRequest request);
|
||||
}
|
||||
|
||||
public static class TestServiceStub extends io.grpc.stub.AbstractStub<TestServiceStub>
|
||||
implements TestService {
|
||||
private TestServiceStub(io.grpc.Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
private TestServiceStub(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
super(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected TestServiceStub build(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
return new TestServiceStub(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public void unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request,
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse> responseObserver) {
|
||||
asyncUnaryCall(
|
||||
getChannel().newCall(METHOD_UNARY_CALL, getCallOptions()), request, responseObserver);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public void streamingOutputCall(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request,
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
|
||||
asyncServerStreamingCall(
|
||||
getChannel().newCall(METHOD_STREAMING_OUTPUT_CALL, getCallOptions()), request, responseObserver);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest> streamingInputCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> responseObserver) {
|
||||
return asyncClientStreamingCall(
|
||||
getChannel().newCall(METHOD_STREAMING_INPUT_CALL, getCallOptions()), responseObserver);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> fullBidiCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
|
||||
return asyncBidiStreamingCall(
|
||||
getChannel().newCall(METHOD_FULL_BIDI_CALL, getCallOptions()), responseObserver);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> halfBidiCall(
|
||||
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
|
||||
return asyncBidiStreamingCall(
|
||||
getChannel().newCall(METHOD_HALF_BIDI_CALL, getCallOptions()), responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestServiceBlockingStub extends io.grpc.stub.AbstractStub<TestServiceBlockingStub>
|
||||
implements TestServiceBlockingClient {
|
||||
private TestServiceBlockingStub(io.grpc.Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
private TestServiceBlockingStub(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
super(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected TestServiceBlockingStub build(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
return new TestServiceBlockingStub(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public io.grpc.testing.integration.lite.TestLite.SimpleResponse unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request) {
|
||||
return blockingUnaryCall(
|
||||
getChannel(), METHOD_UNARY_CALL, getCallOptions(), request);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public java.util.Iterator<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> streamingOutputCall(
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request) {
|
||||
return blockingServerStreamingCall(
|
||||
getChannel(), METHOD_STREAMING_OUTPUT_CALL, getCallOptions(), request);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestServiceFutureStub extends io.grpc.stub.AbstractStub<TestServiceFutureStub>
|
||||
implements TestServiceFutureClient {
|
||||
private TestServiceFutureStub(io.grpc.Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
private TestServiceFutureStub(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
super(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected TestServiceFutureStub build(io.grpc.Channel channel,
|
||||
io.grpc.CallOptions callOptions) {
|
||||
return new TestServiceFutureStub(channel, callOptions);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.google.common.util.concurrent.ListenableFuture<io.grpc.testing.integration.lite.TestLite.SimpleResponse> unaryCall(
|
||||
io.grpc.testing.integration.lite.TestLite.SimpleRequest request) {
|
||||
return futureUnaryCall(
|
||||
getChannel().newCall(METHOD_UNARY_CALL, getCallOptions()), request);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int METHODID_UNARY_CALL = 0;
|
||||
private static final int METHODID_STREAMING_OUTPUT_CALL = 1;
|
||||
private static final int METHODID_STREAMING_INPUT_CALL = 2;
|
||||
private static final int METHODID_FULL_BIDI_CALL = 3;
|
||||
private static final int METHODID_HALF_BIDI_CALL = 4;
|
||||
|
||||
private static class MethodHandlers<Req, Resp> implements
|
||||
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
|
||||
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
|
||||
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
|
||||
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
|
||||
private final TestService serviceImpl;
|
||||
private final int methodId;
|
||||
|
||||
public MethodHandlers(TestService serviceImpl, int methodId) {
|
||||
this.serviceImpl = serviceImpl;
|
||||
this.methodId = methodId;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("unchecked")
|
||||
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
|
||||
switch (methodId) {
|
||||
case METHODID_UNARY_CALL:
|
||||
serviceImpl.unaryCall((io.grpc.testing.integration.lite.TestLite.SimpleRequest) request,
|
||||
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse>) responseObserver);
|
||||
break;
|
||||
case METHODID_STREAMING_OUTPUT_CALL:
|
||||
serviceImpl.streamingOutputCall((io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest) request,
|
||||
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("unchecked")
|
||||
public io.grpc.stub.StreamObserver<Req> invoke(
|
||||
io.grpc.stub.StreamObserver<Resp> responseObserver) {
|
||||
switch (methodId) {
|
||||
case METHODID_STREAMING_INPUT_CALL:
|
||||
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.streamingInputCall(
|
||||
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse>) responseObserver);
|
||||
case METHODID_FULL_BIDI_CALL:
|
||||
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.fullBidiCall(
|
||||
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
|
||||
case METHODID_HALF_BIDI_CALL:
|
||||
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.halfBidiCall(
|
||||
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static io.grpc.ServerServiceDefinition bindService(
|
||||
final TestService serviceImpl) {
|
||||
return io.grpc.ServerServiceDefinition.builder(SERVICE_NAME)
|
||||
.addMethod(
|
||||
METHOD_UNARY_CALL,
|
||||
asyncUnaryCall(
|
||||
new MethodHandlers<
|
||||
io.grpc.testing.integration.lite.TestLite.SimpleRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.SimpleResponse>(
|
||||
serviceImpl, METHODID_UNARY_CALL)))
|
||||
.addMethod(
|
||||
METHOD_STREAMING_OUTPUT_CALL,
|
||||
asyncServerStreamingCall(
|
||||
new MethodHandlers<
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
|
||||
serviceImpl, METHODID_STREAMING_OUTPUT_CALL)))
|
||||
.addMethod(
|
||||
METHOD_STREAMING_INPUT_CALL,
|
||||
asyncClientStreamingCall(
|
||||
new MethodHandlers<
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse>(
|
||||
serviceImpl, METHODID_STREAMING_INPUT_CALL)))
|
||||
.addMethod(
|
||||
METHOD_FULL_BIDI_CALL,
|
||||
asyncBidiStreamingCall(
|
||||
new MethodHandlers<
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
|
||||
serviceImpl, METHODID_FULL_BIDI_CALL)))
|
||||
.addMethod(
|
||||
METHOD_HALF_BIDI_CALL,
|
||||
asyncBidiStreamingCall(
|
||||
new MethodHandlers<
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
|
||||
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
|
||||
serviceImpl, METHODID_HALF_BIDI_CALL)))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// A simple service definition for testing the protoc plugin.
|
||||
syntax = "proto2";
|
||||
|
||||
package grpc.testing.lite;
|
||||
|
||||
option java_package = "io.grpc.testing.integration.lite";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message SimpleRequest {
|
||||
}
|
||||
|
||||
message SimpleResponse {
|
||||
}
|
||||
|
||||
message StreamingInputCallRequest {
|
||||
}
|
||||
|
||||
message StreamingInputCallResponse {
|
||||
}
|
||||
|
||||
message StreamingOutputCallRequest {
|
||||
}
|
||||
|
||||
message StreamingOutputCallResponse {
|
||||
}
|
||||
|
||||
service TestService {
|
||||
// One request followed by one response.
|
||||
// The server returns the client payload as-is.
|
||||
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
|
||||
|
||||
// One request followed by a sequence of responses (streamed download).
|
||||
// The server returns the payload with client desired type and sizes.
|
||||
rpc StreamingOutputCall(StreamingOutputCallRequest)
|
||||
returns (stream StreamingOutputCallResponse);
|
||||
|
||||
// A sequence of requests followed by one response (streamed upload).
|
||||
// The server returns the aggregated size of client payload as the result.
|
||||
rpc StreamingInputCall(stream StreamingInputCallRequest)
|
||||
returns (StreamingInputCallResponse);
|
||||
|
||||
// A sequence of requests with each request served by the server immediately.
|
||||
// As one request could lead to multiple responses, this interface
|
||||
// demonstrates the idea of full bidirectionality.
|
||||
rpc FullBidiCall(stream StreamingOutputCallRequest)
|
||||
returns (stream StreamingOutputCallResponse);
|
||||
|
||||
// A sequence of requests followed by a sequence of responses.
|
||||
// The server buffers all the client requests and then serves them in order. A
|
||||
// stream of responses are returned to the client when the server starts with
|
||||
// first request.
|
||||
rpc HalfBidiCall(stream StreamingOutputCallRequest)
|
||||
returns (stream StreamingOutputCallResponse);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
id "be.insaneprogramming.gradle.animalsniffer" version "1.4.0"
|
||||
}
|
||||
|
||||
description = 'gRPC: Protobuf Lite'
|
||||
|
||||
dependencies {
|
||||
compile project(':grpc-core'),
|
||||
libraries.protobuf,
|
||||
libraries.guava
|
||||
}
|
||||
|
||||
animalsniffer {
|
||||
signature = "org.codehaus.mojo.signature:java16:+@signature"
|
||||
}
|
|
@ -29,7 +29,7 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package io.grpc.protobuf;
|
||||
package io.grpc.protobuf.lite;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.protobuf.CodedOutputStream;
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright 2014, Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package io.grpc.protobuf.lite;
|
||||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.MessageLite;
|
||||
import com.google.protobuf.Parser;
|
||||
|
||||
import io.grpc.ExperimentalApi;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor.Marshaller;
|
||||
import io.grpc.Status;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Utility methods for using protobuf with grpc.
|
||||
*/
|
||||
@ExperimentalApi("Experimental until Lite is stable in protobuf")
|
||||
public class ProtoLiteUtils {
|
||||
|
||||
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
|
||||
public static <T extends MessageLite> Marshaller<T> marshaller(final T defaultInstance) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Parser<T> parser = (Parser<T>) defaultInstance.getParserForType();
|
||||
return new Marshaller<T>() {
|
||||
@Override
|
||||
public InputStream stream(T value) {
|
||||
return new ProtoInputStream(value, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parse(InputStream stream) {
|
||||
if (stream instanceof ProtoInputStream) {
|
||||
ProtoInputStream protoStream = (ProtoInputStream) stream;
|
||||
// Optimization for in-memory transport. Returning provided object is safe since protobufs
|
||||
// are immutable.
|
||||
//
|
||||
// However, we can't assume the types match, so we have to verify the parser matches.
|
||||
// Today the parser is always the same for a given proto, but that isn't guaranteed. Even
|
||||
// if not, using the same MethodDescriptor would ensure the parser matches and permit us
|
||||
// to enable this optimization.
|
||||
if (protoStream.parser() == parser) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T message = (T) ((ProtoInputStream) stream).message();
|
||||
return message;
|
||||
} catch (IllegalStateException ex) {
|
||||
// Stream must have been read from, which is a strange state. Since the point of this
|
||||
// optimization is to be transparent, instead of throwing an error we'll continue,
|
||||
// even though it seems likely there's a bug.
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return parseFrom(stream);
|
||||
} catch (InvalidProtocolBufferException ipbe) {
|
||||
throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence")
|
||||
.withCause(ipbe).asRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private T parseFrom(InputStream stream) throws InvalidProtocolBufferException {
|
||||
// Pre-create the CodedInputStream so that we can remove the size limit restriction
|
||||
// when parsing.
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(stream);
|
||||
codedInput.setSizeLimit(Integer.MAX_VALUE);
|
||||
|
||||
T message = parser.parseFrom(codedInput);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
e.setUnfinishedMessage(message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a metadata marshaller for a protobuf type.
|
||||
*/
|
||||
public static <T extends MessageLite> Metadata.BinaryMarshaller<T> metadataMarshaller(
|
||||
final T instance) {
|
||||
return new Metadata.BinaryMarshaller<T>() {
|
||||
@Override
|
||||
public byte[] toBytes(T value) {
|
||||
return value.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T parseBytes(byte[] serialized) {
|
||||
try {
|
||||
return (T) instance.getParserForType().parseFrom(serialized);
|
||||
} catch (InvalidProtocolBufferException ipbe) {
|
||||
throw new IllegalArgumentException(ipbe);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ProtoLiteUtils() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2015, Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package io.grpc.protobuf.lite;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Empty;
|
||||
import com.google.protobuf.Enum;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Type;
|
||||
|
||||
import io.grpc.Drainable;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor.Marshaller;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Unit tests for {@link ProtoLiteUtils}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class ProtoLiteUtilsTest {
|
||||
private Marshaller<Type> marshaller = ProtoLiteUtils.marshaller(Type.getDefaultInstance());
|
||||
private Type proto = Type.newBuilder().setName("name").build();
|
||||
|
||||
@Test
|
||||
public void testPassthrough() {
|
||||
assertSame(proto, marshaller.parse(marshaller.stream(proto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtrip() throws Exception {
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
|
||||
assertEquals(proto, marshaller.parse(is));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidatedMessage() throws Exception {
|
||||
InputStream is = marshaller.stream(proto);
|
||||
// Invalidates message, and drains all bytes
|
||||
ByteStreams.toByteArray(is);
|
||||
try {
|
||||
((ProtoInputStream) is).message();
|
||||
fail("Expected exception");
|
||||
} catch (IllegalStateException ex) {
|
||||
// expected
|
||||
}
|
||||
// Zero bytes is the default message
|
||||
assertEquals(Type.getDefaultInstance(), marshaller.parse(is));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInvalid() throws Exception {
|
||||
InputStream is = new ByteArrayInputStream(new byte[] {-127});
|
||||
try {
|
||||
marshaller.parse(is);
|
||||
fail("Expected exception");
|
||||
} catch (StatusRuntimeException ex) {
|
||||
assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode());
|
||||
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMismatch() throws Exception {
|
||||
Marshaller<Enum> enumMarshaller = ProtoLiteUtils.marshaller(Enum.getDefaultInstance());
|
||||
// Enum's name and Type's name are both strings with tag 1.
|
||||
Enum altProto = Enum.newBuilder().setName(proto.getName()).build();
|
||||
assertEquals(proto, marshaller.parse(enumMarshaller.stream(altProto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void marshallerShouldNotLimitProtoSize() throws Exception {
|
||||
// The default limit is 64MB. Using a larger proto to verify that the limit is not enforced.
|
||||
byte[] bigName = new byte[70 * 1024 * 1024];
|
||||
Arrays.fill(bigName, (byte) 32);
|
||||
|
||||
proto = Type.newBuilder().setNameBytes(ByteString.copyFrom(bigName)).build();
|
||||
|
||||
// Just perform a round trip to verify that it works.
|
||||
testRoundtrip();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable() throws Exception {
|
||||
InputStream is = marshaller.stream(proto);
|
||||
assertEquals(proto.getSerializedSize(), is.available());
|
||||
is.read();
|
||||
assertEquals(proto.getSerializedSize() - 1, is.available());
|
||||
while (is.read() != -1) {}
|
||||
assertEquals(-1, is.read());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws IOException {
|
||||
Marshaller<Empty> marshaller = ProtoLiteUtils.marshaller(Empty.getDefaultInstance());
|
||||
InputStream is = marshaller.stream(Empty.getDefaultInstance());
|
||||
assertEquals(0, is.available());
|
||||
byte[] b = new byte[10];
|
||||
assertEquals(-1, is.read(b));
|
||||
assertArrayEquals(new byte[10], b);
|
||||
// Do the same thing again, because the internal state may be different
|
||||
assertEquals(-1, is.read(b));
|
||||
assertArrayEquals(new byte[10], b);
|
||||
assertEquals(-1, is.read());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_all() throws Exception {
|
||||
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
|
||||
InputStream is = marshaller.stream(proto);
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int drained = d.drainTo(baos);
|
||||
assertEquals(baos.size(), drained);
|
||||
assertArrayEquals(golden, baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_partial() throws Exception {
|
||||
final byte[] golden;
|
||||
{
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is.read();
|
||||
golden = ByteStreams.toByteArray(is);
|
||||
}
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is.read();
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int drained = d.drainTo(baos);
|
||||
assertEquals(baos.size(), drained);
|
||||
assertArrayEquals(golden, baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_none() throws Exception {
|
||||
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
|
||||
InputStream is = marshaller.stream(proto);
|
||||
ByteStreams.toByteArray(is);
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
assertEquals(0, d.drainTo(baos));
|
||||
assertArrayEquals(new byte[0], baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataMarshaller_roundtrip() {
|
||||
Metadata.BinaryMarshaller<Type> metadataMarshaller =
|
||||
ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance());
|
||||
assertEquals(proto, metadataMarshaller.parseBytes(metadataMarshaller.toBytes(proto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataMarshaller_invalid() {
|
||||
Metadata.BinaryMarshaller<Type> metadataMarshaller =
|
||||
ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance());
|
||||
try {
|
||||
metadataMarshaller.parseBytes(new byte[] {-127});
|
||||
fail("Expected exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ description = 'gRPC: Protobuf'
|
|||
|
||||
dependencies {
|
||||
compile project(':grpc-core'),
|
||||
project(':grpc-protobuf-lite'),
|
||||
libraries.protobuf,
|
||||
libraries.guava,
|
||||
libraries.protobuf_util
|
||||
|
|
|
@ -31,13 +31,10 @@
|
|||
|
||||
package io.grpc.protobuf;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.Message.Builder;
|
||||
import com.google.protobuf.MessageLite;
|
||||
import com.google.protobuf.Parser;
|
||||
import com.google.protobuf.util.JsonFormat;
|
||||
import com.google.protobuf.util.JsonFormat.Printer;
|
||||
|
||||
|
@ -45,6 +42,7 @@ import io.grpc.ExperimentalApi;
|
|||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor.Marshaller;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.protobuf.lite.ProtoLiteUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -58,63 +56,19 @@ import java.nio.charset.Charset;
|
|||
*/
|
||||
public class ProtoUtils {
|
||||
|
||||
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
|
||||
/**
|
||||
* Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}.
|
||||
*
|
||||
* @deprecated Use ProtoLiteUtils.marshaller() or Message-based marshaller() instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T extends MessageLite> Marshaller<T> marshaller(final T defaultInstance) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Parser<T> parser = (Parser<T>) defaultInstance.getParserForType();
|
||||
return new Marshaller<T>() {
|
||||
@Override
|
||||
public InputStream stream(T value) {
|
||||
return new ProtoInputStream(value, parser);
|
||||
}
|
||||
return ProtoLiteUtils.marshaller(defaultInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parse(InputStream stream) {
|
||||
if (stream instanceof ProtoInputStream) {
|
||||
ProtoInputStream protoStream = (ProtoInputStream) stream;
|
||||
// Optimization for in-memory transport. Returning provided object is safe since protobufs
|
||||
// are immutable.
|
||||
//
|
||||
// However, we can't assume the types match, so we have to verify the parser matches.
|
||||
// Today the parser is always the same for a given proto, but that isn't guaranteed. Even
|
||||
// if not, using the same MethodDescriptor would ensure the parser matches and permit us
|
||||
// to enable this optimization.
|
||||
if (protoStream.parser() == parser) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T message = (T) ((ProtoInputStream) stream).message();
|
||||
return message;
|
||||
} catch (IllegalStateException ex) {
|
||||
// Stream must have been read from, which is a strange state. Since the point of this
|
||||
// optimization is to be transparent, instead of throwing an error we'll continue,
|
||||
// even though it seems likely there's a bug.
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return parseFrom(stream);
|
||||
} catch (InvalidProtocolBufferException ipbe) {
|
||||
throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence")
|
||||
.withCause(ipbe).asRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private T parseFrom(InputStream stream) throws InvalidProtocolBufferException {
|
||||
// Pre-create the CodedInputStream so that we can remove the size limit restriction
|
||||
// when parsing.
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(stream);
|
||||
codedInput.setSizeLimit(Integer.MAX_VALUE);
|
||||
|
||||
T message = parser.parseFrom(codedInput);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
e.setUnfinishedMessage(message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
|
||||
public static <T extends Message> Marshaller<T> marshaller(final T defaultInstance) {
|
||||
return ProtoLiteUtils.marshaller(defaultInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,27 +124,7 @@ public class ProtoUtils {
|
|||
public static <T extends Message> Metadata.Key<T> keyForProto(T instance) {
|
||||
return Metadata.Key.of(
|
||||
instance.getDescriptorForType().getFullName() + Metadata.BINARY_HEADER_SUFFIX,
|
||||
keyMarshaller(instance));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static <T extends MessageLite> Metadata.BinaryMarshaller<T> keyMarshaller(final T instance) {
|
||||
return new Metadata.BinaryMarshaller<T>() {
|
||||
@Override
|
||||
public byte[] toBytes(T value) {
|
||||
return value.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T parseBytes(byte[] serialized) {
|
||||
try {
|
||||
return (T) instance.getParserForType().parseFrom(serialized);
|
||||
} catch (InvalidProtocolBufferException ipbe) {
|
||||
throw new IllegalArgumentException(ipbe);
|
||||
}
|
||||
}
|
||||
};
|
||||
ProtoLiteUtils.metadataMarshaller(instance));
|
||||
}
|
||||
|
||||
private ProtoUtils() {
|
||||
|
|
|
@ -31,166 +31,42 @@
|
|||
|
||||
package io.grpc.protobuf;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Empty;
|
||||
import com.google.protobuf.Enum;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.MessageLite;
|
||||
import com.google.protobuf.Type;
|
||||
|
||||
import io.grpc.Drainable;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor.Marshaller;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Unit tests for {@link ProtoUtils}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class ProtoUtilsTest {
|
||||
private Marshaller<Type> marshaller = ProtoUtils.marshaller(Type.getDefaultInstance());
|
||||
private Type proto = Type.newBuilder().setName("name").build();
|
||||
|
||||
@Test
|
||||
public void testPassthrough() {
|
||||
assertSame(proto, marshaller.parse(marshaller.stream(proto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtrip() throws Exception {
|
||||
Marshaller<Type> marshaller = ProtoUtils.marshaller(Type.getDefaultInstance());
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
|
||||
assertEquals(proto, marshaller.parse(is));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidatedMessage() throws Exception {
|
||||
@Deprecated
|
||||
public void testRoundtripLite() throws Exception {
|
||||
Marshaller<MessageLite> marshaller
|
||||
= ProtoUtils.marshaller((MessageLite) Type.getDefaultInstance());
|
||||
InputStream is = marshaller.stream(proto);
|
||||
// Invalidates message, and drains all bytes
|
||||
ByteStreams.toByteArray(is);
|
||||
try {
|
||||
((ProtoInputStream) is).message();
|
||||
fail("Expected exception");
|
||||
} catch (IllegalStateException ex) {
|
||||
// expected
|
||||
}
|
||||
// Zero bytes is the default message
|
||||
assertEquals(Type.getDefaultInstance(), marshaller.parse(is));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInvalid() throws Exception {
|
||||
InputStream is = new ByteArrayInputStream(new byte[] {-127});
|
||||
try {
|
||||
marshaller.parse(is);
|
||||
fail("Expected exception");
|
||||
} catch (StatusRuntimeException ex) {
|
||||
assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode());
|
||||
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMismatch() throws Exception {
|
||||
Marshaller<Enum> enumMarshaller = ProtoUtils.marshaller(Enum.getDefaultInstance());
|
||||
// Enum's name and Type's name are both strings with tag 1.
|
||||
Enum altProto = Enum.newBuilder().setName(proto.getName()).build();
|
||||
assertEquals(proto, marshaller.parse(enumMarshaller.stream(altProto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void marshallerShouldNotLimitProtoSize() throws Exception {
|
||||
// The default limit is 64MB. Using a larger proto to verify that the limit is not enforced.
|
||||
byte[] bigName = new byte[70 * 1024 * 1024];
|
||||
Arrays.fill(bigName, (byte) 32);
|
||||
|
||||
proto = Type.newBuilder().setNameBytes(ByteString.copyFrom(bigName)).build();
|
||||
|
||||
// Just perform a round trip to verify that it works.
|
||||
testRoundtrip();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable() throws Exception {
|
||||
InputStream is = marshaller.stream(proto);
|
||||
assertEquals(proto.getSerializedSize(), is.available());
|
||||
is.read();
|
||||
assertEquals(proto.getSerializedSize() - 1, is.available());
|
||||
while (is.read() != -1) {}
|
||||
assertEquals(-1, is.read());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws IOException {
|
||||
Marshaller<Empty> marshaller = ProtoUtils.marshaller(Empty.getDefaultInstance());
|
||||
InputStream is = marshaller.stream(Empty.getDefaultInstance());
|
||||
assertEquals(0, is.available());
|
||||
byte[] b = new byte[10];
|
||||
assertEquals(-1, is.read(b));
|
||||
assertArrayEquals(new byte[10], b);
|
||||
// Do the same thing again, because the internal state may be different
|
||||
assertEquals(-1, is.read(b));
|
||||
assertArrayEquals(new byte[10], b);
|
||||
assertEquals(-1, is.read());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_all() throws Exception {
|
||||
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
|
||||
InputStream is = marshaller.stream(proto);
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int drained = d.drainTo(baos);
|
||||
assertEquals(baos.size(), drained);
|
||||
assertArrayEquals(golden, baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_partial() throws Exception {
|
||||
final byte[] golden;
|
||||
{
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is.read();
|
||||
golden = ByteStreams.toByteArray(is);
|
||||
}
|
||||
InputStream is = marshaller.stream(proto);
|
||||
is.read();
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int drained = d.drainTo(baos);
|
||||
assertEquals(baos.size(), drained);
|
||||
assertArrayEquals(golden, baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo_none() throws Exception {
|
||||
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
|
||||
InputStream is = marshaller.stream(proto);
|
||||
ByteStreams.toByteArray(is);
|
||||
Drainable d = (Drainable) is;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
assertEquals(0, d.drainTo(baos));
|
||||
assertArrayEquals(new byte[0], baos.toByteArray());
|
||||
assertEquals(0, is.available());
|
||||
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
|
||||
assertEquals(proto, marshaller.parse(is));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -198,25 +74,4 @@ public class ProtoUtilsTest {
|
|||
assertEquals("google.protobuf.Type-bin",
|
||||
ProtoUtils.keyForProto(Type.getDefaultInstance()).originalName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyMarshaller_roundtrip() {
|
||||
Metadata.BinaryMarshaller<Type> keyMarshaller =
|
||||
ProtoUtils.keyMarshaller(Type.getDefaultInstance());
|
||||
assertEquals(proto, keyMarshaller.parseBytes(keyMarshaller.toBytes(proto)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyMarshaller_invalid() {
|
||||
Metadata.BinaryMarshaller<Type> keyMarshaller =
|
||||
ProtoUtils.keyMarshaller(Type.getDefaultInstance());
|
||||
try {
|
||||
keyMarshaller.parseBytes(new byte[] {-127});
|
||||
fail("Expected exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ include ":grpc-stub"
|
|||
include ":grpc-auth"
|
||||
include ":grpc-okhttp"
|
||||
include ":grpc-protobuf"
|
||||
include ":grpc-protobuf-lite"
|
||||
include ":grpc-protobuf-nano"
|
||||
include ":grpc-netty"
|
||||
include ":grpc-grpclb"
|
||||
|
@ -18,6 +19,7 @@ project(':grpc-stub').projectDir = "$rootDir/stub" as File
|
|||
project(':grpc-auth').projectDir = "$rootDir/auth" as File
|
||||
project(':grpc-okhttp').projectDir = "$rootDir/okhttp" as File
|
||||
project(':grpc-protobuf').projectDir = "$rootDir/protobuf" as File
|
||||
project(':grpc-protobuf-lite').projectDir = "$rootDir/protobuf-lite" as File
|
||||
project(':grpc-protobuf-nano').projectDir = "$rootDir/protobuf-nano" as File
|
||||
project(':grpc-netty').projectDir = "$rootDir/netty" as File
|
||||
project(':grpc-grpclb').projectDir = "$rootDir/grpclb" as File
|
||||
|
|
Loading…
Reference in New Issue