diff --git a/build.gradle b/build.gradle index 78913c1d617..82a0227c70a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7") classpath("org.asciidoctor:asciidoctor-gradle-plugin:0.7.0") classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.8") + classpath("ws.antonov.gradle.plugins:gradle-plugin-protobuf:0.9.1") } } @@ -37,6 +38,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.4" ext.tomcatVersion = "8.0.9" ext.xstreamVersion = "1.4.7" + ext.protobufVersion = "2.5.0" ext.gradleScriptDir = "${rootProject.projectDir}/gradle" @@ -611,6 +613,21 @@ project("spring-web") { description = "Spring Web" apply plugin: "groovy" + // Re-generate Protobuf classes from *.proto files and move them in test sources + if (project.hasProperty('genProtobuf')) { + apply plugin: 'protobuf' + + task updateGenProtobuf(type:Copy, dependsOn: ":spring-web:generateTestProto") { + from "${project.buildDir}/generated-sources/test/" + into "${projectDir}/src/test/java" + doLast { + project.delete "${project.buildDir}/generated-sources/test" + } + } + + tasks.getByPath("compileTestJava").dependsOn "updateGenProtobuf" + } + dependencies { compile(project(":spring-aop")) // for JaxWsPortProxyFactoryBean compile(project(":spring-beans")) // for MultiPartFilter @@ -638,6 +655,8 @@ project("spring-web") { exclude group: "javax.servlet", module: "javax.servlet-api" } optional("log4j:log4j:1.2.17") + optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2") + optional("com.google.protobuf:protobuf-java:${protobufVersion}") testCompile(project(":spring-context-support")) // for JafMediaTypeFactory testCompile("xmlunit:xmlunit:1.5") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ExtensionRegistryInitializer.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ExtensionRegistryInitializer.java new file mode 100644 index 00000000000..ac08b660ebf --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ExtensionRegistryInitializer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2014 the original author or 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 org.springframework.http.converter.protobuf; + +import com.google.protobuf.ExtensionRegistry; + +/** + * Google Protocol Messages can contain message extensions that can be parsed if + * the appropriate configuration has been registered in the {@code ExtensionRegistry}. + * + * This interface provides a facility to populate the {@code ExtensionRegistry}. + * + * @author Alex Antonov + * @since 4.1 + * @see + * com.google.protobuf.ExtensionRegistry + */ +public interface ExtensionRegistryInitializer { + + /** + * Initializes the {@code ExtensionRegistry} with Protocol Message extensions + * @param registry the registry to populate + */ + void initializeExtensionRegistry(ExtensionRegistry registry); + +} diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java new file mode 100644 index 00000000000..3379b13d253 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java @@ -0,0 +1,204 @@ +/* + * Copyright 2002-2014 the original author or 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 org.springframework.http.converter.protobuf; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.util.concurrent.ConcurrentHashMap; + +import com.google.protobuf.ExtensionRegistry; +import com.google.protobuf.Message; +import com.google.protobuf.TextFormat; +import com.googlecode.protobuf.format.HtmlFormat; +import com.googlecode.protobuf.format.JsonFormat; +import com.googlecode.protobuf.format.XmlFormat; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; + + +/** + * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} + * that can read and write Protobuf {@code Message}s using the + * Google Protocol buffers library. + * + *
By default, it supports {@code application/json}, {@code application/xml}, {@code text/plain} + * and {@code application/x-protobuf}. {@code text/html} is only supported when writing messages. + * + *
In order to generate Message Java classes, you should install the protoc binary on your system. + *
Tested against Protobuf version 2.5.0.
+ *
+ * @author Alex Antonov
+ * @author Brian Clozel
+ * @since 4.1
+ */
+public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter This method uses a ConcurrentHashMap for caching method lookups.
+ */
+ private Message.Builder createMessageBuilder(Class extends Message> clazz) throws Exception {
+ Method m = newBuilderMethodCache.get(clazz);
+ if (m == null) {
+ m = clazz.getMethod("newBuilder");
+ newBuilderMethodCache.put(clazz, m);
+ }
+ return (Message.Builder) m.invoke(clazz);
+ }
+
+ /**
+ * This method overrides the parent implementation, since this HttpMessageConverter
+ * can also produce {@code MediaType.HTML "text/html"} ContentType.
+ */
+ @Override
+ protected boolean canWrite(MediaType mediaType) {
+ return super.canWrite(mediaType) || MediaType.TEXT_HTML.isCompatibleWith(mediaType);
+ }
+
+ @Override
+ protected void writeInternal(Message message, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
+ MediaType contentType = outputMessage.getHeaders().getContentType();
+ Charset charset = getCharset(contentType);
+ OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), charset);
+
+ if (MediaType.TEXT_HTML.isCompatibleWith(contentType)) {
+ HtmlFormat.print(message, writer);
+ }
+ else if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
+ JsonFormat.print(message, writer);
+ }
+ else if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) {
+ TextFormat.print(message, writer);
+ }
+ else if (MediaType.APPLICATION_XML.isCompatibleWith(contentType)) {
+ XmlFormat.print(message, writer);
+ }
+ else if (PROTOBUF.isCompatibleWith(contentType)) {
+ setProtoHeader(outputMessage, message);
+ FileCopyUtils.copy(message.toByteArray(), outputMessage.getBody());
+ }
+ }
+
+ private Charset getCharset(MediaType contentType) {
+ return contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
+ }
+
+ /**
+ * Set the "X-Protobuf-*" HTTP headers when responding with
+ * a message of ContentType "application/x-protobuf"
+ */
+ private void setProtoHeader(final HttpOutputMessage response, final Message message) {
+ response.getHeaders().set(X_PROTOBUF_SCHEMA_HEADER,
+ message.getDescriptorForType().getFile().getName());
+
+ response.getHeaders().set(X_PROTOBUF_MESSAGE_HEADER,
+ message.getDescriptorForType().getFullName());
+ }
+
+ @Override
+ protected MediaType getDefaultContentType(Message message) {
+ return PROTOBUF;
+ }
+
+}
diff --git a/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java
new file mode 100644
index 00000000000..965b9d7467b
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002-2014 the original author or 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 org.springframework.http.converter.protobuf;
+
+import java.io.IOException;
+
+import com.google.protobuf.Message;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.MockHttpInputMessage;
+import org.springframework.http.MockHttpOutputMessage;
+import org.springframework.protobuf.Msg;
+import org.springframework.protobuf.SecondMsg;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Test suite for {@link ProtobufHttpMessageConverter}
+ * @author Alex Antonov
+ */
+public class ProtobufHttpMessageConverterTests {
+
+ private ProtobufHttpMessageConverter converter;
+
+ private ExtensionRegistryInitializer registryInitializer;
+
+ private Msg testMsg;
+
+ @Before
+ public void setUp() {
+ this.registryInitializer = mock(ExtensionRegistryInitializer.class);
+ this.converter = new ProtobufHttpMessageConverter(this.registryInitializer);
+ this.testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build();
+ }
+
+ @Test
+ public void extensionRegistryInitialized() {
+ verify(this.registryInitializer, times(1)).initializeExtensionRegistry(anyObject());
+ }
+
+ @Test
+ public void canRead() {
+ assertTrue(this.converter.canRead(Msg.class, null));
+ assertTrue(this.converter.canRead(Msg.class, ProtobufHttpMessageConverter.PROTOBUF));
+ assertTrue(this.converter.canRead(Msg.class, MediaType.APPLICATION_JSON));
+ assertTrue(this.converter.canRead(Msg.class, MediaType.APPLICATION_XML));
+ assertTrue(this.converter.canRead(Msg.class, MediaType.TEXT_PLAIN));
+ // only supported as an output format
+ assertFalse(this.converter.canRead(Msg.class, MediaType.TEXT_HTML));
+ }
+
+ @Test
+ public void canWrite() {
+ assertTrue(this.converter.canWrite(Msg.class, null));
+ assertTrue(this.converter.canWrite(Msg.class, ProtobufHttpMessageConverter.PROTOBUF));
+ assertTrue(this.converter.canWrite(Msg.class, MediaType.APPLICATION_JSON));
+ assertTrue(this.converter.canWrite(Msg.class, MediaType.APPLICATION_XML));
+ assertTrue(this.converter.canWrite(Msg.class, MediaType.TEXT_PLAIN));
+ assertTrue(this.converter.canWrite(Msg.class, MediaType.TEXT_HTML));
+ }
+
+ @Test
+ public void read() throws IOException {
+ byte[] body = this.testMsg.toByteArray();
+ MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
+ inputMessage.getHeaders().setContentType(ProtobufHttpMessageConverter.PROTOBUF);
+ Message result = this.converter.read(Msg.class, inputMessage);
+ assertEquals(this.testMsg, result);
+ }
+
+ @Test
+ public void readNoContentType() throws IOException {
+ byte[] body = this.testMsg.toByteArray();
+ MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
+ Message result = this.converter.read(Msg.class, inputMessage);
+ assertEquals(this.testMsg, result);
+ }
+
+ @Test
+ public void write() throws IOException {
+ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
+ MediaType contentType = ProtobufHttpMessageConverter.PROTOBUF;
+ this.converter.write(this.testMsg, contentType, outputMessage);
+ assertEquals(contentType, outputMessage.getHeaders().getContentType());
+ assertTrue(outputMessage.getBodyAsBytes().length > 0);
+ Message result = Msg.parseFrom(outputMessage.getBodyAsBytes());
+ assertEquals(this.testMsg, result);
+
+ String messageHeader = outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_MESSAGE_HEADER);
+ assertEquals("Msg", messageHeader);
+ String schemaHeader = outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_SCHEMA_HEADER);
+ assertEquals("sample.proto", schemaHeader);
+ }
+
+ @Test
+ public void defaultContentType() throws Exception {
+ assertEquals(ProtobufHttpMessageConverter.PROTOBUF, this.converter.getDefaultContentType(this.testMsg));
+ }
+
+ @Test
+ public void getContentLength() throws Exception {
+ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
+ MediaType contentType = ProtobufHttpMessageConverter.PROTOBUF;
+ this.converter.write(this.testMsg, contentType, outputMessage);
+ assertEquals(-1, outputMessage.getHeaders().getContentLength());
+ }
+}
diff --git a/spring-web/src/test/java/org/springframework/protobuf/Msg.java b/spring-web/src/test/java/org/springframework/protobuf/Msg.java
new file mode 100644
index 00000000000..b8d966600ff
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/protobuf/Msg.java
@@ -0,0 +1,636 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: sample.proto
+
+package org.springframework.protobuf;
+
+/**
+ * Protobuf type {@code Msg}
+ */
+public final class Msg extends
+ com.google.protobuf.GeneratedMessage
+ implements MsgOrBuilder {
+ // Use Msg.newBuilder() to construct.
+ private Msg(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private Msg(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final Msg defaultInstance;
+ public static Msg getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public Msg getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private Msg(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 10: {
+ bitField0_ |= 0x00000001;
+ foo_ = input.readBytes();
+ break;
+ }
+ case 18: {
+ org.springframework.protobuf.SecondMsg.Builder subBuilder = null;
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ subBuilder = blah_.toBuilder();
+ }
+ blah_ = input.readMessage(org.springframework.protobuf.SecondMsg.PARSER, extensionRegistry);
+ if (subBuilder != null) {
+ subBuilder.mergeFrom(blah_);
+ blah_ = subBuilder.buildPartial();
+ }
+ bitField0_ |= 0x00000002;
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.springframework.protobuf.OuterSample.internal_static_Msg_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.springframework.protobuf.OuterSample.internal_static_Msg_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.springframework.protobuf.Msg.class, org.springframework.protobuf.Msg.Builder.class);
+ }
+
+ public static com.google.protobuf.Parseroptional string foo = 1;
+ */
+ public boolean hasFoo() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public java.lang.String getFoo() {
+ java.lang.Object ref = foo_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs =
+ (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ if (bs.isValidUtf8()) {
+ foo_ = s;
+ }
+ return s;
+ }
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public com.google.protobuf.ByteString
+ getFooBytes() {
+ java.lang.Object ref = foo_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ foo_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ // optional .SecondMsg blah = 2;
+ public static final int BLAH_FIELD_NUMBER = 2;
+ private org.springframework.protobuf.SecondMsg blah_;
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public boolean hasBlah() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public org.springframework.protobuf.SecondMsg getBlah() {
+ return blah_;
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public org.springframework.protobuf.SecondMsgOrBuilder getBlahOrBuilder() {
+ return blah_;
+ }
+
+ private void initFields() {
+ foo_ = "";
+ blah_ = org.springframework.protobuf.SecondMsg.getDefaultInstance();
+ }
+ private byte memoizedIsInitialized = -1;
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized != -1) return isInitialized == 1;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ getSerializedSize();
+ if (((bitField0_ & 0x00000001) == 0x00000001)) {
+ output.writeBytes(1, getFooBytes());
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeMessage(2, blah_);
+ }
+ getUnknownFields().writeTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public int getSerializedSize() {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (((bitField0_ & 0x00000001) == 0x00000001)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(1, getFooBytes());
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeMessageSize(2, blah_);
+ }
+ size += getUnknownFields().getSerializedSize();
+ memoizedSerializedSize = size;
+ return size;
+ }
+
+ private static final long serialVersionUID = 0L;
+ @java.lang.Override
+ protected java.lang.Object writeReplace()
+ throws java.io.ObjectStreamException {
+ return super.writeReplace();
+ }
+
+ public static org.springframework.protobuf.Msg parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.springframework.protobuf.Msg parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.springframework.protobuf.Msg parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.springframework.protobuf.Msg parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.springframework.protobuf.Msg prototype) {
+ return newBuilder().mergeFrom(prototype);
+ }
+ public Builder toBuilder() { return newBuilder(this); }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code Msg}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builderoptional string foo = 1;
+ */
+ public boolean hasFoo() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public java.lang.String getFoo() {
+ java.lang.Object ref = foo_;
+ if (!(ref instanceof java.lang.String)) {
+ java.lang.String s = ((com.google.protobuf.ByteString) ref)
+ .toStringUtf8();
+ foo_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public com.google.protobuf.ByteString
+ getFooBytes() {
+ java.lang.Object ref = foo_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8(
+ (java.lang.String) ref);
+ foo_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public Builder setFoo(
+ java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ foo_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public Builder clearFoo() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ foo_ = getDefaultInstance().getFoo();
+ onChanged();
+ return this;
+ }
+ /**
+ * optional string foo = 1;
+ */
+ public Builder setFooBytes(
+ com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ foo_ = value;
+ onChanged();
+ return this;
+ }
+
+ // optional .SecondMsg blah = 2;
+ private org.springframework.protobuf.SecondMsg blah_ = org.springframework.protobuf.SecondMsg.getDefaultInstance();
+ private com.google.protobuf.SingleFieldBuilder<
+ org.springframework.protobuf.SecondMsg, org.springframework.protobuf.SecondMsg.Builder, org.springframework.protobuf.SecondMsgOrBuilder> blahBuilder_;
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public boolean hasBlah() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public org.springframework.protobuf.SecondMsg getBlah() {
+ if (blahBuilder_ == null) {
+ return blah_;
+ } else {
+ return blahBuilder_.getMessage();
+ }
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public Builder setBlah(org.springframework.protobuf.SecondMsg value) {
+ if (blahBuilder_ == null) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ blah_ = value;
+ onChanged();
+ } else {
+ blahBuilder_.setMessage(value);
+ }
+ bitField0_ |= 0x00000002;
+ return this;
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public Builder setBlah(
+ org.springframework.protobuf.SecondMsg.Builder builderForValue) {
+ if (blahBuilder_ == null) {
+ blah_ = builderForValue.build();
+ onChanged();
+ } else {
+ blahBuilder_.setMessage(builderForValue.build());
+ }
+ bitField0_ |= 0x00000002;
+ return this;
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public Builder mergeBlah(org.springframework.protobuf.SecondMsg value) {
+ if (blahBuilder_ == null) {
+ if (((bitField0_ & 0x00000002) == 0x00000002) &&
+ blah_ != org.springframework.protobuf.SecondMsg.getDefaultInstance()) {
+ blah_ =
+ org.springframework.protobuf.SecondMsg.newBuilder(blah_).mergeFrom(value).buildPartial();
+ } else {
+ blah_ = value;
+ }
+ onChanged();
+ } else {
+ blahBuilder_.mergeFrom(value);
+ }
+ bitField0_ |= 0x00000002;
+ return this;
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public Builder clearBlah() {
+ if (blahBuilder_ == null) {
+ blah_ = org.springframework.protobuf.SecondMsg.getDefaultInstance();
+ onChanged();
+ } else {
+ blahBuilder_.clear();
+ }
+ bitField0_ = (bitField0_ & ~0x00000002);
+ return this;
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public org.springframework.protobuf.SecondMsg.Builder getBlahBuilder() {
+ bitField0_ |= 0x00000002;
+ onChanged();
+ return getBlahFieldBuilder().getBuilder();
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ public org.springframework.protobuf.SecondMsgOrBuilder getBlahOrBuilder() {
+ if (blahBuilder_ != null) {
+ return blahBuilder_.getMessageOrBuilder();
+ } else {
+ return blah_;
+ }
+ }
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ private com.google.protobuf.SingleFieldBuilder<
+ org.springframework.protobuf.SecondMsg, org.springframework.protobuf.SecondMsg.Builder, org.springframework.protobuf.SecondMsgOrBuilder>
+ getBlahFieldBuilder() {
+ if (blahBuilder_ == null) {
+ blahBuilder_ = new com.google.protobuf.SingleFieldBuilder<
+ org.springframework.protobuf.SecondMsg, org.springframework.protobuf.SecondMsg.Builder, org.springframework.protobuf.SecondMsgOrBuilder>(
+ blah_,
+ getParentForChildren(),
+ isClean());
+ blah_ = null;
+ }
+ return blahBuilder_;
+ }
+
+ // @@protoc_insertion_point(builder_scope:Msg)
+ }
+
+ static {
+ defaultInstance = new Msg(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:Msg)
+}
+
diff --git a/spring-web/src/test/java/org/springframework/protobuf/MsgOrBuilder.java b/spring-web/src/test/java/org/springframework/protobuf/MsgOrBuilder.java
new file mode 100644
index 00000000000..7a25a3ad556
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/protobuf/MsgOrBuilder.java
@@ -0,0 +1,37 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: sample.proto
+
+package org.springframework.protobuf;
+
+public interface MsgOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional string foo = 1;
+ /**
+ * optional string foo = 1;
+ */
+ boolean hasFoo();
+ /**
+ * optional string foo = 1;
+ */
+ java.lang.String getFoo();
+ /**
+ * optional string foo = 1;
+ */
+ com.google.protobuf.ByteString
+ getFooBytes();
+
+ // optional .SecondMsg blah = 2;
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ boolean hasBlah();
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ org.springframework.protobuf.SecondMsg getBlah();
+ /**
+ * optional .SecondMsg blah = 2;
+ */
+ org.springframework.protobuf.SecondMsgOrBuilder getBlahOrBuilder();
+}
diff --git a/spring-web/src/test/java/org/springframework/protobuf/OuterSample.java b/spring-web/src/test/java/org/springframework/protobuf/OuterSample.java
new file mode 100644
index 00000000000..c952ef5a9fe
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/protobuf/OuterSample.java
@@ -0,0 +1,62 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: sample.proto
+
+package org.springframework.protobuf;
+
+public final class OuterSample {
+ private OuterSample() {}
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistry registry) {
+ }
+ static com.google.protobuf.Descriptors.Descriptor
+ internal_static_Msg_descriptor;
+ static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_Msg_fieldAccessorTable;
+ static com.google.protobuf.Descriptors.Descriptor
+ internal_static_SecondMsg_descriptor;
+ static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_SecondMsg_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor
+ getDescriptor() {
+ return descriptor;
+ }
+ private static com.google.protobuf.Descriptors.FileDescriptor
+ descriptor;
+ static {
+ java.lang.String[] descriptorData = {
+ "\n\014sample.proto\",\n\003Msg\022\013\n\003foo\030\001 \001(\t\022\030\n\004bl" +
+ "ah\030\002 \001(\0132\n.SecondMsg\"\031\n\tSecondMsg\022\014\n\004bla" +
+ "h\030\001 \001(\005B-\n\034org.springframework.protobufB" +
+ "\013OuterSampleP\001"
+ };
+ com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+ new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
+ public com.google.protobuf.ExtensionRegistry assignDescriptors(
+ com.google.protobuf.Descriptors.FileDescriptor root) {
+ descriptor = root;
+ internal_static_Msg_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_Msg_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_Msg_descriptor,
+ new java.lang.String[] { "Foo", "Blah", });
+ internal_static_SecondMsg_descriptor =
+ getDescriptor().getMessageTypes().get(1);
+ internal_static_SecondMsg_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_SecondMsg_descriptor,
+ new java.lang.String[] { "Blah", });
+ return null;
+ }
+ };
+ com.google.protobuf.Descriptors.FileDescriptor
+ .internalBuildGeneratedFileFrom(descriptorData,
+ new com.google.protobuf.Descriptors.FileDescriptor[] {
+ }, assigner);
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/spring-web/src/test/java/org/springframework/protobuf/SecondMsg.java b/spring-web/src/test/java/org/springframework/protobuf/SecondMsg.java
new file mode 100644
index 00000000000..9e3ec7639a3
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/protobuf/SecondMsg.java
@@ -0,0 +1,388 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: sample.proto
+
+package org.springframework.protobuf;
+
+/**
+ * Protobuf type {@code SecondMsg}
+ */
+public final class SecondMsg extends
+ com.google.protobuf.GeneratedMessage
+ implements SecondMsgOrBuilder {
+ // Use SecondMsg.newBuilder() to construct.
+ private SecondMsg(com.google.protobuf.GeneratedMessage.Builder> builder) {
+ super(builder);
+ this.unknownFields = builder.getUnknownFields();
+ }
+ private SecondMsg(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+ private static final SecondMsg defaultInstance;
+ public static SecondMsg getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public SecondMsg getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ private final com.google.protobuf.UnknownFieldSet unknownFields;
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private SecondMsg(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ initFields();
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ case 8: {
+ bitField0_ |= 0x00000001;
+ blah_ = input.readInt32();
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e.getMessage()).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.springframework.protobuf.OuterSample.internal_static_SecondMsg_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.springframework.protobuf.OuterSample.internal_static_SecondMsg_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ org.springframework.protobuf.SecondMsg.class, org.springframework.protobuf.SecondMsg.Builder.class);
+ }
+
+ public static com.google.protobuf.Parseroptional int32 blah = 1;
+ */
+ public boolean hasBlah() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional int32 blah = 1;
+ */
+ public int getBlah() {
+ return blah_;
+ }
+
+ private void initFields() {
+ blah_ = 0;
+ }
+ private byte memoizedIsInitialized = -1;
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized != -1) return isInitialized == 1;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ getSerializedSize();
+ if (((bitField0_ & 0x00000001) == 0x00000001)) {
+ output.writeInt32(1, blah_);
+ }
+ getUnknownFields().writeTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public int getSerializedSize() {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (((bitField0_ & 0x00000001) == 0x00000001)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeInt32Size(1, blah_);
+ }
+ size += getUnknownFields().getSerializedSize();
+ memoizedSerializedSize = size;
+ return size;
+ }
+
+ private static final long serialVersionUID = 0L;
+ @java.lang.Override
+ protected java.lang.Object writeReplace()
+ throws java.io.ObjectStreamException {
+ return super.writeReplace();
+ }
+
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+ public static org.springframework.protobuf.SecondMsg parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input);
+ }
+ public static org.springframework.protobuf.SecondMsg parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseDelimitedFrom(input, extensionRegistry);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input);
+ }
+ public static org.springframework.protobuf.SecondMsg parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return PARSER.parseFrom(input, extensionRegistry);
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.springframework.protobuf.SecondMsg prototype) {
+ return newBuilder().mergeFrom(prototype);
+ }
+ public Builder toBuilder() { return newBuilder(this); }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code SecondMsg}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builderoptional int32 blah = 1;
+ */
+ public boolean hasBlah() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ /**
+ * optional int32 blah = 1;
+ */
+ public int getBlah() {
+ return blah_;
+ }
+ /**
+ * optional int32 blah = 1;
+ */
+ public Builder setBlah(int value) {
+ bitField0_ |= 0x00000001;
+ blah_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional int32 blah = 1;
+ */
+ public Builder clearBlah() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ blah_ = 0;
+ onChanged();
+ return this;
+ }
+
+ // @@protoc_insertion_point(builder_scope:SecondMsg)
+ }
+
+ static {
+ defaultInstance = new SecondMsg(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:SecondMsg)
+}
+
diff --git a/spring-web/src/test/java/org/springframework/protobuf/SecondMsgOrBuilder.java b/spring-web/src/test/java/org/springframework/protobuf/SecondMsgOrBuilder.java
new file mode 100644
index 00000000000..baafb872ddf
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/protobuf/SecondMsgOrBuilder.java
@@ -0,0 +1,18 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: sample.proto
+
+package org.springframework.protobuf;
+
+public interface SecondMsgOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional int32 blah = 1;
+ /**
+ * optional int32 blah = 1;
+ */
+ boolean hasBlah();
+ /**
+ * optional int32 blah = 1;
+ */
+ int getBlah();
+}
diff --git a/spring-web/src/test/proto/sample.proto b/spring-web/src/test/proto/sample.proto
new file mode 100644
index 00000000000..812b4c2e4df
--- /dev/null
+++ b/spring-web/src/test/proto/sample.proto
@@ -0,0 +1,12 @@
+option java_package = "org.springframework.protobuf";
+option java_outer_classname = "OuterSample";
+option java_multiple_files = true;
+
+message Msg {
+ optional string foo = 1;
+ optional SecondMsg blah = 2;
+}
+
+message SecondMsg {
+ optional int32 blah = 1;
+}