diff --git a/examples/build.gradle b/examples/build.gradle index c41857e864..f71a8d7310 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -30,6 +30,8 @@ repositories { // updating the version in our release process. def grpcVersion = '1.13.0-SNAPSHOT' // CURRENT_GRPC_VERSION def nettyTcNativeVersion = '2.0.7.Final' +def protobufVersion = '3.5.1' +def protocVersion = '3.5.1-1' dependencies { compile "com.google.api.grpc:proto-google-common-protos:1.0.0" @@ -42,6 +44,8 @@ dependencies { // Used for TLS in HelloWorldServerTls compile "io.netty:netty-tcnative-boringssl-static:${nettyTcNativeVersion}" + compile "com.google.protobuf:protobuf-java-util:${protobufVersion}" + testCompile "io.grpc:grpc-testing:${grpcVersion}" testCompile "junit:junit:4.12" testCompile "org.mockito:mockito-core:1.9.5" @@ -49,7 +53,7 @@ dependencies { protobuf { protoc { - artifact = 'com.google.protobuf:protoc:3.5.1-1' + artifact = "com.google.protobuf:protoc:${protocVersion}" } plugins { grpc { diff --git a/examples/pom.xml b/examples/pom.xml index c060f27ff4..490685b368 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -12,6 +12,8 @@ UTF-8 1.13.0-SNAPSHOT + 3.5.1 + 3.5.1-1 2.0.7.Final @@ -51,6 +53,11 @@ proto-google-common-protos 1.0.0 + + com.google.protobuf + protobuf-java-util + ${protobuf.version} + junit junit @@ -78,7 +85,7 @@ protobuf-maven-plugin 0.5.1 - com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} diff --git a/examples/src/main/java/io/grpc/examples/advanced/HelloJsonClient.java b/examples/src/main/java/io/grpc/examples/advanced/HelloJsonClient.java index 815734fb68..0e163168af 100644 --- a/examples/src/main/java/io/grpc/examples/advanced/HelloJsonClient.java +++ b/examples/src/main/java/io/grpc/examples/advanced/HelloJsonClient.java @@ -28,7 +28,6 @@ import io.grpc.examples.helloworld.GreeterGrpc; import io.grpc.examples.helloworld.HelloReply; import io.grpc.examples.helloworld.HelloRequest; import io.grpc.examples.helloworld.HelloWorldClient; -import io.grpc.protobuf.ProtoUtils; import io.grpc.stub.AbstractStub; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -98,8 +97,8 @@ public final class HelloJsonClient { static final MethodDescriptor METHOD_SAY_HELLO = GreeterGrpc.getSayHelloMethod() .toBuilder( - ProtoUtils.jsonMarshaller(HelloRequest.getDefaultInstance()), - ProtoUtils.jsonMarshaller(HelloReply.getDefaultInstance())) + JsonMarshaller.jsonMarshaller(HelloRequest.getDefaultInstance()), + JsonMarshaller.jsonMarshaller(HelloReply.getDefaultInstance())) .build(); protected HelloJsonStub(Channel channel) { diff --git a/examples/src/main/java/io/grpc/examples/advanced/JsonMarshaller.java b/examples/src/main/java/io/grpc/examples/advanced/JsonMarshaller.java new file mode 100644 index 0000000000..7b9aae94f8 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/advanced/JsonMarshaller.java @@ -0,0 +1,99 @@ +/* + * 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.examples.advanced; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.Message.Builder; +import com.google.protobuf.util.JsonFormat; +import com.google.protobuf.util.JsonFormat.Parser; +import com.google.protobuf.util.JsonFormat.Printer; +import io.grpc.ExperimentalApi; +import io.grpc.MethodDescriptor.Marshaller; +import io.grpc.Status; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +/** + * A {@link Marshaller} for JSON. This marshals in the Protobuf 3 format described here: + * https://developers.google.com/protocol-buffers/docs/proto3#json + */ +final class JsonMarshaller { + + private JsonMarshaller() {} + + /** + * Create a {@code Marshaller} for json protos of the same type as {@code defaultInstance}. + * + *

This is an unstable API and has not been optimized yet for performance. + */ + public static Marshaller jsonMarshaller(final T defaultInstance) { + final Parser parser = JsonFormat.parser(); + final Printer printer = JsonFormat.printer(); + return jsonMarshaller(defaultInstance, parser, printer); + } + + /** + * Create a {@code Marshaller} for json protos of the same type as {@code defaultInstance}. + * + *

This is an unstable API and has not been optimized yet for performance. + */ + public static Marshaller jsonMarshaller( + final T defaultInstance, final Parser parser, final Printer printer) { + + final Charset charset = Charset.forName("UTF-8"); + + return new Marshaller() { + @Override + public InputStream stream(T value) { + try { + return new ByteArrayInputStream(printer.print(value).getBytes(charset)); + } catch (InvalidProtocolBufferException e) { + throw Status.INTERNAL + .withCause(e) + .withDescription("Unable to print json proto") + .asRuntimeException(); + } + } + + @SuppressWarnings("unchecked") + @Override + public T parse(InputStream stream) { + Builder builder = defaultInstance.newBuilderForType(); + Reader reader = new InputStreamReader(stream, charset); + T proto; + try { + parser.merge(reader, builder); + proto = (T) builder.build(); + reader.close(); + } catch (InvalidProtocolBufferException e) { + throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence") + .withCause(e).asRuntimeException(); + } catch (IOException e) { + // Same for now, might be unavailable + throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence") + .withCause(e).asRuntimeException(); + } + return proto; + } + }; + } +} diff --git a/grpclb/build.gradle b/grpclb/build.gradle index 95ca0a6e0a..63381e9589 100644 --- a/grpclb/build.gradle +++ b/grpclb/build.gradle @@ -17,6 +17,10 @@ dependencies { project(':grpc-protobuf'), project(':grpc-stub'), libraries.protobuf + compile (libraries.protobuf_util) { + // prefer 20.0 from libraries instead of 19.0 + exclude group: 'com.google.guava', module: 'guava' + } compileOnly libraries.javax_annotation testCompile libraries.truth, project(':grpc-core').sourceSets.test.output diff --git a/protobuf/build.gradle b/protobuf/build.gradle index 74bea7fb34..fb2e14c484 100644 --- a/protobuf/build.gradle +++ b/protobuf/build.gradle @@ -17,11 +17,6 @@ dependencies { libraries.protobuf, libraries.guava - compile (libraries.protobuf_util) { - // prefer 20.0 from libraries instead of 19.0 - exclude group: 'com.google.guava', module: 'guava' - } - compile (libraries.google_api_protos) { // 'com.google.api:api-common' transitively depends on auto-value, which breaks our // annotations. diff --git a/protobuf/src/main/java/io/grpc/protobuf/ProtoUtils.java b/protobuf/src/main/java/io/grpc/protobuf/ProtoUtils.java index c1ac29a352..00421de102 100644 --- a/protobuf/src/main/java/io/grpc/protobuf/ProtoUtils.java +++ b/protobuf/src/main/java/io/grpc/protobuf/ProtoUtils.java @@ -16,23 +16,10 @@ package io.grpc.protobuf; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; -import com.google.protobuf.Message.Builder; -import com.google.protobuf.util.JsonFormat; -import com.google.protobuf.util.JsonFormat.Parser; -import com.google.protobuf.util.JsonFormat.Printer; -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; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.Charset; /** * Utility methods for using protobuf with grpc. @@ -44,65 +31,6 @@ public class ProtoUtils { return ProtoLiteUtils.marshaller(defaultInstance); } - /** - * Create a {@code Marshaller} for json protos of the same type as {@code defaultInstance}. - * - *

This is an unstable API and has not been optimized yet for performance. - */ - @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1786") - public static Marshaller jsonMarshaller(final T defaultInstance) { - final Parser parser = JsonFormat.parser(); - final Printer printer = JsonFormat.printer(); - return jsonMarshaller(defaultInstance, parser, printer); - } - - /** - * Create a {@code Marshaller} for json protos of the same type as {@code defaultInstance}. - * - *

This is an unstable API and has not been optimized yet for performance. - */ - @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1786") - public static Marshaller jsonMarshaller( - final T defaultInstance, final Parser parser, final Printer printer) { - - final Charset charset = Charset.forName("UTF-8"); - - return new Marshaller() { - @Override - public InputStream stream(T value) { - try { - return new ByteArrayInputStream(printer.print(value).getBytes(charset)); - } catch (InvalidProtocolBufferException e) { - throw Status.INTERNAL - .withCause(e) - .withDescription("Unable to print json proto") - .asRuntimeException(); - } - } - - @SuppressWarnings("unchecked") - @Override - public T parse(InputStream stream) { - Builder builder = defaultInstance.newBuilderForType(); - Reader reader = new InputStreamReader(stream, charset); - T proto; - try { - parser.merge(reader, builder); - proto = (T) builder.build(); - reader.close(); - } catch (InvalidProtocolBufferException e) { - throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence") - .withCause(e).asRuntimeException(); - } catch (IOException e) { - // Same for now, might be unavailable - throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence") - .withCause(e).asRuntimeException(); - } - return proto; - } - }; - } - /** * Produce a metadata key for a generated protobuf type. */ diff --git a/protobuf/src/test/java/io/grpc/protobuf/ProtoUtilsTest.java b/protobuf/src/test/java/io/grpc/protobuf/ProtoUtilsTest.java index 20da6a0129..74707c9103 100644 --- a/protobuf/src/test/java/io/grpc/protobuf/ProtoUtilsTest.java +++ b/protobuf/src/test/java/io/grpc/protobuf/ProtoUtilsTest.java @@ -17,20 +17,12 @@ package io.grpc.protobuf; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; import com.google.common.io.ByteStreams; -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; import com.google.protobuf.Type; import io.grpc.MethodDescriptor.Marshaller; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -53,77 +45,4 @@ public class ProtoUtilsTest { assertEquals("google.protobuf.Type-bin", ProtoUtils.keyForProto(Type.getDefaultInstance()).originalName()); } - - @Test - public void testJsonRoundtrip() throws Exception { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Type.getDefaultInstance()); - InputStream is = marshaller.stream(proto); - is = new ByteArrayInputStream(ByteStreams.toByteArray(is)); - assertEquals(proto, marshaller.parse(is)); - } - - @Test - public void testJsonMarshallerFailToPrint() { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Any.getDefaultInstance()); - try { - marshaller.stream( - Any.newBuilder().setValue(ByteString.copyFromUtf8("invalid (no type url)")).build()); - fail("Expected exception"); - } catch (StatusRuntimeException e) { - assertNotNull(e.getCause()); - assertNotNull(e.getMessage()); - assertEquals(Status.Code.INTERNAL, e.getStatus().getCode()); - } - } - - @Test - public void testJsonRepresentation() throws Exception { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Type.getDefaultInstance()); - InputStream is = marshaller.stream(proto); - String s = new String(ByteStreams.toByteArray(is), "UTF-8"); - assertEquals("{\"name\":\"value\"}", s.replaceAll("\\s", "")); - } - - @Ignore("https://github.com/google/protobuf/issues/1470") - @Test - public void testJsonInvalid() throws Exception { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Type.getDefaultInstance()); - try { - marshaller.parse(new ByteArrayInputStream("{]".getBytes("UTF-8"))); - fail("Expected exception"); - } catch (StatusRuntimeException ex) { - assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode()); - assertNotNull(ex.getCause()); - } - } - - @Test - public void testJsonInvalidProto() throws Exception { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Type.getDefaultInstance()); - try { - marshaller.parse(new ByteArrayInputStream("{\"\":3}".getBytes("UTF-8"))); - fail("Expected exception"); - } catch (StatusRuntimeException ex) { - assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode()); - assertNotNull(ex.getCause()); - } - } - - @Test - public void testJsonIoException() throws Exception { - Marshaller marshaller = ProtoUtils.jsonMarshaller(Type.getDefaultInstance()); - final IOException ioe = new IOException(); - try { - marshaller.parse(new ByteArrayInputStream("{}".getBytes("UTF-8")) { - @Override - public void close() throws IOException { - throw ioe; - } - }); - fail("Exception expected"); - } catch (StatusRuntimeException ex) { - assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode()); - assertEquals(ioe, ex.getCause()); - } - } } diff --git a/services/build.gradle b/services/build.gradle index 3a2b52a57c..f9c34337d6 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -16,6 +16,11 @@ description = "gRPC: Services" dependencies { compile project(':grpc-protobuf'), project(':grpc-stub') + compile (libraries.protobuf_util) { + // prefer 20.0 from libraries instead of 19.0 + exclude group: 'com.google.guava', module: 'guava' + } + compileOnly libraries.javax_annotation testCompile project(':grpc-testing'), libraries.netty_epoll // for DomainSocketAddress