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

co.paralleluniverse.io.serialization.kryo.ReplaceableObjectKryo Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU Lesser General Public License version 3.0
 * as published by the Free Software Foundation.
 */
package co.paralleluniverse.io.serialization.kryo;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Registration;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.FieldSerializer;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * A subclass of {@link Kryo} that respects {@link Serializable java.io.Serializable}'s {@code writeReplace} and {@code readResolve}.
 *
 * @author pron
 */
public class ReplaceableObjectKryo extends Kryo {
    private static final ClassValue replaceMethodsCache = new ClassValue() {
        @Override
        protected SerializationMethods computeValue(Class type) {
            return new SerializationMethods(getMethodByReflection(type, WRITE_REPLACE),
                    getMethodByReflection(type, READ_RESOLVE));
        }
    };
    private static final String WRITE_REPLACE = "writeReplace";
    private static final String READ_RESOLVE = "readResolve";

    @Override
    public void writeClassAndObject(Output output, Object object) {
        if (output == null)
            throw new IllegalArgumentException("output cannot be null.");
        if (object == null) {
            super.writeClass(output, null);
            return;
        }
        Object newObj = getReplacement(getMethods(object.getClass()).writeReplace, object);
        setAutoReset(false);
        Registration registration = super.writeClass(output, newObj.getClass());
        setAutoReset(true);
        super.writeObject(output, newObj, registration.getSerializer());
//        System.out.println("wrote an object "+newObj+" id "+registration.getId());
//        reset();
    }

    public ReplaceableObjectKryo() {
    }

    @Override
    protected Serializer newDefaultSerializer(Class type) {
        final Serializer s = super.newDefaultSerializer(type);
        if (s instanceof FieldSerializer)
            ((FieldSerializer) s).setIgnoreSyntheticFields(false);
        return s;
    }

    @Override
    public Registration writeClass(Output output, Class type) {
        if (type == null || getMethods(type).writeReplace == null)
            return super.writeClass(output, type);
        return super.getRegistration(type); // do nothing. write object will write the class too
    }

    @Override
    public void writeObject(Output output, Object object, Serializer serializer) {
        Method m = getMethods(object.getClass()).writeReplace;
        if (m != null) {
            object = getReplacement(m, object);
            Registration reg = super.writeClass(output, object.getClass());
            serializer = reg.getSerializer();
        }
        super.writeObject(output, object, serializer);
//        System.out.println("wrote2 an object "+object+" id "+getRegistration(object.getClass()).getId());

    }

    @Override
    public  T readObject(Input input, Class type, Serializer serializer) {
        return readReplace(super.readObject(input, type, serializer));
    }

    @Override
    public  T readObject(Input input, Class type) {
        return readReplace(super.readObject(input, type));
    }

    @Override
    public  T readObjectOrNull(Input input, Class type) {
        return readReplace(super.readObjectOrNull(input, type));
    }

    @Override
    public  T readObjectOrNull(Input input, Class type, Serializer serializer) {
        return readReplace(super.readObjectOrNull(input, type, serializer));
    }

    @Override
    public Object readClassAndObject(Input input) {
        return readReplace(super.readClassAndObject(input));
    }

    private  T readReplace(Object obj) {
        if (obj == null)
            return null;
        return (T) getReplacement(getMethods(obj.getClass()).readResolve, obj);
    }

    private static Object getReplacement(Method m, Object object) {
        if (m == null)
            return object;
        try {
            return m.invoke(object);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getCause()); // Exceptions.rethrow(e.getCause());
        }
    }

    private static SerializationMethods getMethods(Class clazz) {
        return replaceMethodsCache.get(clazz);
    }

    private static Method getMethodByReflection(Class clazz, final String methodName, Class... paramTypes) throws SecurityException {
        if (!Serializable.class.isAssignableFrom(clazz))
            return null;

        Method m = null;
        try {
            m = clazz.getDeclaredMethod(methodName, paramTypes);
        } catch (NoSuchMethodException ex) {
            Class ancestor = clazz.getSuperclass();
            while (ancestor != null) {
                if (!Serializable.class.isAssignableFrom(ancestor))
                    return null;
                try {
                    m = ancestor.getDeclaredMethod(methodName, paramTypes);
                    if (!Modifier.isPublic(m.getModifiers()) && !Modifier.isProtected(m.getModifiers()))
                        return null;
                    break;
                } catch (NoSuchMethodException ex1) {
                    ancestor = ancestor.getSuperclass();
                }
            }
        }
        if (m != null)
            m.setAccessible(true);
        return m;
    }

    private static class SerializationMethods {
        Method writeReplace;
        Method readResolve;

        public SerializationMethods(Method writeReplace, Method readResolve) {
            this.writeReplace = writeReplace;
            this.readResolve = readResolve;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy