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.
diff --git a/compiler/build.gradle b/compiler/build.gradle
index a487992..740bf4b 100644
--- a/compiler/build.gradle
+++ b/compiler/build.gradle
@@ -127,6 +127,11 @@
   }
 }
 
+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 @@
   }
 }
 
-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 @@
   }
   // 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')
diff --git a/compiler/src/java_plugin/cpp/java_generator.cpp b/compiler/src/java_plugin/cpp/java_generator.cpp
index 30a6222..cf8cdbe 100644
--- a/compiler/src/java_plugin/cpp/java_generator.cpp
+++ b/compiler/src/java_plugin/cpp/java_generator.cpp
@@ -83,7 +83,7 @@
 
 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 @@
     (*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 @@
       }
     }
 
-    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 @@
           "            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 @@
   }
   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 @@
         "  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 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 @@
       "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 @@
   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 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 @@
   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 @@
   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) {
diff --git a/compiler/src/java_plugin/cpp/java_generator.h b/compiler/src/java_plugin/cpp/java_generator.h
index 96b3b8a..29c6fd9 100644
--- a/compiler/src/java_plugin/cpp/java_generator.h
+++ b/compiler/src/java_plugin/cpp/java_generator.h
@@ -38,6 +38,10 @@
 
 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 @@
 // 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
 
diff --git a/compiler/src/java_plugin/cpp/java_plugin.cpp b/compiler/src/java_plugin/cpp/java_plugin.cpp
index 8f9a2e8..1c95497 100644
--- a/compiler/src/java_plugin/cpp/java_plugin.cpp
+++ b/compiler/src/java_plugin/cpp/java_plugin.cpp
@@ -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 @@
     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 @@
           + 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;
   }
diff --git a/compiler/src/test/golden/TestServiceLite.java.txt b/compiler/src/test/golden/TestServiceLite.java.txt
new file mode 100644
index 0000000..86da7e7
--- /dev/null
+++ b/compiler/src/test/golden/TestServiceLite.java.txt
@@ -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();
+  }
+}
diff --git a/compiler/src/test/proto/test_lite.proto b/compiler/src/test/proto/test_lite.proto
new file mode 100644
index 0000000..0e42203
--- /dev/null
+++ b/compiler/src/test/proto/test_lite.proto
@@ -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);
+}