Browse Source
This commit introduces Protobuf support in WebFlux via dedicated codecs. Flux<Message> are serialized/deserialized using delimited Protobuf messages with the size of each message specified before the message itself. In that case, a "delimited=true" parameter is added to the content type. Mono<Message> are expected to use regular Protobuf message format (without the size prepended before the message). Related HttpMessageReader/Writer are automatically registered when the "com.google.protobuf:protobuf-java" library is detected in the classpath, and can be customized easily if needed via CodecConfigurer, for example to specify protocol extensions via the ExtensionRegistry based constructors. Both "application/x-protobuf" and "application/octet-stream" mime types are supported. Issue: SPR-15776pull/1895/merge
23 changed files with 2225 additions and 18 deletions
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
/** |
||||
* Base class providing support methods for Protobuf encoding and decoding. |
||||
* |
||||
* @author Sebastien Deleuze |
||||
* @since 5.1 |
||||
*/ |
||||
public abstract class ProtobufCodecSupport { |
||||
|
||||
static final List<MimeType> MIME_TYPES = Collections.unmodifiableList( |
||||
Arrays.asList( |
||||
new MimeType("application", "x-protobuf"), |
||||
new MimeType("application", "octet-stream"))); |
||||
|
||||
static final String DELIMITED_KEY = "delimited"; |
||||
|
||||
static final String DELIMITED_VALUE = "true"; |
||||
|
||||
|
||||
protected boolean supportsMimeType(@Nullable MimeType mimeType) { |
||||
return (mimeType == null || MIME_TYPES.stream().anyMatch(m -> m.isCompatibleWith(mimeType))); |
||||
} |
||||
|
||||
protected List<MimeType> getMimeTypes() { |
||||
return MIME_TYPES; |
||||
} |
||||
} |
||||
@ -0,0 +1,212 @@
@@ -0,0 +1,212 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.io.IOException; |
||||
import java.lang.reflect.Method; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.function.Function; |
||||
|
||||
import com.google.protobuf.CodedInputStream; |
||||
import com.google.protobuf.ExtensionRegistry; |
||||
import com.google.protobuf.Message; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.codec.Decoder; |
||||
import org.springframework.core.codec.DecodingException; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.core.io.buffer.DataBufferUtils; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
/** |
||||
* A {@code Decoder} that reads {@link com.google.protobuf.Message}s |
||||
* using <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>. |
||||
* |
||||
* Flux deserialized via |
||||
* {@link #decode(Publisher, ResolvableType, MimeType, Map)} are expected to use |
||||
* <a href="https://developers.google.com/protocol-buffers/docs/techniques?hl=en#streaming">delimited Protobuf messages</a> |
||||
* with the size of each message specified before the message itself. Single values deserialized |
||||
* via {@link #decodeToMono(Publisher, ResolvableType, MimeType, Map)} are expected to use |
||||
* regular Protobuf message format (without the size prepended before the message). |
||||
* |
||||
* <p>To generate {@code Message} Java classes, you need to install the {@code protoc} binary. |
||||
* |
||||
* <p>This decoder requires Protobuf 3 or higher, and supports |
||||
* {@code "application/x-protobuf"} and {@code "application/octet-stream"} with the official |
||||
* {@code "com.google.protobuf:protobuf-java"} library. |
||||
* |
||||
* @author Sébastien Deleuze |
||||
* @since 5.1 |
||||
* @see ProtobufEncoder |
||||
*/ |
||||
public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Message> { |
||||
|
||||
/** |
||||
* The default max size for aggregating messages. |
||||
*/ |
||||
protected static final int DEFAULT_MESSAGE_MAX_SIZE = 64 * 1024; |
||||
|
||||
private static final ConcurrentHashMap<Class<?>, Method> methodCache = new ConcurrentHashMap<>(); |
||||
|
||||
private final ExtensionRegistry extensionRegistry; |
||||
|
||||
private int maxMessageSize = DEFAULT_MESSAGE_MAX_SIZE; |
||||
|
||||
|
||||
/** |
||||
* Construct a new {@code ProtobufDecoder}. |
||||
*/ |
||||
public ProtobufDecoder() { |
||||
this(ExtensionRegistry.newInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Construct a new {@code ProtobufDecoder} with an initializer that allows the |
||||
* registration of message extensions. |
||||
* @param extensionRegistry a message extension registry |
||||
*/ |
||||
public ProtobufDecoder(ExtensionRegistry extensionRegistry) { |
||||
Assert.notNull(extensionRegistry, "ExtensionRegistry must not be null"); |
||||
this.extensionRegistry = extensionRegistry; |
||||
} |
||||
|
||||
public void setMaxMessageSize(int maxMessageSize) { |
||||
this.maxMessageSize = maxMessageSize; |
||||
} |
||||
|
||||
@Override |
||||
public boolean canDecode(ResolvableType elementType, MimeType mimeType) { |
||||
return Message.class.isAssignableFrom(elementType.getRawClass()) && supportsMimeType(mimeType); |
||||
} |
||||
|
||||
@Override |
||||
public Flux<Message> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, |
||||
MimeType mimeType, Map<String, Object> hints) { |
||||
|
||||
return Flux.from(inputStream) |
||||
.concatMap(new MessageDecoderFunction(elementType, this.maxMessageSize)); |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Message> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType, |
||||
MimeType mimeType, Map<String, Object> hints) { |
||||
return DataBufferUtils.join(inputStream).map(dataBuffer -> { |
||||
try { |
||||
Message.Builder builder = getMessageBuilder(elementType.getRawClass()); |
||||
builder.mergeFrom(CodedInputStream.newInstance(dataBuffer.asByteBuffer()), this.extensionRegistry); |
||||
Message message = builder.build(); |
||||
DataBufferUtils.release(dataBuffer); |
||||
return message; |
||||
} |
||||
catch (IOException ex) { |
||||
throw new DecodingException("I/O error while parsing input stream", ex); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex); |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@code Message.Builder} instance for the given class. |
||||
* <p>This method uses a ConcurrentHashMap for caching method lookups. |
||||
*/ |
||||
private static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception { |
||||
Method method = methodCache.get(clazz); |
||||
if (method == null) { |
||||
method = clazz.getMethod("newBuilder"); |
||||
methodCache.put(clazz, method); |
||||
} |
||||
return (Message.Builder) method.invoke(clazz); |
||||
} |
||||
|
||||
@Override |
||||
public List<MimeType> getDecodableMimeTypes() { |
||||
return getMimeTypes(); |
||||
} |
||||
|
||||
|
||||
private class MessageDecoderFunction implements Function<DataBuffer, Publisher<? extends Message>> { |
||||
|
||||
private final ResolvableType elementType; |
||||
|
||||
private final int maxMessageSize; |
||||
|
||||
private DataBuffer output; |
||||
|
||||
private int messageBytesToRead; |
||||
|
||||
public MessageDecoderFunction(ResolvableType elementType, int maxMessageSize) { |
||||
this.elementType = elementType; |
||||
this.maxMessageSize = maxMessageSize; |
||||
} |
||||
|
||||
// TODO Instead of the recursive call, loop over the current DataBuffer, produce a list of as many messages as are contained, and save any remaining bytes with flatMapIterable
|
||||
@Override |
||||
public Publisher<? extends Message> apply(DataBuffer input) { |
||||
|
||||
try { |
||||
if (this.output == null) { |
||||
int firstByte = input.read(); |
||||
if (firstByte == -1) { |
||||
return Flux.error(new DecodingException("Can't parse message size")); |
||||
} |
||||
this.messageBytesToRead = CodedInputStream.readRawVarint32(firstByte, input.asInputStream()); |
||||
if (this.messageBytesToRead > this.maxMessageSize) { |
||||
return Flux.error(new DecodingException( |
||||
"The number of bytes to read parsed in the incoming stream (" + |
||||
this.messageBytesToRead + ") exceeds the configured limit (" + this.maxMessageSize + ")")); |
||||
} |
||||
this.output = input.factory().allocateBuffer(this.messageBytesToRead); |
||||
} |
||||
int chunkBytesToRead = this.messageBytesToRead >= input.readableByteCount() ? |
||||
input.readableByteCount() : this.messageBytesToRead; |
||||
int remainingBytesToRead = input.readableByteCount() - chunkBytesToRead; |
||||
this.output.write(input.slice(input.readPosition(), chunkBytesToRead)); |
||||
this.messageBytesToRead -= chunkBytesToRead; |
||||
Message message = null; |
||||
if (this.messageBytesToRead == 0) { |
||||
Message.Builder builder = getMessageBuilder(this.elementType.getRawClass()); |
||||
builder.mergeFrom(CodedInputStream.newInstance(this.output.asByteBuffer()), extensionRegistry); |
||||
message = builder.build(); |
||||
DataBufferUtils.release(this.output); |
||||
this.output = null; |
||||
} |
||||
if (remainingBytesToRead > 0) { |
||||
return Mono.justOrEmpty(message).concatWith( |
||||
apply(input.slice(input.readPosition() + chunkBytesToRead, remainingBytesToRead))); |
||||
} |
||||
else { |
||||
return Mono.justOrEmpty(message); |
||||
} |
||||
} |
||||
catch (IOException ex) { |
||||
return Flux.error(new DecodingException("I/O error while parsing input stream", ex)); |
||||
} |
||||
catch (Exception ex) { |
||||
return Flux.error(new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import com.google.protobuf.Message; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.core.io.buffer.DataBufferFactory; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.codec.HttpMessageEncoder; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
/** |
||||
* An {@code Encoder} that writes {@link com.google.protobuf.Message}s |
||||
* using <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>. |
||||
* |
||||
* Flux are serialized using |
||||
* <a href="https://developers.google.com/protocol-buffers/docs/techniques?hl=en#streaming">delimited Protobuf messages</a> |
||||
* with the size of each message specified before the message itself. Single values are |
||||
* serialized using regular Protobuf message format (without the size prepended before the message). |
||||
* |
||||
* <p>To generate {@code Message} Java classes, you need to install the {@code protoc} binary. |
||||
* |
||||
* <p>This encoder requires Protobuf 3 or higher, and supports |
||||
* {@code "application/x-protobuf"} and {@code "application/octet-stream"} with the official |
||||
* {@code "com.google.protobuf:protobuf-java"} library. |
||||
* |
||||
* @author Sébastien Deleuze |
||||
* @since 5.1 |
||||
* @see ProtobufDecoder |
||||
*/ |
||||
public class ProtobufEncoder extends ProtobufCodecSupport implements HttpMessageEncoder<Message> { |
||||
|
||||
private static final List<MediaType> streamingMediaTypes = MIME_TYPES |
||||
.stream() |
||||
.map(mimeType -> new MediaType(mimeType.getType(), mimeType.getSubtype(), Collections.singletonMap(DELIMITED_KEY, DELIMITED_VALUE))) |
||||
.collect(Collectors.toList()); |
||||
|
||||
@Override |
||||
public boolean canEncode(ResolvableType elementType, MimeType mimeType) { |
||||
return Message.class.isAssignableFrom(elementType.getRawClass()) && supportsMimeType(mimeType); |
||||
} |
||||
|
||||
@Override |
||||
public Flux<DataBuffer> encode(Publisher<? extends Message> inputStream, |
||||
DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) { |
||||
return Flux |
||||
.from(inputStream) |
||||
.map(message -> encodeMessage(message, bufferFactory, !(inputStream instanceof Mono))); |
||||
} |
||||
|
||||
private DataBuffer encodeMessage(Message message, DataBufferFactory bufferFactory, boolean streaming) { |
||||
DataBuffer buffer = bufferFactory.allocateBuffer(); |
||||
OutputStream outputStream = buffer.asOutputStream(); |
||||
try { |
||||
if (streaming) { |
||||
message.writeDelimitedTo(outputStream); |
||||
} |
||||
else { |
||||
message.writeTo(outputStream); |
||||
} |
||||
return buffer; |
||||
} |
||||
catch (IOException ex) { |
||||
throw new IllegalStateException("Unexpected I/O error while writing to data buffer", ex); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public List<MediaType> getStreamingMediaTypes() { |
||||
return streamingMediaTypes; |
||||
} |
||||
|
||||
@Override |
||||
public List<MimeType> getEncodableMimeTypes() { |
||||
return getMimeTypes(); |
||||
} |
||||
} |
||||
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import com.google.protobuf.Descriptors; |
||||
import com.google.protobuf.Message; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.codec.DecodingException; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ReactiveHttpOutputMessage; |
||||
import org.springframework.http.codec.EncoderHttpMessageWriter; |
||||
import org.springframework.http.codec.HttpMessageEncoder; |
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* {@code HttpMessageWriter} that can write a protobuf {@link Message} and adds |
||||
* {@code X-Protobuf-Schema}, {@code X-Protobuf-Message} headers and a |
||||
* {@code delimited=true} parameter is added to the content type if a flux is serialized. |
||||
* |
||||
* <p>For {@code HttpMessageReader}, just use |
||||
* {@code new DecoderHttpMessageReader(new ProtobufDecoder())}. |
||||
* |
||||
* @author Sébastien Deleuze |
||||
* @since 5.1 |
||||
* @see ProtobufEncoder |
||||
*/ |
||||
public class ProtobufHttpMessageWriter extends EncoderHttpMessageWriter<Message> { |
||||
|
||||
private static final ConcurrentHashMap<Class<?>, Method> methodCache = new ConcurrentHashMap<>(); |
||||
|
||||
private static final String X_PROTOBUF_SCHEMA_HEADER = "X-Protobuf-Schema"; |
||||
|
||||
private static final String X_PROTOBUF_MESSAGE_HEADER = "X-Protobuf-Message"; |
||||
|
||||
|
||||
public ProtobufHttpMessageWriter() { |
||||
super(new ProtobufEncoder()); |
||||
} |
||||
|
||||
public ProtobufHttpMessageWriter(ProtobufEncoder encoder) { |
||||
super(encoder); |
||||
} |
||||
|
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
public Mono<Void> write(Publisher<? extends Message> inputStream, ResolvableType elementType, |
||||
@Nullable MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints) { |
||||
|
||||
try { |
||||
Message.Builder builder = getMessageBuilder(elementType.getRawClass()); |
||||
Descriptors.Descriptor descriptor = builder.getDescriptorForType(); |
||||
message.getHeaders().add(X_PROTOBUF_SCHEMA_HEADER, descriptor.getFile().getName()); |
||||
message.getHeaders().add(X_PROTOBUF_MESSAGE_HEADER, descriptor.getFullName()); |
||||
if (inputStream instanceof Flux) { |
||||
if (mediaType == null) { |
||||
message.getHeaders().setContentType(((HttpMessageEncoder<?>)getEncoder()).getStreamingMediaTypes().get(0)); |
||||
} |
||||
else if (!ProtobufEncoder.DELIMITED_VALUE.equals(mediaType.getParameters().get(ProtobufEncoder.DELIMITED_KEY))) { |
||||
Map<String, String> parameters = new HashMap<>(mediaType.getParameters()); |
||||
parameters.put(ProtobufEncoder.DELIMITED_KEY, ProtobufEncoder.DELIMITED_VALUE); |
||||
message.getHeaders().setContentType(new MediaType(mediaType.getType(), mediaType.getSubtype(), parameters)); |
||||
} |
||||
} |
||||
return super.write(inputStream, elementType, mediaType, message, hints); |
||||
} |
||||
catch (Exception ex) { |
||||
return Mono.error(new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@code Message.Builder} instance for the given class. |
||||
* <p>This method uses a ConcurrentHashMap for caching method lookups. |
||||
*/ |
||||
private static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception { |
||||
Method method = methodCache.get(clazz); |
||||
if (method == null) { |
||||
method = clazz.getMethod("newBuilder"); |
||||
methodCache.put(clazz, method); |
||||
} |
||||
return (Message.Builder) method.invoke(clazz); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import com.google.protobuf.Message; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.test.StepVerifier; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.codec.DecodingException; |
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.core.io.buffer.DataBufferUtils; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.protobuf.Msg; |
||||
import org.springframework.protobuf.SecondMsg; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
import static java.util.Collections.emptyMap; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
import static org.springframework.core.ResolvableType.forClass; |
||||
|
||||
/** |
||||
* Unit tests for {@link ProtobufDecoder}. |
||||
* TODO Make tests more readable |
||||
* TODO Add a test where an input DataBuffer is larger than a message |
||||
* |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
public class ProtobufDecoderTests extends AbstractDataBufferAllocatingTestCase { |
||||
|
||||
private final static MimeType PROTOBUF_MIME_TYPE = new MimeType("application", "x-protobuf"); |
||||
|
||||
private final Msg testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build(); |
||||
|
||||
private ProtobufDecoder decoder; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.decoder = new ProtobufDecoder(); |
||||
} |
||||
|
||||
@Test(expected = IllegalArgumentException.class) |
||||
public void extensionRegistryNull() { |
||||
new ProtobufDecoder(null); |
||||
} |
||||
|
||||
@Test |
||||
public void canDecode() { |
||||
assertTrue(this.decoder.canDecode(forClass(Msg.class), null)); |
||||
assertTrue(this.decoder.canDecode(forClass(Msg.class), PROTOBUF_MIME_TYPE)); |
||||
assertTrue(this.decoder.canDecode(forClass(Msg.class), MediaType.APPLICATION_OCTET_STREAM)); |
||||
assertFalse(this.decoder.canDecode(forClass(Msg.class), MediaType.APPLICATION_JSON)); |
||||
assertFalse(this.decoder.canDecode(forClass(Object.class), PROTOBUF_MIME_TYPE)); |
||||
} |
||||
|
||||
@Test |
||||
public void decodeToMono() { |
||||
byte[] body = this.testMsg.toByteArray(); |
||||
Flux<DataBuffer> source = Flux.just(this.bufferFactory.wrap(body)); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Mono<Message> mono = this.decoder.decodeToMono(source, elementType, null, |
||||
emptyMap()); |
||||
|
||||
StepVerifier.create(mono) |
||||
.expectNext(this.testMsg) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void decodeChunksToMono() { |
||||
byte[] body = this.testMsg.toByteArray(); |
||||
List<DataBuffer> chunks = new ArrayList<>(); |
||||
chunks.add(this.bufferFactory.wrap(Arrays.copyOfRange(body, 0, 4))); |
||||
chunks.add(this.bufferFactory.wrap(Arrays.copyOfRange(body, 4, body.length))); |
||||
Flux<DataBuffer> source = Flux.fromIterable(chunks); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Mono<Message> mono = this.decoder.decodeToMono(source, elementType, null, |
||||
emptyMap()); |
||||
|
||||
StepVerifier.create(mono) |
||||
.expectNext(this.testMsg) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void decode() throws IOException { |
||||
Msg testMsg2 = Msg.newBuilder().setFoo("Bar").setBlah(SecondMsg.newBuilder().setBlah(456).build()).build(); |
||||
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(); |
||||
OutputStream outputStream = buffer.asOutputStream(); |
||||
this.testMsg.writeDelimitedTo(outputStream); |
||||
|
||||
DataBuffer buffer2 = bufferFactory.allocateBuffer(); |
||||
OutputStream outputStream2 = buffer2.asOutputStream(); |
||||
testMsg2.writeDelimitedTo(outputStream2); |
||||
|
||||
Flux<DataBuffer> source = Flux.just(buffer, buffer2); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Flux<Message> messages = this.decoder.decode(source, elementType, null, emptyMap()); |
||||
|
||||
StepVerifier.create(messages) |
||||
.expectNext(this.testMsg) |
||||
.expectNext(testMsg2) |
||||
.verifyComplete(); |
||||
|
||||
DataBufferUtils.release(buffer); |
||||
DataBufferUtils.release(buffer2); |
||||
} |
||||
|
||||
@Test |
||||
public void decodeChunks() throws IOException { |
||||
Msg testMsg2 = Msg.newBuilder().setFoo("Bar").setBlah(SecondMsg.newBuilder().setBlah(456).build()).build(); |
||||
List<DataBuffer> chunks = new ArrayList<>(); |
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
||||
this.testMsg.writeDelimitedTo(outputStream); |
||||
byte[] byteArray = outputStream.toByteArray(); |
||||
ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); |
||||
testMsg2.writeDelimitedTo(outputStream2); |
||||
byte[] byteArray2 = outputStream2.toByteArray(); |
||||
|
||||
chunks.add(this.bufferFactory.wrap(Arrays.copyOfRange(byteArray, 0, 4))); |
||||
byte[] chunk2 = Arrays.copyOfRange(byteArray, 4, byteArray.length); |
||||
byte[] chunk3 = Arrays.copyOfRange(byteArray2, 0, 4); |
||||
byte[] combined = new byte[chunk2.length + chunk3.length]; |
||||
for (int i = 0; i < combined.length; ++i) |
||||
{ |
||||
combined[i] = i < chunk2.length ? chunk2[i] : chunk3[i - chunk2.length]; |
||||
} |
||||
chunks.add(this.bufferFactory.wrap(combined)); |
||||
chunks.add(this.bufferFactory.wrap(Arrays.copyOfRange(byteArray2, 4, byteArray2.length))); |
||||
|
||||
Flux<DataBuffer> source = Flux.fromIterable(chunks); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Flux<Message> messages = this.decoder.decode(source, elementType, null, emptyMap()); |
||||
|
||||
StepVerifier.create(messages) |
||||
.expectNext(this.testMsg) |
||||
.expectNext(testMsg2) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void exceedMaxSize() { |
||||
this.decoder.setMaxMessageSize(1); |
||||
byte[] body = this.testMsg.toByteArray(); |
||||
Flux<DataBuffer> source = Flux.just(this.bufferFactory.wrap(body)); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Flux<Message> messages = this.decoder.decode(source, elementType, null, |
||||
emptyMap()); |
||||
|
||||
StepVerifier.create(messages) |
||||
.verifyError(DecodingException.class); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.codec.protobuf; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.UncheckedIOException; |
||||
|
||||
import com.google.protobuf.Message; |
||||
import org.junit.Test; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.test.StepVerifier; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.core.io.buffer.DataBufferUtils; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.protobuf.Msg; |
||||
import org.springframework.protobuf.SecondMsg; |
||||
import org.springframework.util.MimeType; |
||||
|
||||
import static java.util.Collections.emptyMap; |
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
import static org.springframework.core.ResolvableType.forClass; |
||||
|
||||
/** |
||||
* Unit tests for {@link ProtobufEncoder}. |
||||
* |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
public class ProtobufEncoderTests extends AbstractDataBufferAllocatingTestCase { |
||||
|
||||
private final static MimeType PROTOBUF_MIME_TYPE = new MimeType("application", "x-protobuf"); |
||||
|
||||
private final Msg testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build(); |
||||
|
||||
private final ProtobufEncoder encoder = new ProtobufEncoder(); |
||||
|
||||
@Test |
||||
public void canEncode() { |
||||
assertTrue(this.encoder.canEncode(forClass(Msg.class), null)); |
||||
assertTrue(this.encoder.canEncode(forClass(Msg.class), PROTOBUF_MIME_TYPE)); |
||||
assertTrue(this.encoder.canEncode(forClass(Msg.class), MediaType.APPLICATION_OCTET_STREAM)); |
||||
assertFalse(this.encoder.canEncode(forClass(Msg.class), MediaType.APPLICATION_JSON)); |
||||
assertFalse(this.encoder.canEncode(forClass(Object.class), PROTOBUF_MIME_TYPE)); |
||||
} |
||||
|
||||
@Test |
||||
public void encode() { |
||||
Mono<Message> message = Mono.just(this.testMsg); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Flux<DataBuffer> output = this.encoder.encode(message, this.bufferFactory, elementType, PROTOBUF_MIME_TYPE, emptyMap()); |
||||
StepVerifier.create(output) |
||||
.consumeNextWith(dataBuffer -> { |
||||
try { |
||||
assertEquals(this.testMsg, Msg.parseFrom(dataBuffer.asInputStream())); |
||||
DataBufferUtils.release(dataBuffer); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new UncheckedIOException(ex); |
||||
} |
||||
}) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void encodeStream() { |
||||
Msg testMsg2 = Msg.newBuilder().setFoo("Bar").setBlah(SecondMsg.newBuilder().setBlah(456).build()).build(); |
||||
Flux<Message> messages = Flux.just(this.testMsg, testMsg2); |
||||
ResolvableType elementType = forClass(Msg.class); |
||||
Flux<DataBuffer> output = this.encoder.encode(messages, this.bufferFactory, elementType, PROTOBUF_MIME_TYPE, emptyMap()); |
||||
StepVerifier.create(output) |
||||
.consumeNextWith(dataBuffer -> { |
||||
try { |
||||
assertEquals(this.testMsg, Msg.parseDelimitedFrom(dataBuffer.asInputStream())); |
||||
DataBufferUtils.release(dataBuffer); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new UncheckedIOException(ex); |
||||
} |
||||
}) |
||||
.consumeNextWith(dataBuffer -> { |
||||
try { |
||||
assertEquals(testMsg2, Msg.parseDelimitedFrom(dataBuffer.asInputStream())); |
||||
DataBufferUtils.release(dataBuffer); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new UncheckedIOException(ex); |
||||
} |
||||
}) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,654 @@
@@ -0,0 +1,654 @@
|
||||
/* |
||||
* Copyright 2002-2018 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. |
||||
*/ |
||||
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: sample.proto
|
||||
|
||||
package org.springframework.web.reactive.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; |
||||
@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(); |
||||
@SuppressWarnings("unused") |
||||
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: { |
||||
SecondMsg.Builder subBuilder = null; |
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) { |
||||
subBuilder = blah_.toBuilder(); |
||||
} |
||||
blah_ = input.readMessage(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 OuterSample.internal_static_Msg_descriptor; |
||||
} |
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable |
||||
internalGetFieldAccessorTable() { |
||||
return OuterSample.internal_static_Msg_fieldAccessorTable |
||||
.ensureFieldAccessorsInitialized( |
||||
Msg.class, Msg.Builder.class); |
||||
} |
||||
|
||||
public static com.google.protobuf.Parser<Msg> PARSER = |
||||
new com.google.protobuf.AbstractParser<Msg>() { |
||||
public Msg parsePartialFrom( |
||||
com.google.protobuf.CodedInputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return new Msg(input, extensionRegistry); |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
public com.google.protobuf.Parser<Msg> getParserForType() { |
||||
return PARSER; |
||||
} |
||||
|
||||
private int bitField0_; |
||||
// optional string foo = 1;
|
||||
public static final int FOO_FIELD_NUMBER = 1; |
||||
private Object foo_; |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public boolean hasFoo() { |
||||
return ((bitField0_ & 0x00000001) == 0x00000001); |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public String getFoo() { |
||||
Object ref = foo_; |
||||
if (ref instanceof String) { |
||||
return (String) ref; |
||||
} else { |
||||
com.google.protobuf.ByteString bs = |
||||
(com.google.protobuf.ByteString) ref; |
||||
String s = bs.toStringUtf8(); |
||||
if (bs.isValidUtf8()) { |
||||
foo_ = s; |
||||
} |
||||
return s; |
||||
} |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public com.google.protobuf.ByteString |
||||
getFooBytes() { |
||||
Object ref = foo_; |
||||
if (ref instanceof String) { |
||||
com.google.protobuf.ByteString b = |
||||
com.google.protobuf.ByteString.copyFromUtf8( |
||||
(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 SecondMsg blah_; |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public boolean hasBlah() { |
||||
return ((bitField0_ & 0x00000002) == 0x00000002); |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public SecondMsg getBlah() { |
||||
return blah_; |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public SecondMsgOrBuilder getBlahOrBuilder() { |
||||
return blah_; |
||||
} |
||||
|
||||
private void initFields() { |
||||
foo_ = ""; |
||||
blah_ = 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; |
||||
@Override |
||||
protected Object writeReplace() |
||||
throws java.io.ObjectStreamException { |
||||
return super.writeReplace(); |
||||
} |
||||
|
||||
public static Msg parseFrom( |
||||
com.google.protobuf.ByteString data) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data); |
||||
} |
||||
public static Msg parseFrom( |
||||
com.google.protobuf.ByteString data, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data, extensionRegistry); |
||||
} |
||||
public static Msg parseFrom(byte[] data) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data); |
||||
} |
||||
public static Msg parseFrom( |
||||
byte[] data, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data, extensionRegistry); |
||||
} |
||||
public static Msg parseFrom(java.io.InputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input); |
||||
} |
||||
public static Msg parseFrom( |
||||
java.io.InputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input, extensionRegistry); |
||||
} |
||||
public static Msg parseDelimitedFrom(java.io.InputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseDelimitedFrom(input); |
||||
} |
||||
public static Msg parseDelimitedFrom( |
||||
java.io.InputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
return PARSER.parseDelimitedFrom(input, extensionRegistry); |
||||
} |
||||
public static Msg parseFrom( |
||||
com.google.protobuf.CodedInputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input); |
||||
} |
||||
public static 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(Msg prototype) { |
||||
return newBuilder().mergeFrom(prototype); |
||||
} |
||||
public Builder toBuilder() { return newBuilder(this); } |
||||
|
||||
@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.Builder<Builder> |
||||
implements MsgOrBuilder { |
||||
public static final com.google.protobuf.Descriptors.Descriptor |
||||
getDescriptor() { |
||||
return OuterSample.internal_static_Msg_descriptor; |
||||
} |
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable |
||||
internalGetFieldAccessorTable() { |
||||
return OuterSample.internal_static_Msg_fieldAccessorTable |
||||
.ensureFieldAccessorsInitialized( |
||||
Msg.class, Msg.Builder.class); |
||||
} |
||||
|
||||
// Construct using org.springframework.protobuf.Msg.newBuilder()
|
||||
private Builder() { |
||||
maybeForceBuilderInitialization(); |
||||
} |
||||
|
||||
private Builder( |
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) { |
||||
super(parent); |
||||
maybeForceBuilderInitialization(); |
||||
} |
||||
private void maybeForceBuilderInitialization() { |
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { |
||||
getBlahFieldBuilder(); |
||||
} |
||||
} |
||||
private static Builder create() { |
||||
return new Builder(); |
||||
} |
||||
|
||||
public Builder clear() { |
||||
super.clear(); |
||||
foo_ = ""; |
||||
bitField0_ = (bitField0_ & ~0x00000001); |
||||
if (blahBuilder_ == null) { |
||||
blah_ = SecondMsg.getDefaultInstance(); |
||||
} else { |
||||
blahBuilder_.clear(); |
||||
} |
||||
bitField0_ = (bitField0_ & ~0x00000002); |
||||
return this; |
||||
} |
||||
|
||||
public Builder clone() { |
||||
return create().mergeFrom(buildPartial()); |
||||
} |
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor |
||||
getDescriptorForType() { |
||||
return OuterSample.internal_static_Msg_descriptor; |
||||
} |
||||
|
||||
public Msg getDefaultInstanceForType() { |
||||
return Msg.getDefaultInstance(); |
||||
} |
||||
|
||||
public Msg build() { |
||||
Msg result = buildPartial(); |
||||
if (!result.isInitialized()) { |
||||
throw newUninitializedMessageException(result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public Msg buildPartial() { |
||||
Msg result = new Msg(this); |
||||
int from_bitField0_ = bitField0_; |
||||
int to_bitField0_ = 0; |
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) { |
||||
to_bitField0_ |= 0x00000001; |
||||
} |
||||
result.foo_ = foo_; |
||||
if (((from_bitField0_ & 0x00000002) == 0x00000002)) { |
||||
to_bitField0_ |= 0x00000002; |
||||
} |
||||
if (blahBuilder_ == null) { |
||||
result.blah_ = blah_; |
||||
} else { |
||||
result.blah_ = blahBuilder_.build(); |
||||
} |
||||
result.bitField0_ = to_bitField0_; |
||||
onBuilt(); |
||||
return result; |
||||
} |
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) { |
||||
if (other instanceof Msg) { |
||||
return mergeFrom((Msg)other); |
||||
} else { |
||||
super.mergeFrom(other); |
||||
return this; |
||||
} |
||||
} |
||||
|
||||
public Builder mergeFrom(Msg other) { |
||||
if (other == Msg.getDefaultInstance()) return this; |
||||
if (other.hasFoo()) { |
||||
bitField0_ |= 0x00000001; |
||||
foo_ = other.foo_; |
||||
onChanged(); |
||||
} |
||||
if (other.hasBlah()) { |
||||
mergeBlah(other.getBlah()); |
||||
} |
||||
this.mergeUnknownFields(other.getUnknownFields()); |
||||
return this; |
||||
} |
||||
|
||||
public final boolean isInitialized() { |
||||
return true; |
||||
} |
||||
|
||||
public Builder mergeFrom( |
||||
com.google.protobuf.CodedInputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
Msg parsedMessage = null; |
||||
try { |
||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); |
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) { |
||||
parsedMessage = (Msg) e.getUnfinishedMessage(); |
||||
throw e; |
||||
} finally { |
||||
if (parsedMessage != null) { |
||||
mergeFrom(parsedMessage); |
||||
} |
||||
} |
||||
return this; |
||||
} |
||||
private int bitField0_; |
||||
|
||||
// optional string foo = 1;
|
||||
private Object foo_ = ""; |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public boolean hasFoo() { |
||||
return ((bitField0_ & 0x00000001) == 0x00000001); |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public String getFoo() { |
||||
Object ref = foo_; |
||||
if (!(ref instanceof String)) { |
||||
String s = ((com.google.protobuf.ByteString) ref) |
||||
.toStringUtf8(); |
||||
foo_ = s; |
||||
return s; |
||||
} else { |
||||
return (String) ref; |
||||
} |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public com.google.protobuf.ByteString |
||||
getFooBytes() { |
||||
Object ref = foo_; |
||||
if (ref instanceof String) { |
||||
com.google.protobuf.ByteString b = |
||||
com.google.protobuf.ByteString.copyFromUtf8( |
||||
(String) ref); |
||||
foo_ = b; |
||||
return b; |
||||
} else { |
||||
return (com.google.protobuf.ByteString) ref; |
||||
} |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public Builder setFoo( |
||||
String value) { |
||||
if (value == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
bitField0_ |= 0x00000001; |
||||
foo_ = value; |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
public Builder clearFoo() { |
||||
bitField0_ = (bitField0_ & ~0x00000001); |
||||
foo_ = getDefaultInstance().getFoo(); |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
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 SecondMsg blah_ = SecondMsg.getDefaultInstance(); |
||||
private com.google.protobuf.SingleFieldBuilder< |
||||
SecondMsg, SecondMsg.Builder, |
||||
SecondMsgOrBuilder> blahBuilder_; |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public boolean hasBlah() { |
||||
return ((bitField0_ & 0x00000002) == 0x00000002); |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public SecondMsg getBlah() { |
||||
if (blahBuilder_ == null) { |
||||
return blah_; |
||||
} else { |
||||
return blahBuilder_.getMessage(); |
||||
} |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public Builder setBlah(SecondMsg value) { |
||||
if (blahBuilder_ == null) { |
||||
if (value == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
blah_ = value; |
||||
onChanged(); |
||||
} else { |
||||
blahBuilder_.setMessage(value); |
||||
} |
||||
bitField0_ |= 0x00000002; |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public Builder setBlah( |
||||
SecondMsg.Builder builderForValue) { |
||||
if (blahBuilder_ == null) { |
||||
blah_ = builderForValue.build(); |
||||
onChanged(); |
||||
} else { |
||||
blahBuilder_.setMessage(builderForValue.build()); |
||||
} |
||||
bitField0_ |= 0x00000002; |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public Builder mergeBlah(SecondMsg value) { |
||||
if (blahBuilder_ == null) { |
||||
if (((bitField0_ & 0x00000002) == 0x00000002) && |
||||
blah_ != SecondMsg.getDefaultInstance()) { |
||||
blah_ = |
||||
SecondMsg.newBuilder(blah_).mergeFrom(value).buildPartial(); |
||||
} else { |
||||
blah_ = value; |
||||
} |
||||
onChanged(); |
||||
} else { |
||||
blahBuilder_.mergeFrom(value); |
||||
} |
||||
bitField0_ |= 0x00000002; |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public Builder clearBlah() { |
||||
if (blahBuilder_ == null) { |
||||
blah_ = SecondMsg.getDefaultInstance(); |
||||
onChanged(); |
||||
} else { |
||||
blahBuilder_.clear(); |
||||
} |
||||
bitField0_ = (bitField0_ & ~0x00000002); |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public SecondMsg.Builder getBlahBuilder() { |
||||
bitField0_ |= 0x00000002; |
||||
onChanged(); |
||||
return getBlahFieldBuilder().getBuilder(); |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
public SecondMsgOrBuilder getBlahOrBuilder() { |
||||
if (blahBuilder_ != null) { |
||||
return blahBuilder_.getMessageOrBuilder(); |
||||
} else { |
||||
return blah_; |
||||
} |
||||
} |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
private com.google.protobuf.SingleFieldBuilder< |
||||
SecondMsg, SecondMsg.Builder, |
||||
SecondMsgOrBuilder> |
||||
getBlahFieldBuilder() { |
||||
if (blahBuilder_ == null) { |
||||
blahBuilder_ = new com.google.protobuf.SingleFieldBuilder<>( |
||||
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)
|
||||
} |
||||
|
||||
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: sample.proto
|
||||
|
||||
package org.springframework.web.reactive.protobuf; |
||||
|
||||
public interface MsgOrBuilder |
||||
extends com.google.protobuf.MessageOrBuilder { |
||||
|
||||
// optional string foo = 1;
|
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
boolean hasFoo(); |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
String getFoo(); |
||||
/** |
||||
* <code>optional string foo = 1;</code> |
||||
*/ |
||||
com.google.protobuf.ByteString |
||||
getFooBytes(); |
||||
|
||||
// optional .SecondMsg blah = 2;
|
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
boolean hasBlah(); |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
SecondMsg getBlah(); |
||||
/** |
||||
* <code>optional .SecondMsg blah = 2;</code> |
||||
*/ |
||||
SecondMsgOrBuilder getBlahOrBuilder(); |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: sample.proto
|
||||
|
||||
package org.springframework.web.reactive.protobuf; |
||||
|
||||
public 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 { |
||||
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 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 String[] { "Blah", }); |
||||
return null; |
||||
} |
||||
}; |
||||
com.google.protobuf.Descriptors.FileDescriptor |
||||
.internalBuildGeneratedFileFrom(descriptorData, |
||||
new com.google.protobuf.Descriptors.FileDescriptor[] { |
||||
}, assigner); |
||||
} |
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
} |
||||
@ -0,0 +1,389 @@
@@ -0,0 +1,389 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: sample.proto
|
||||
|
||||
package org.springframework.web.reactive.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; |
||||
@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(); |
||||
@SuppressWarnings("unused") |
||||
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 OuterSample.internal_static_SecondMsg_descriptor; |
||||
} |
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable |
||||
internalGetFieldAccessorTable() { |
||||
return OuterSample.internal_static_SecondMsg_fieldAccessorTable |
||||
.ensureFieldAccessorsInitialized( |
||||
SecondMsg.class, SecondMsg.Builder.class); |
||||
} |
||||
|
||||
public static com.google.protobuf.Parser<SecondMsg> PARSER = |
||||
new com.google.protobuf.AbstractParser<SecondMsg>() { |
||||
public SecondMsg parsePartialFrom( |
||||
com.google.protobuf.CodedInputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return new SecondMsg(input, extensionRegistry); |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
public com.google.protobuf.Parser<SecondMsg> getParserForType() { |
||||
return PARSER; |
||||
} |
||||
|
||||
private int bitField0_; |
||||
// optional int32 blah = 1;
|
||||
public static final int BLAH_FIELD_NUMBER = 1; |
||||
private int blah_; |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
public boolean hasBlah() { |
||||
return ((bitField0_ & 0x00000001) == 0x00000001); |
||||
} |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
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; |
||||
@Override |
||||
protected Object writeReplace() |
||||
throws java.io.ObjectStreamException { |
||||
return super.writeReplace(); |
||||
} |
||||
|
||||
public static SecondMsg parseFrom( |
||||
com.google.protobuf.ByteString data) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data); |
||||
} |
||||
public static SecondMsg parseFrom( |
||||
com.google.protobuf.ByteString data, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data, extensionRegistry); |
||||
} |
||||
public static SecondMsg parseFrom(byte[] data) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data); |
||||
} |
||||
public static SecondMsg parseFrom( |
||||
byte[] data, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws com.google.protobuf.InvalidProtocolBufferException { |
||||
return PARSER.parseFrom(data, extensionRegistry); |
||||
} |
||||
public static SecondMsg parseFrom(java.io.InputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input); |
||||
} |
||||
public static SecondMsg parseFrom( |
||||
java.io.InputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input, extensionRegistry); |
||||
} |
||||
public static SecondMsg parseDelimitedFrom(java.io.InputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseDelimitedFrom(input); |
||||
} |
||||
public static SecondMsg parseDelimitedFrom( |
||||
java.io.InputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
return PARSER.parseDelimitedFrom(input, extensionRegistry); |
||||
} |
||||
public static SecondMsg parseFrom( |
||||
com.google.protobuf.CodedInputStream input) |
||||
throws java.io.IOException { |
||||
return PARSER.parseFrom(input); |
||||
} |
||||
public static 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(SecondMsg prototype) { |
||||
return newBuilder().mergeFrom(prototype); |
||||
} |
||||
public Builder toBuilder() { return newBuilder(this); } |
||||
|
||||
@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.Builder<Builder> |
||||
implements SecondMsgOrBuilder { |
||||
public static final com.google.protobuf.Descriptors.Descriptor |
||||
getDescriptor() { |
||||
return OuterSample.internal_static_SecondMsg_descriptor; |
||||
} |
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable |
||||
internalGetFieldAccessorTable() { |
||||
return OuterSample.internal_static_SecondMsg_fieldAccessorTable |
||||
.ensureFieldAccessorsInitialized( |
||||
SecondMsg.class, SecondMsg.Builder.class); |
||||
} |
||||
|
||||
// Construct using org.springframework.protobuf.SecondMsg.newBuilder()
|
||||
private Builder() { |
||||
maybeForceBuilderInitialization(); |
||||
} |
||||
|
||||
private Builder( |
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) { |
||||
super(parent); |
||||
maybeForceBuilderInitialization(); |
||||
} |
||||
private void maybeForceBuilderInitialization() { |
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { |
||||
} |
||||
} |
||||
private static Builder create() { |
||||
return new Builder(); |
||||
} |
||||
|
||||
public Builder clear() { |
||||
super.clear(); |
||||
blah_ = 0; |
||||
bitField0_ = (bitField0_ & ~0x00000001); |
||||
return this; |
||||
} |
||||
|
||||
public Builder clone() { |
||||
return create().mergeFrom(buildPartial()); |
||||
} |
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor |
||||
getDescriptorForType() { |
||||
return OuterSample.internal_static_SecondMsg_descriptor; |
||||
} |
||||
|
||||
public SecondMsg getDefaultInstanceForType() { |
||||
return SecondMsg.getDefaultInstance(); |
||||
} |
||||
|
||||
public SecondMsg build() { |
||||
SecondMsg result = buildPartial(); |
||||
if (!result.isInitialized()) { |
||||
throw newUninitializedMessageException(result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public SecondMsg buildPartial() { |
||||
SecondMsg result = new SecondMsg(this); |
||||
int from_bitField0_ = bitField0_; |
||||
int to_bitField0_ = 0; |
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) { |
||||
to_bitField0_ |= 0x00000001; |
||||
} |
||||
result.blah_ = blah_; |
||||
result.bitField0_ = to_bitField0_; |
||||
onBuilt(); |
||||
return result; |
||||
} |
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) { |
||||
if (other instanceof SecondMsg) { |
||||
return mergeFrom((SecondMsg)other); |
||||
} else { |
||||
super.mergeFrom(other); |
||||
return this; |
||||
} |
||||
} |
||||
|
||||
public Builder mergeFrom(SecondMsg other) { |
||||
if (other == SecondMsg.getDefaultInstance()) return this; |
||||
if (other.hasBlah()) { |
||||
setBlah(other.getBlah()); |
||||
} |
||||
this.mergeUnknownFields(other.getUnknownFields()); |
||||
return this; |
||||
} |
||||
|
||||
public final boolean isInitialized() { |
||||
return true; |
||||
} |
||||
|
||||
public Builder mergeFrom( |
||||
com.google.protobuf.CodedInputStream input, |
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry) |
||||
throws java.io.IOException { |
||||
SecondMsg parsedMessage = null; |
||||
try { |
||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); |
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) { |
||||
parsedMessage = (SecondMsg) e.getUnfinishedMessage(); |
||||
throw e; |
||||
} finally { |
||||
if (parsedMessage != null) { |
||||
mergeFrom(parsedMessage); |
||||
} |
||||
} |
||||
return this; |
||||
} |
||||
private int bitField0_; |
||||
|
||||
// optional int32 blah = 1;
|
||||
private int blah_ ; |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
public boolean hasBlah() { |
||||
return ((bitField0_ & 0x00000001) == 0x00000001); |
||||
} |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
public int getBlah() { |
||||
return blah_; |
||||
} |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
public Builder setBlah(int value) { |
||||
bitField0_ |= 0x00000001; |
||||
blah_ = value; |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
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)
|
||||
} |
||||
|
||||
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: sample.proto
|
||||
|
||||
package org.springframework.web.reactive.protobuf; |
||||
|
||||
public interface SecondMsgOrBuilder |
||||
extends com.google.protobuf.MessageOrBuilder { |
||||
|
||||
// optional int32 blah = 1;
|
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
boolean hasBlah(); |
||||
/** |
||||
* <code>optional int32 blah = 1;</code> |
||||
*/ |
||||
int getBlah(); |
||||
} |
||||
@ -0,0 +1,164 @@
@@ -0,0 +1,164 @@
|
||||
/* |
||||
* Copyright 2002-2018 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.web.reactive.result.method.annotation; |
||||
|
||||
import java.time.Duration; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.test.StepVerifier; |
||||
|
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.ComponentScan; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.reactive.config.EnableWebFlux; |
||||
import org.springframework.web.reactive.function.client.WebClient; |
||||
import org.springframework.web.reactive.protobuf.Msg; |
||||
import org.springframework.web.reactive.protobuf.SecondMsg; |
||||
|
||||
/** |
||||
* Integration tests for Protobuf support. |
||||
* |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
public class ProtobufIntegrationTests extends AbstractRequestMappingIntegrationTests { |
||||
|
||||
public static final Msg TEST_MSG = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build(); |
||||
|
||||
private WebClient webClient; |
||||
|
||||
|
||||
@Override |
||||
protected ApplicationContext initApplicationContext() { |
||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext(); |
||||
wac.register(TestConfiguration .class); |
||||
wac.refresh(); |
||||
return wac; |
||||
} |
||||
|
||||
@Override |
||||
@Before |
||||
public void setup() throws Exception { |
||||
super.setup(); |
||||
this.webClient = WebClient.create("http://localhost:" + this.port); |
||||
} |
||||
|
||||
@Test |
||||
public void value() { |
||||
Mono<Msg> result = this.webClient.get() |
||||
.uri("/message") |
||||
.exchange() |
||||
.doOnNext(response -> { |
||||
Assert.assertFalse(response.headers().contentType().get().getParameters().containsKey("delimited")); |
||||
Assert.assertEquals("sample.proto", response.headers().header("X-Protobuf-Schema").get(0)); |
||||
Assert.assertEquals("Msg", response.headers().header("X-Protobuf-Message").get(0)); |
||||
}) |
||||
.flatMap(response -> response.bodyToMono(Msg.class)); |
||||
|
||||
StepVerifier.create(result) |
||||
.expectNext(TEST_MSG) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void values() { |
||||
Flux<Msg> result = this.webClient.get() |
||||
.uri("/messages") |
||||
.exchange() |
||||
.doOnNext(response -> { |
||||
Assert.assertEquals("true", response.headers().contentType().get().getParameters().get("delimited")); |
||||
Assert.assertEquals("sample.proto", response.headers().header("X-Protobuf-Schema").get(0)); |
||||
Assert.assertEquals("Msg", response.headers().header("X-Protobuf-Message").get(0)); |
||||
}) |
||||
.flatMapMany(response -> response.bodyToFlux(Msg.class)); |
||||
|
||||
StepVerifier.create(result) |
||||
.expectNext(TEST_MSG) |
||||
.expectNext(TEST_MSG) |
||||
.expectNext(TEST_MSG) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void streaming() { |
||||
Flux<Msg> result = this.webClient.get() |
||||
.uri("/message-stream") |
||||
.exchange() |
||||
.doOnNext(response -> { |
||||
Assert.assertEquals("true", response.headers().contentType().get().getParameters().get("delimited")); |
||||
Assert.assertEquals("sample.proto", response.headers().header("X-Protobuf-Schema").get(0)); |
||||
Assert.assertEquals("Msg", response.headers().header("X-Protobuf-Message").get(0)); |
||||
}) |
||||
.flatMapMany(response -> response.bodyToFlux(Msg.class)); |
||||
|
||||
StepVerifier.create(result) |
||||
.expectNext(Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(0).build()).build()) |
||||
.expectNext(Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(1).build()).build()) |
||||
.thenCancel() |
||||
.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void empty() { |
||||
Mono<Msg> result = this.webClient.get() |
||||
.uri("/empty") |
||||
.retrieve() |
||||
.bodyToMono(Msg.class); |
||||
|
||||
StepVerifier.create(result) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@RestController |
||||
@SuppressWarnings("unused") |
||||
static class ProtobufController { |
||||
|
||||
@GetMapping("/message") |
||||
Mono<Msg> message() { |
||||
return Mono.just(TEST_MSG); |
||||
} |
||||
|
||||
@GetMapping("/messages") |
||||
Flux<Msg> messages() { |
||||
return Flux.just(TEST_MSG, TEST_MSG, TEST_MSG); |
||||
} |
||||
|
||||
@GetMapping(value = "/message-stream", produces = "application/x-protobuf;delimited=true") |
||||
Flux<Msg> messageStream() { |
||||
return testInterval(Duration.ofMillis(50), 5).map(l -> Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(l.intValue()).build()).build()); |
||||
} |
||||
|
||||
@GetMapping("/empty") |
||||
Mono<Msg> empty() { |
||||
return Mono.empty(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Configuration |
||||
@EnableWebFlux |
||||
@ComponentScan(resourcePattern = "**/ProtobufIntegrationTests*.class") |
||||
@SuppressWarnings("unused") |
||||
static class TestConfiguration { |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
option java_package = "org.springframework.web.reactive.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; |
||||
} |
||||
Loading…
Reference in new issue