public class

JaxbSerializer

extends AbstractSerializer<T>
package com.netflix.astyanax.serializers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import com.netflix.astyanax.connectionpool.exceptions.SerializationException;

/**
 * Serializes Objects using Jaxb. An instance of this class may only serialize
 * JAXB compatible objects of classes known to its configured context.
 * 
 * @author shuzhang0@gmail.com
 */
public class JaxbSerializer extends AbstractSerializer<Object> {

    /** The cached per-thread marshaller. */
    private ThreadLocal<Marshaller> marshaller;
    /** The cached per-thread unmarshaller. */
    private ThreadLocal<Unmarshaller> unmarshaller;

    /**
     * Lazily initialized singleton factory for producing default
     * XMLStreamWriters.
     */
    private static XMLOutputFactory outputFactory;
    /**
     * Lazily initialized singleton factory for producing default
     * XMLStreamReaders.
     */
    private static XMLInputFactory inputFactory;

    /**
     * Constructor.
     * 
     * @param serializableClasses
     *            List of classes which can be serialized by this instance. Note
     *            that concrete classes directly referenced by any class in the
     *            list will also be serializable through this instance.
     */
    public JaxbSerializer(final Class... serializableClasses) {
        marshaller = new ThreadLocal<Marshaller>() {
            @Override
            protected Marshaller initialValue() {
                try {
                    return JAXBContext.newInstance(serializableClasses).createMarshaller();
                }
                catch (JAXBException e) {
                    throw new IllegalArgumentException("Classes to serialize are not JAXB compatible.", e);
                }
            }
        };

        unmarshaller = new ThreadLocal<Unmarshaller>() {
            @Override
            protected Unmarshaller initialValue() {
                try {
                    return JAXBContext.newInstance(serializableClasses).createUnmarshaller();
                }
                catch (JAXBException e) {
                    throw new IllegalArgumentException("Classes to serialize are not JAXB compatible.", e);
                }
            }
        };
    }

    /** {@inheritDoc} */
    @Override
    public ByteBuffer toByteBuffer(Object obj) {
        if (obj == null) {
            return null;
        }

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try {
            XMLStreamWriter writer = createStreamWriter(buffer);
            marshaller.get().marshal(obj, writer);
            writer.flush();
            writer.close();
        }
        catch (JAXBException e) {
            throw new SerializationException("Object to serialize " + obj
                    + " does not seem compatible with the configured JaxbContext;"
                    + " note this Serializer works only with JAXBable objects.", e);
        }
        catch (XMLStreamException e) {
            throw new SerializationException("Exception occurred writing XML stream.", e);
        }
        return ByteBuffer.wrap(buffer.toByteArray());
    }

    /** {@inheritDoc} */
    @Override
    public Object fromByteBuffer(ByteBuffer bytes) {
        if (bytes == null || !bytes.hasRemaining()) {
            return null;
        }

        ByteArrayInputStream bais = new ByteArrayInputStream(bytes.array());
        try {
            XMLStreamReader reader = createStreamReader(bais);
            Object ret = unmarshaller.get().unmarshal(reader);
            reader.close();
            return ret;
        }
        catch (JAXBException e) {
            throw new SerializationException("Jaxb exception occurred during deserialization.", e);
        }
        catch (XMLStreamException e) {
            throw new SerializationException("Exception reading XML stream.", e);
        }
    }

    /**
     * Get a new XML stream writer.
     * 
     * @param output
     *            An underlying OutputStream to write to.
     * @return a new {@link XMLStreamWriter} which writes to the specified
     *         OutputStream. The output written by this XMLStreamWriter is
     *         understandable by XMLStreamReaders produced by
     *         {@link #createStreamReader(InputStream)}.
     * @throws XMLStreamException
     */
    // Provides hook for subclasses to override how marshalling results are
    // serialized (ex. encoding).
    protected XMLStreamWriter createStreamWriter(OutputStream output) throws XMLStreamException {
        if (outputFactory == null) {
            outputFactory = XMLOutputFactory.newInstance();
        }
        return outputFactory.createXMLStreamWriter(output);
    }

    /**
     * Get a new XML stream reader.
     * 
     * @param input
     *            the underlying InputStream to read from.
     * @return a new {@link XmlStreamReader} which reads from the specified
     *         InputStream. The reader can read anything written by
     *         {@link #createStreamWriter(OutputStream)}.
     * @throws XMLStreamException
     */
    protected XMLStreamReader createStreamReader(InputStream input) throws XMLStreamException {
        if (inputFactory == null) {
            inputFactory = XMLInputFactory.newInstance();
        }
        return inputFactory.createXMLStreamReader(input);
    }

}