package io.dropwizard.jersey.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import com.google.protobuf.Message;
import com.google.protobuf.TextFormat;
/**
* A Jersey provider which enables using Protocol Buffers to parse request
* entities into objects and generate response entities from objects.
*/
@Provider
@Consumes(ProtocolBufferMediaType.APPLICATION_PROTOBUF)
@Produces({ ProtocolBufferMediaType.APPLICATION_PROTOBUF,
ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT })
public class ProtocolBufferMessageBodyProvider
implements MessageBodyReader<Message>, MessageBodyWriter<Message> {
@Override
public boolean isReadable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return Message.class.isAssignableFrom(type);
}
@Override
public Message readFrom(final Class<Message> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, String> httpHeaders,
final InputStream entityStream) throws IOException {
try {
final Method newBuilder = type.getMethod("newBuilder");
final Message.Builder builder = (Message.Builder) newBuilder
.invoke(type);
return builder.mergeFrom(entityStream).build();
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
@Override
public long getSize(final Message m, final Class<?> type,
final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
if (mediaType.getSubtype().contains("text-format")) {
final String formatted = TextFormat.printToUnicodeString(m);
return formatted.getBytes(StandardCharsets.UTF_8).length;
}
return m.getSerializedSize();
}
@Override
public boolean isWriteable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return Message.class.isAssignableFrom(type);
}
@Override
public void writeTo(final Message m, final Class<?> type,
final Type genericType, final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders,
final OutputStream entityStream) throws IOException {
if (mediaType.getSubtype().contains("text-format")) {
entityStream.write(m.toString().getBytes(StandardCharsets.UTF_8));
} else {
entityStream.write(m.toByteArray());
}
}
}