Class VersionedSerializer<T>

  • Type Parameters:
    T - Type of the object to serialize.
    Direct Known Subclasses:
    VersionedSerializer.Direct, VersionedSerializer.MultiType, VersionedSerializer.WithBuilder

    public abstract class VersionedSerializer<T>
    extends java.lang.Object
    Custom serializer base class that supports backward and forward compatibility. Subclass one of the following based on your needs: * VersionedSerializer.Direct for mutable objects. * VersionedSerializer.WithBuilder for immutable objects with Builders. * VersionedSerializer.MultiType for objects of multiple types inheriting from a common base type. General Notes: Versions * Provide a means of making incompatible format changes, most likely once enough Revisions are accumulated. Serializations in different versions are not meant to be compatible, and that's exactly what the goal of Versions is. * To introduce a new Version B = A + 1, its format needs to be established and published with the code. While doing so, the code must still write version A (since during an upgrade not all existing code will immediately know how to handle Version B). Only after all existing deployed code knows about Version B, can we have the code serialize in Version B. ** This can be achieved by declaring the serialization version using the getWriteVersion() method. Revisions * Are incremental on top of the previous ones, can be added on the fly, and can be used to make format changes without breaking backward or forward compatibility. * Older code will read as many revisions as it knows about, so even if newer code encodes B revisions, older code that only knows about A < B revisions will only read the first A revisions, ignoring the rest. Similarly, newer code that knows about B revisions will be able to handle A < B revisions by reading as much as is available. ** It is the responsibility of the calling code to fill-in-the-blanks for newly added fields in revisions > A. * Once published, the format for a Version-Revision should never change, otherwise existing (older) code will not be able to process that serialization. OutputStreams/InputStreams * Each Revision's serialization gets an exclusive RevisionDataOutput for writing and an exclusive RevisionDataInput for reading. These are backed by OutputStreams/InputStreams that segment the data within the entire Serialization Stream. * A RevisionDataInput will disallow reading beyond the data serialized for a Revision, and if less data was read, it will skip over the remaining bytes as needed. * A RevisionDataOutput requires the length of the serialization so that it can encode it (for use by RevisionDataInput). This length is encoded as the first 4 bytes of the serialization of each Revision. ** If the target OutputStream (where we serialize to) implements RandomOutput, then the length can be automatically determined and backfilled without any extra work by the caller. Otherwise the caller is required to call length(int) with an appropriate value prior to writing any data to this object so that the length can be written (the serialization will fail if the number of bytes written differs from the length declared). ** RevisionDataOutput has a requiresExplicitLength() to aid in determining which kind of OutputStream is being used. ** RevisionDataOutput has a number of methods that can be used in calculating the length of Strings and other complex structures. ** Consider serializing to a ByteBufferOutputStream if you want to make use of the RandomOutput features (such as automatic length measurement). *** Consider using ByteArraySegment serialize(T object) if you want the VersionedSerializer to do this for you. Be mindful that this will create a new buffer for the serialization, which might affect performance. Data Formats * RevisionDataOutput and RevisionDataInput extend Java's DataOutput(Stream) and DataInput(Stream) and they use those classes' implementations for encoding primitive data types. * On top of that, they provide APIs for serializing commonly used structures: ** UUIDs ** Collections ** Maps ** Compact Numbers (Integers which serialize to 1, 2 or 4 bytes and Longs that serialize to 1, 2, 4 or 8 bytes). * Refer to RevisionDataOutput and RevisionDataInput Javadoc for more details.
    • Constructor Detail

      • VersionedSerializer

        public VersionedSerializer()
    • Method Detail

      • serialize

        public void serialize​(RevisionDataOutput dataOutput,
                              T object)
                       throws java.io.IOException
        Serializes the given object to the given RevisionDataOutput. This overload is usually invoked for serializing nested classes or collections.
        Parameters:
        dataOutput - The RevisionDataOutput to serialize to.
        object - The object to serialize.
        Throws:
        java.io.IOException - If an IO Exception occurred.
      • serialize

        public ByteArraySegment serialize​(T object)
                                   throws java.io.IOException
        Serializes the given object to an in-memory buffer (RandomAccessOutputStream) and returns a view of it.
        Parameters:
        object - The object to serialize.
        Returns:
        An ArrayView which represents the serialized data. This provides a view (offset+length) into a Java byte array and has APIs to extract or copy the data out of there.
        Throws:
        java.io.IOException - If an IO Exception occurred.
      • serialize

        public abstract void serialize​(java.io.OutputStream stream,
                                       T object)
                                throws java.io.IOException
        Serializes the given object to the given OutputStream.
        Parameters:
        stream - The OutputStream to serialize to.
        object - The object to serialize.
        Throws:
        java.io.IOException - If an IO Exception occurred.