package tap.core.mapreduce.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tap.util.Protobufs;
import tap.util.TypeRef;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.UninitializedMessageException;
/**
* {@link BinaryConverter} for Protobufs
*/
public class ProtobufConverter<M extends Message> implements BinaryConverter<M> {
private static final Logger LOG = LoggerFactory.getLogger(ProtobufConverter.class);
private Message.Builder protoBuilder;
private TypeRef<M> typeRef;
// limit the number of warnings in case of serialization errors.
private static final int MAX_WARNINGS = 100;
private static int numWarningsLogged = 0;
private static void logWarning(String message, Throwable t) {
// does not need to be thread safe
if ( numWarningsLogged < MAX_WARNINGS ) {
LOG.info(message, t);
numWarningsLogged++;
}
}
/**
* Returns a ProtobufConverter for a given Protobuf class.
*/
public static <M extends Message> ProtobufConverter<M> newInstance(Class<M> protoClass) {
return new ProtobufConverter<M>(new TypeRef<M>(protoClass){});
}
public static <M extends Message> ProtobufConverter<M> newInstance(TypeRef<M> typeRef) {
return new ProtobufConverter<M>(typeRef);
}
public ProtobufConverter(TypeRef<M> typeRef) {
this.typeRef = typeRef;
}
@SuppressWarnings("unchecked")
@Override
public M fromBytes(byte[] messageBuffer) {
try {
if (protoBuilder == null) {
protoBuilder = Protobufs.getMessageBuilder(typeRef.getRawClass());
}
return (M) protoBuilder.clone().mergeFrom(messageBuffer).build();
} catch (InvalidProtocolBufferException e) {
logWarning("Invalid Protobuf exception while building " + typeRef.getRawClass().getName(), e);
} catch(UninitializedMessageException ume) {
logWarning("Uninitialized Message Exception while building " + typeRef.getRawClass().getName(), ume);
}
return null;
}
@Override
public byte[] toBytes(M message) {
return message.toByteArray();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
try {
return typeRef.getType().equals(((ProtobufConverter<?>)obj).typeRef.getType());
} catch (ClassCastException e) {
return false;
}
}
}