Package org.axonframework.serializer.json

Source Code of org.axonframework.serializer.json.JacksonSerializer

/*
* Copyright (c) 2010-2014. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.axonframework.serializer.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.axonframework.serializer.AnnotationRevisionResolver;
import org.axonframework.serializer.ChainingConverterFactory;
import org.axonframework.serializer.ConverterFactory;
import org.axonframework.serializer.RevisionResolver;
import org.axonframework.serializer.SerializationException;
import org.axonframework.serializer.SerializedObject;
import org.axonframework.serializer.SerializedType;
import org.axonframework.serializer.Serializer;
import org.axonframework.serializer.SimpleSerializedObject;
import org.axonframework.serializer.SimpleSerializedType;
import org.axonframework.serializer.UnknownSerializedTypeException;
import org.joda.time.DateTime;
import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.MonthDay;
import org.joda.time.MutableDateTime;
import org.joda.time.ReadableInstant;
import org.joda.time.YearMonth;

import java.io.IOException;

/**
* Serializer implementation that uses Jackson to serialize objects into a JSON format. Although the Jackson serializer
* requires classes to be compatible with this specific serializer, it providers much more compact serialization, while
* still being human readable.
*
* @author Allard Buijze
* @since 2.2
*/
public class JacksonSerializer implements Serializer {

    private final RevisionResolver revisionResolver;
    private final ConverterFactory converterFactory;
    private final ObjectMapper objectMapper;
    private final ClassLoader classLoader;

    /**
     * Initialize the serializer with a default ObjectMapper instance. Revisions are resolved using {@link
     * org.axonframework.serializer.Revision @Revision} annotations on the serialized classes.
     */
    public JacksonSerializer() {
        this(new AnnotationRevisionResolver(), new ChainingConverterFactory());
    }

    /**
     * Initialize the serializer with the given <code>objectMapper</code> to serialize and parse the objects to JSON.
     * This objectMapper allows for customization of the serialized form.
     *
     * @param objectMapper The objectMapper to serialize objects and parse JSON with
     */
    public JacksonSerializer(ObjectMapper objectMapper) {
        this(objectMapper, new AnnotationRevisionResolver(), new ChainingConverterFactory());
    }

    /**
     * Initialize the serializer using a default ObjectMapper instance, using the given <code>revisionResolver</code>
     * to define revision for each object to serialize, and given <code>converterFactory</code> to be used by
     * upcasters.
     *
     * @param revisionResolver The strategy to use to resolve the revision of an object
     * @param converterFactory The factory providing the converter instances for upcasters
     */
    public JacksonSerializer(RevisionResolver revisionResolver, ConverterFactory converterFactory) {
        this(new ObjectMapper(), revisionResolver, converterFactory);
    }

    /**
     * Initialize the serializer with the given <code>objectMapper</code> to serialize and parse the objects to JSON.
     * This objectMapper allows for customization of the serialized form. The given <code>revisionResolver</code> is
     * used to resolve the revision from an object to be serialized.
     *
     * @param objectMapper     The objectMapper to serialize objects and parse JSON with
     * @param revisionResolver The strategy to use to resolve the revision of an object
     */
    public JacksonSerializer(ObjectMapper objectMapper, RevisionResolver revisionResolver) {
        this(objectMapper, revisionResolver, new ChainingConverterFactory());
    }

    /**
     * Initialize the serializer with the given <code>objectMapper</code> to serialize and parse the objects to JSON.
     * This objectMapper allows for customization of the serialized form. The given <code>revisionResolver</code> is
     * used to resolve the revision from an object to be serialized. The given <code>converterFactory</code> is the
     * converter factory used by upcasters to convert between content types.
     *
     * @param objectMapper     The objectMapper to serialize objects and parse JSON with
     * @param revisionResolver The strategy to use to resolve the revision of an object
     * @param converterFactory The factory providing the converter instances for upcasters
     */
    public JacksonSerializer(ObjectMapper objectMapper, RevisionResolver revisionResolver,
                             ConverterFactory converterFactory) {
        this(objectMapper, revisionResolver, converterFactory, null);
    }

    /**
     * Initialize the serializer with the given <code>objectMapper</code> to serialize and parse the objects to JSON.
     * This objectMapper allows for customization of the serialized form. The given <code>revisionResolver</code> is
     * used to resolve the revision from an object to be serialized. The given <code>converterFactory</code> is the
     * converter factory used by upcasters to convert between content types.
     *
     * @param objectMapper     The objectMapper to serialize objects and parse JSON with
     * @param revisionResolver The strategy to use to resolve the revision of an object
     * @param converterFactory The factory providing the converter instances for upcasters
     * @param classLoader      The class loader to load classes with when deserializing
     */
    public JacksonSerializer(ObjectMapper objectMapper, RevisionResolver revisionResolver,
                             ConverterFactory converterFactory, ClassLoader classLoader) {
        this.revisionResolver = revisionResolver;
        this.converterFactory = converterFactory;
        this.objectMapper = objectMapper;
        this.classLoader = classLoader == null ? getClass().getClassLoader() : classLoader;
        this.objectMapper.registerModule(
                new SimpleModule("Axon-Jackson Module")
                        .addSerializer(ReadableInstant.class, new ToStringSerializer())
                        .addDeserializer(DateTime.class, new JodaDeserializer<DateTime>(DateTime.class))
                        .addDeserializer(Instant.class, new JodaDeserializer<Instant>(Instant.class))
                        .addDeserializer(MutableDateTime.class,
                                         new JodaDeserializer<MutableDateTime>(MutableDateTime.class))
                        .addDeserializer(YearMonth.class, new JodaDeserializer<YearMonth>(YearMonth.class))
                        .addDeserializer(MonthDay.class, new JodaDeserializer<MonthDay>(MonthDay.class))
                        .addDeserializer(LocalDate.class, new JodaDeserializer<LocalDate>(LocalDate.class))
                        .addDeserializer(LocalTime.class, new JodaDeserializer<LocalTime>(LocalTime.class))
                        .addDeserializer(LocalDateTime.class, new JodaDeserializer<LocalDateTime>(LocalDateTime.class))
        );
        if (converterFactory instanceof ChainingConverterFactory) {
            registerConverters((ChainingConverterFactory) converterFactory);
        }
    }

    /**
     * Registers converters with the given <code>converterFactory</code> which depend on the actual contents of the
     * serialized for to represent a JSON format.
     *
     * @param converterFactory The ChainingConverterFactory instance to register the converters with.
     */
    protected void registerConverters(ChainingConverterFactory converterFactory) {
        converterFactory.registerConverter(new JsonNodeToByteArrayConverter(objectMapper));
        converterFactory.registerConverter(new ByteArrayToJsonNodeConverter(objectMapper));
    }

    @Override
    public <T> SerializedObject<T> serialize(Object object, Class<T> expectedRepresentation) {

        try {
            if (String.class.equals(expectedRepresentation)) {
                //noinspection unchecked
                return new SimpleSerializedObject<T>((T) getWriter().writeValueAsString(object),
                                                     expectedRepresentation, typeForClass(object.getClass()));
            }

            byte[] serializedBytes = getWriter().writeValueAsBytes(object);
            T serializedContent = converterFactory.getConverter(byte[].class, expectedRepresentation)
                                                  .convert(serializedBytes);
            return new SimpleSerializedObject<T>(serializedContent, expectedRepresentation,
                                                 typeForClass(object.getClass()));
        } catch (JsonProcessingException e) {
            throw new SerializationException("Unable to serialize object", e);
        }
    }

    /**
     * Returns the ObjectMapper used by this serializer, allowing for configuration of the serialization settings.
     *
     * @return the ObjectMapper instance used by his serializer
     */
    public final ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /**
     * Provides the ObjectWriter, with which objects are serialized to JSON form. This method may be overridden to
     * change the configuration of the writer to use.
     *
     * @return The writer to serialize objects with
     */
    protected ObjectWriter getWriter() {
        return objectMapper.writer();
    }

    /**
     * Provides the ObjectReader, with which objects are read from the JSON form. This method may be overridden to
     * change the configuration of the reader to use.
     *
     * @param type The type of object to create a reader for
     * @return The writer to serialize objects with
     */
    protected ObjectReader getReader(Class<?> type) {
        return objectMapper.reader(type);
    }

    @Override
    public <T> boolean canSerializeTo(Class<T> expectedRepresentation) {
        return JsonNode.class.equals(expectedRepresentation)
                || String.class.equals(expectedRepresentation)
                || converterFactory.hasConverter(byte[].class, expectedRepresentation);
    }

    @Override
    public <S, T> T deserialize(SerializedObject<S> serializedObject) {
        try {
            if (JsonNode.class.equals(serializedObject.getContentType())) {
                return getReader(classForType(serializedObject.getType()))
                        .readValue((JsonNode) serializedObject.getData());
            }
            SerializedObject<byte[]> byteSerialized = converterFactory.getConverter(serializedObject.getContentType(),
                                                                                    byte[].class)
                                                                      .convert(serializedObject);
            return getReader(classForType(serializedObject.getType())).readValue(byteSerialized.getData());
        } catch (IOException e) {
            throw new SerializationException("Error while deserializing object", e);
        }
    }

    @Override
    public Class classForType(SerializedType type) throws UnknownSerializedTypeException {
        try {
            return classLoader.loadClass(resolveClassName(type));
        } catch (ClassNotFoundException e) {
            throw new UnknownSerializedTypeException(type, e);
        }
    }

    /**
     * Resolve the class name from the given <code>serializedType</code>. This method may be overridden to customize
     * the names used to denote certain classes, for example, by leaving out a certain base package for brevity.
     *
     * @param serializedType The serialized type to resolve the class name for
     * @return The fully qualified name of the class to load
     */
    protected String resolveClassName(SerializedType serializedType) {
        return serializedType.getName();
    }

    @Override
    public SerializedType typeForClass(Class type) {
        return new SimpleSerializedType(type.getName(), revisionResolver.revisionOf(type));
    }

    @Override
    public ConverterFactory getConverterFactory() {
        return converterFactory;
    }
}
TOP

Related Classes of org.axonframework.serializer.json.JacksonSerializer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.