/*
* Copyright (c) 2016 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.jersey.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Charsets;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.Message;
import com.google.protobuf.Message.Builder;
import com.googlecode.protobuf.format.JsonFormat;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@Provider
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public class ProtobufJsonProvider extends AbstractProtobufProvider
implements MessageBodyReader<Object>, MessageBodyWriter<Object> {
private static final Logger log = LoggerFactory.getLogger(ProtobufJsonProvider.class);
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return Message.class.isAssignableFrom(type) || isWrapped(type, genericType);
}
@Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
Class<Message> messageType = extractMessageType(type, genericType);
ExtensionRegistry extensionRegistry = extensions().forMessage(messageType);
Builder builder = builders().forMessage(messageType);
Readable input = new InputStreamReader(entityStream, Charsets.UTF_8);
if(isWrapped(type, genericType)) {
// JsonFormat does not provide a mergeCollection method
return JsonIoUtil.mergeCollection(input, extensionRegistry, builder);
}
JsonFormat.merge(input, extensionRegistry, builder);
return builder.build();
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return Message.class.isAssignableFrom(type) || isWrapped(type, genericType);
}
@Override
public long getSize(Object t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
@SuppressWarnings({ "unchecked", "PMD.ExcessiveParameterList" })
public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
try(OutputStreamWriter output = new OutputStreamWriter(entityStream, Charsets.UTF_8)) {
if(isWrapped(type, genericType)) {
// JsonFormat does not provide a printList method
if(log.isDebugEnabled()) {
Appendable sb = new StringBuilder();
JsonIoUtil.printCollection((Iterable<Message>) obj, sb);
log.trace("Print message collection: {}", sb);
}
JsonIoUtil.printCollection((Iterable<Message>) obj, output);
} else {
log.trace("Print single message: {}", JsonFormat.printToString((Message) obj));
JsonFormat.print((Message) obj, output);
}
output.flush();
}
}
}