org.springframework.http.codec.protobuf.ProtobufEncoder Maven / Gradle / Ivy
/*
* 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.lang.Nullable;
import org.springframework.util.MimeType;
/**
* An {@code Encoder} that writes {@link com.google.protobuf.Message}s
* using Google Protocol Buffers.
*
* Flux are serialized using
* delimited Protobuf messages
* 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).
*
*
To generate {@code Message} Java classes, you need to install the {@code protoc} binary.
*
*
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 {
private static final List 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, @Nullable MimeType mimeType) {
return Message.class.isAssignableFrom(elementType.toClass()) && supportsMimeType(mimeType);
}
@Override
public Flux encode(Publisher inputStream, DataBufferFactory bufferFactory,
ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map 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 getStreamingMediaTypes() {
return streamingMediaTypes;
}
@Override
public List getEncodableMimeTypes() {
return getMimeTypes();
}
}