blob: 0607ecaa46287a4de275c621db90fb7081c087d4 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.businessobjects.utils;
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.MEDIATYPE_APPLICATION_PROTOBUF;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
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.io.ByteStreams;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
/**
* MessageBodyProvider for handling protobuf payloads.
*
*/
@Provider
@Consumes({ MEDIATYPE_APPLICATION_PROTOBUF })
@Produces({ MEDIATYPE_APPLICATION_PROTOBUF })
public class ProtobufMessageBodyProvider implements MessageBodyReader<Message>, MessageBodyWriter<Message> {
private static final Logger LOG = LoggerFactory.getLogger(ProtobufMessageBodyProvider.class);
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
private static final int MAX_SIZE = DEFAULT_SIZE_LIMIT;
/*
* (non-Javadoc)
*
* @see javax.ws.rs.ext.MessageBodyReader#isReadable(java.lang.Class,
* java.lang.reflect.Type, java.lang.annotation.Annotation[],
* javax.ws.rs.core.MediaType)
*/
@Override
public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
return Message.class.isAssignableFrom(type);
}
/*
* (non-Javadoc)
*
* @see javax.ws.rs.ext.MessageBodyReader#readFrom(java.lang.Class,
* java.lang.reflect.Type, java.lang.annotation.Annotation[],
* javax.ws.rs.core.MediaType, javax.ws.rs.core.MultivaluedMap,
* java.io.InputStream)
*/
@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 AbstractMessage.Builder<?> builder = (AbstractMessage.Builder<?>) newBuilder.invoke(type);
CodedInputStream in = CodedInputStream.newInstance(entityStream);
in.setSizeLimit(MAX_SIZE);
byte[] b = ByteStreams.toByteArray(entityStream);
LOG.debug("Reading type {} with size {}.", genericType, b.length);
return builder.mergeFrom(b).build();
} catch (InvalidProtocolBufferException e) {
throw new IOException("Could not read Protobuf entity!", e);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new IOException("Could not retrive builder for type " + genericType, e);
}
}
/*
* (non-Javadoc)
*
* @see javax.ws.rs.ext.MessageBodyWriter#getSize(java.lang.Object,
* java.lang.Class, java.lang.reflect.Type, java.lang.annotation.Annotation[],
* javax.ws.rs.core.MediaType)
*/
@Override
public long getSize(final Message m, final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
return -1; // as method is not actually used by JAX-RS
}
/*
* (non-Javadoc)
*
* @see javax.ws.rs.ext.MessageBodyWriter#isWriteable(java.lang.Class,
* java.lang.reflect.Type, java.lang.annotation.Annotation[],
* javax.ws.rs.core.MediaType)
*/
@Override
public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
return Message.class.isAssignableFrom(type);
}
/*
* (non-Javadoc)
*
* @see javax.ws.rs.ext.MessageBodyWriter#writeTo(java.lang.Object,
* java.lang.Class, java.lang.reflect.Type, java.lang.annotation.Annotation[],
* javax.ws.rs.core.MediaType, javax.ws.rs.core.MultivaluedMap,
* java.io.OutputStream)
*/
@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 (LOG.isDebugEnabled()) {
LOG.debug("Writing type {} with size {}.", genericType, m.getSerializedSize());
}
m.writeTo(entityStream);
}
}