All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.protostream.WrappedMessage Maven / Gradle / Ivy

package org.infinispan.protostream;

import java.io.IOException;

import org.infinispan.protostream.impl.BaseMarshallerDelegate;
import org.infinispan.protostream.impl.ByteArrayOutputStreamEx;
import org.infinispan.protostream.impl.RawProtoStreamReaderImpl;
import org.infinispan.protostream.impl.RawProtoStreamWriterImpl;
import org.infinispan.protostream.impl.SerializationContextImpl;
import org.infinispan.protostream.impl.WireFormat;

/**
 * A wrapper for messages, enums or primitive types that encodes the type of the inner object/value and also helps keep
 * track of where the message ends. The need for this wrapper stems from two particular design choices in the Protocol
 * Buffers encoding.
 * 

* 1. The Protocol Buffers encoding format does not contain any description of the message type that follows next in the * data stream, unlike for example the Java serialization format which provides information about classes that are saved * in a Serialization stream in the form of class descriptors which contain the fully qualified name of the class being * serialized. The Protocol Buffers client is expected to know what message type he is expecting to read from the * stream. This knowledge exists in most cases, statically, so this encoding scheme saves a lot of space by not * including redundant type descriptors in the stream by default. For all other use cases where data types are dynamic * you are on your own, but {@link WrappedMessage} is here to help you. *

* 2. The Protocol Buffer wire format is also not self-delimiting, so when reading a message we see just a stream of * fields and we are not able to determine when the fields of the current message end and the next message starts. The * protocol assumes that the whole contents of the stream is to be interpreted as a single message. If that's not the * case, then the user must provide his own way of delimiting messages either by using message start/stop markers or by * prefixing each message with its size or any other equivalent mechanism. {@link WrappedMessage} relies on an {@code * int32} size prefix. *

* So wherever you cannot statically decide what message type you'll be using and need to defer this until runtime, just * use {@link WrappedMessage}. * * @author [email protected] * @since 1.0 */ public final class WrappedMessage { /** * The fully qualified Protobuf type name of this message. */ public static final String PROTOBUF_TYPE_NAME = "org.infinispan.protostream.WrappedMessage"; /** * A wrapped double. */ public static final int WRAPPED_DOUBLE = 1; /** * A wrapped float. */ public static final int WRAPPED_FLOAT = 2; /** * A wrapped int64. */ public static final int WRAPPED_INT64 = 3; /** * A wrapped uint64. */ public static final int WRAPPED_UINT64 = 4; /** * A wrapped int32. */ public static final int WRAPPED_INT32 = 5; /** * A wrapped fixed64. */ public static final int WRAPPED_FIXED64 = 6; /** * A wrapped fixed32. */ public static final int WRAPPED_FIXED32 = 7; /** * A wrapped bool. */ public static final int WRAPPED_BOOL = 8; /** * A wrapped string. */ public static final int WRAPPED_STRING = 9; /** * A wrapped bytes. */ public static final int WRAPPED_BYTES = 10; /** * A wrapped uint32. */ public static final int WRAPPED_UINT32 = 11; /** * A wrapped sfixed32. */ public static final int WRAPPED_SFIXED32 = 12; /** * A wrapped sfixed64. */ public static final int WRAPPED_SFIXED64 = 13; /** * A wrapped sint32. */ public static final int WRAPPED_SINT32 = 14; /** * A wrapped sint64. */ public static final int WRAPPED_SINT64 = 15; /** * The name of the fully qualified message or enum descriptor, if the wrapped object is a message or enum. */ public static final int WRAPPED_DESCRIPTOR_FULL_NAME = 16; /** * A byte array containing the encoded message. */ public static final int WRAPPED_MESSAGE = 17; /** * @deprecated Use {@link #WRAPPED_MESSAGE} instead. To be removed in 4.1. */ @Deprecated public static final int WRAPPED_MESSAGE_BYTES = WRAPPED_MESSAGE; /** * The enum value. */ public static final int WRAPPED_ENUM = 18; /** * The (optional) type id of the wrapped message or enum. This is an alternative to {@link * #WRAPPED_DESCRIPTOR_FULL_NAME}. */ public static final int WRAPPED_DESCRIPTOR_ID = 19; /** * The wrapped object or (boxed) primitive. */ private final Object value; public WrappedMessage(Object value) { this.value = value; } public Object getValue() { return value; } public static void writeMessage(ImmutableSerializationContext ctx, RawProtoStreamWriter out, Object t) throws IOException { if (t == null) { return; } if (t instanceof String) { out.writeString(WRAPPED_STRING, (String) t); } else if (t instanceof Long) { out.writeInt64(WRAPPED_INT64, (Long) t); } else if (t instanceof Integer) { out.writeInt32(WRAPPED_INT32, (Integer) t); } else if (t instanceof Double) { out.writeDouble(WRAPPED_DOUBLE, (Double) t); } else if (t instanceof Float) { out.writeFloat(WRAPPED_FLOAT, (Float) t); } else if (t instanceof Boolean) { out.writeBool(WRAPPED_BOOL, (Boolean) t); } else if (t instanceof byte[]) { out.writeBytes(WRAPPED_BYTES, (byte[]) t); } else if (t instanceof Enum) { // use an enum encoder EnumMarshaller enumMarshaller = (EnumMarshaller) ctx.getMarshaller((Class) t.getClass()); int encodedEnum = enumMarshaller.encode((Enum) t); Integer typeId = ctx.getTypeIdByName(enumMarshaller.getTypeName()); if (typeId == null) { out.writeString(WRAPPED_DESCRIPTOR_FULL_NAME, enumMarshaller.getTypeName()); } else { out.writeInt32(WRAPPED_DESCRIPTOR_ID, typeId); } out.writeEnum(WRAPPED_ENUM, encodedEnum); } else { // This is either an unknown primitive type or a message type. Try to use a message marshaller. BaseMarshallerDelegate marshallerDelegate = ((SerializationContextImpl) ctx).getMarshallerDelegate(t.getClass()); ByteArrayOutputStreamEx buffer = new ByteArrayOutputStreamEx(); RawProtoStreamWriter nestedOut = RawProtoStreamWriterImpl.newInstance(buffer); marshallerDelegate.marshall(null, t, null, nestedOut); nestedOut.flush(); String typeName = marshallerDelegate.getMarshaller().getTypeName(); Integer typeId = ctx.getTypeIdByName(typeName); if (typeId == null) { out.writeString(WRAPPED_DESCRIPTOR_FULL_NAME, typeName); } else { out.writeInt32(WRAPPED_DESCRIPTOR_ID, typeId); } out.writeBytes(WRAPPED_MESSAGE, buffer.getByteBuffer()); } out.flush(); } public static T readMessage(ImmutableSerializationContext ctx, RawProtoStreamReader in) throws IOException { String descriptorFullName = null; Integer typeId = null; int enumValue = -1; byte[] messageBytes = null; Object value = null; int readTags = 0; int tag; while ((tag = in.readTag()) != 0) { readTags++; switch (tag) { case WRAPPED_DESCRIPTOR_FULL_NAME << 3 | WireFormat.WIRETYPE_LENGTH_DELIMITED: descriptorFullName = in.readString(); break; case WRAPPED_DESCRIPTOR_ID << 3 | WireFormat.WIRETYPE_VARINT: typeId = in.readInt32(); break; case WRAPPED_ENUM << 3 | WireFormat.WIRETYPE_VARINT: enumValue = in.readEnum(); break; case WRAPPED_MESSAGE << 3 | WireFormat.WIRETYPE_LENGTH_DELIMITED: messageBytes = in.readByteArray(); break; case WRAPPED_STRING << 3 | WireFormat.WIRETYPE_LENGTH_DELIMITED: value = in.readString(); break; case WRAPPED_BYTES << 3 | WireFormat.WIRETYPE_LENGTH_DELIMITED: value = in.readByteArray(); break; case WRAPPED_BOOL << 3 | WireFormat.WIRETYPE_VARINT: value = in.readBool(); break; case WRAPPED_DOUBLE << 3 | WireFormat.WIRETYPE_FIXED64: value = in.readDouble(); break; case WRAPPED_FLOAT << 3 | WireFormat.WIRETYPE_FIXED32: value = in.readFloat(); break; case WRAPPED_FIXED32 << 3 | WireFormat.WIRETYPE_FIXED32: value = in.readFixed32(); break; case WRAPPED_SFIXED32 << 3 | WireFormat.WIRETYPE_FIXED32: value = in.readSFixed32(); break; case WRAPPED_FIXED64 << 3 | WireFormat.WIRETYPE_FIXED64: value = in.readFixed64(); break; case WRAPPED_SFIXED64 << 3 | WireFormat.WIRETYPE_FIXED64: value = in.readSFixed64(); break; case WRAPPED_INT64 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readInt64(); break; case WRAPPED_UINT64 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readUInt64(); break; case WRAPPED_SINT64 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readSInt64(); break; case WRAPPED_INT32 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readInt32(); break; case WRAPPED_UINT32 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readUInt32(); break; case WRAPPED_SINT32 << 3 | WireFormat.WIRETYPE_VARINT: value = in.readSInt32(); break; default: throw new IllegalStateException("Unexpected tag : " + tag); } } if (value == null && descriptorFullName == null && typeId == null && messageBytes == null) { return null; } if (value != null) { if (readTags != 1) { throw new IOException("Invalid message encoding."); } return (T) value; } if (descriptorFullName == null && typeId == null || descriptorFullName != null && typeId != null || readTags != 2) { throw new IOException("Invalid message encoding."); } if (typeId != null) { descriptorFullName = ctx.getTypeNameById(typeId); } BaseMarshallerDelegate marshallerDelegate = ((SerializationContextImpl) ctx).getMarshallerDelegate(descriptorFullName); if (messageBytes != null) { // it's a Message type RawProtoStreamReader nestedInput = RawProtoStreamReaderImpl.newInstance(messageBytes); return (T) marshallerDelegate.unmarshall(null, null, nestedInput); } else { // it's an Enum EnumMarshaller marshaller = (EnumMarshaller) marshallerDelegate.getMarshaller(); return (T) marshaller.decode(enumValue); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WrappedMessage other = (WrappedMessage) o; return value != null ? value.equals(other.value) : other.value == null; } @Override public int hashCode() { return value != null ? value.hashCode() : 0; } @Override public String toString() { return "WrappedMessage{value=" + value + '}'; } public static final class Marshaller implements RawProtobufMarshaller { @Override public Class getJavaClass() { return WrappedMessage.class; } @Override public String getTypeName() { return PROTOBUF_TYPE_NAME; } @Override public WrappedMessage readFrom(ImmutableSerializationContext ctx, RawProtoStreamReader in) throws IOException { return new WrappedMessage(readMessage(ctx, in)); } @Override public void writeTo(ImmutableSerializationContext ctx, RawProtoStreamWriter out, WrappedMessage wrappedMessage) throws IOException { writeMessage(ctx, out, wrappedMessage.value); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy