Package com.esotericsoftware.kryo

Source Code of com.esotericsoftware.kryo.Kryo

package com.esotericsoftware.kryo;

import static com.esotericsoftware.minlog.Log.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import com.esotericsoftware.kryo.serialize.ArraySerializer;
import com.esotericsoftware.kryo.serialize.BooleanSerializer;
import com.esotericsoftware.kryo.serialize.ByteSerializer;
import com.esotericsoftware.kryo.serialize.CharSerializer;
import com.esotericsoftware.kryo.serialize.CollectionSerializer;
import com.esotericsoftware.kryo.serialize.CustomSerializer;
import com.esotericsoftware.kryo.serialize.DoubleSerializer;
import com.esotericsoftware.kryo.serialize.EnumSerializer;
import com.esotericsoftware.kryo.serialize.FieldSerializer;
import com.esotericsoftware.kryo.serialize.FloatSerializer;
import com.esotericsoftware.kryo.serialize.IntSerializer;
import com.esotericsoftware.kryo.serialize.LongSerializer;
import com.esotericsoftware.kryo.serialize.MapSerializer;
import com.esotericsoftware.kryo.serialize.ShortSerializer;
import com.esotericsoftware.kryo.serialize.StringSerializer;
import com.esotericsoftware.kryo.util.IntHashMap;

/**
* Maps classes to serializers so object graphs can be serialized automatically.
* @author Nathan Sweet <misc@n4te.com>
*/
public class Kryo {
  static public final String version = "1.03a";

  static private final byte ID_NULL_OBJECT = 0;
  static private final int ID_CLASS_NAME = 16383;

  static private ThreadLocal<Context> contextThreadLocal = new ThreadLocal<Context>() {
    protected Context initialValue () {
      return new Context();
    }
  };

  private final IntHashMap<RegisteredClass> idToRegisteredClass = new IntHashMap(64);
  private final HashMap<Class, RegisteredClass> classToRegisteredClass = new HashMap(64);
  private AtomicInteger nextClassID = new AtomicInteger(1);
  private Object listenerLock = new Object();
  private Listener[] listeners = {};
  private boolean registrationOptional;
  private ClassLoader classLoader = getClass().getClassLoader();

  private final CustomSerializer customSerializer = new CustomSerializer(this);
  private final ArraySerializer arraySerializer = new ArraySerializer(this);
  private final CollectionSerializer collectionSerializer = new CollectionSerializer(this);
  private final MapSerializer mapSerializer = new MapSerializer(this);

  public Kryo () {
    Serializer serializer;
    // Primitives.
    register(boolean.class, new BooleanSerializer());
    register(byte.class, new ByteSerializer());
    register(char.class, new CharSerializer());
    register(short.class, new ShortSerializer());
    register(int.class, new IntSerializer());
    register(long.class, new LongSerializer());
    register(float.class, new FloatSerializer());
    register(double.class, new DoubleSerializer());
    // Primitive wrappers.
    register(Boolean.class, new BooleanSerializer());
    register(Byte.class, new ByteSerializer());
    register(Character.class, new CharSerializer());
    register(Short.class, new ShortSerializer());
    register(Integer.class, new IntSerializer());
    register(Long.class, new LongSerializer());
    register(Float.class, new FloatSerializer());
    register(Double.class, new DoubleSerializer());
    // Other.
    register(String.class, new StringSerializer());
  }

  /**
   * When true, classes that have not been {@link #register(Class, Serializer) registered} will not throw an exception. Instead,
   * {@link #handleUnregisteredClass(Class)} will be called. Default is false.
   */
  public void setRegistrationOptional (boolean registrationOptional) {
    this.registrationOptional = registrationOptional;
  }

  /**
   * Registers a class for serialization.
   * <p>
   * If <tt>useClassNameString</tt> is true, the first time an object of the specified type is encountered, the class name String
   * will be written to the serialized bytes. Each appearance in the graph after the first is stored as an integer ordinal.
   * <p>
   * If <tt>useClassNameString</tt> is false, the class is assigned an ordinal which will be written to the serialized bytes for
   * objects of the specified type. This is more efficient than using the class name String, but has the drawback that the exact
   * same classes must be registered in exactly the same order when the class is deserialized.
   * <p>
   * By default, primitive types, primitive wrappers, and java.lang.String are registered. All other classes must be registered
   * before they can be serialized. Note that JDK classes such as ArrayList, HashMap, etc and even array classes such as
   * "int[].class" or "short[][].class" must be registered. {@link #setRegistrationOptional(boolean) Optional registration} can
   * be enabled to handle unregistered classes as they are encountered.
   * <p>
   * The {@link Serializer} specified will be used to serialize and deserialize objects of the specified type. Note that a
   * serializer can be wrapped with a {@link Compressor} for compression and/or encoding.
   * <p>
   * If the class is already registered, the serializer will be changed.
   * @see #register(Class)
   */
  public RegisteredClass register (Class type, Serializer serializer, boolean useClassNameString) {
    if (type == null) throw new IllegalArgumentException("type cannot be null.");
    if (serializer == null) throw new IllegalArgumentException("serializer cannot be null.");
    if (type.isPrimitive()) serializer.setCanBeNull(false);

    int id;
    RegisteredClass existingRegisteredClass = classToRegisteredClass.get(type);
    if (useClassNameString)
      id = ID_CLASS_NAME;
    else if (existingRegisteredClass != null)
      id = existingRegisteredClass.id;
    else {
      id = nextClassID.getAndIncrement();
      if (id == ID_CLASS_NAME) id = nextClassID.getAndIncrement();
    }

    RegisteredClass registeredClass = new RegisteredClass(type, id, serializer);
    if (!useClassNameString) idToRegisteredClass.put(id, registeredClass);
    classToRegisteredClass.put(type, registeredClass);

    if (TRACE && id > 17) {
      String name = type.getName();
      if (type.isArray()) {
        Class elementClass = ArraySerializer.getElementClass(type);
        StringBuilder buffer = new StringBuilder(16);
        for (int i = 0, n = ArraySerializer.getDimensionCount(type); i < n; i++)
          buffer.append("[]");
        name = elementClass.getName() + buffer;
      }
      if (useClassNameString)
        trace("kryo", "Registered class name: " + name + " (" + serializer.getClass().getName() + ")");
      else
        trace("kryo", "Registered class ID " + id + ": " + name + " (" + serializer.getClass().getName() + ")");
    }
    return registeredClass;
  }

  /**
   * Registers a class with an ordinal.
   * @see #register(Class, Serializer, boolean)
   */
  public RegisteredClass register (Class type, Serializer serializer) {
    return register(type, serializer, false);
  }

  /**
   * Registers a class with an ordinal, automatically determining the serializer to use. The serializer returned by
   * {@link #newSerializer(Class)} is used.
   * <p>
   * Note that some serializers allow additional information to be specified to make serialization more efficient in some cases
   * (eg, {@link ArraySerializer#setElementsCanBeNull(boolean)}). To use these features, call
   * {@link #register(Class, Serializer)} with the configured serializer.
   * @see #register(Class, Serializer, boolean)
   */
  public RegisteredClass register (Class type) {
    if (type == null) throw new IllegalArgumentException("type cannot be null.");
    RegisteredClass existingRegisteredClass = classToRegisteredClass.get(type);
    if (existingRegisteredClass != null && existingRegisteredClass.id >= 1 && existingRegisteredClass.id <= 17)
      throw new IllegalArgumentException("Class is registered by default: " + type.getName());
    return register(type, newSerializer(type));
  }

  /**
   * Registers a class with the ordinal of the specified registered class. This is useful when many classes can be serialized
   * with the same serializer instance, such as when code generation is being used to wrap the actual class being serialized.
   */
  public void register (Class type, RegisteredClass registeredClass) {
    if (type == null) throw new IllegalArgumentException("type cannot be null.");
    if (registeredClass == null) throw new IllegalArgumentException("registeredClass cannot be null.");
    classToRegisteredClass.put(type, registeredClass);
    if (TRACE) {
      String name = type.getName();
      if (type.isArray()) {
        Class elementClass = ArraySerializer.getElementClass(type);
        StringBuilder buffer = new StringBuilder(16);
        for (int i = 0, n = ArraySerializer.getDimensionCount(type); i < n; i++)
          buffer.append("[]");
        name = elementClass.getName() + buffer;
      }
      if (registeredClass.id == ID_CLASS_NAME)
        trace("kryo", "Registered class name: " + name + " (" + registeredClass.serializer.getClass().getName() + ")");
      else
        trace("kryo", "Registered class ID " + registeredClass.id + ": " + name + " ("
          + registeredClass.serializer.getClass().getName() + ")");
    }
  }

  /**
   * Returns a serializer for the specified type, determined according to this table:
   * <p>
   * <table>
   * <tr>
   * <th>Type</th>
   * <th>Serializer</th>
   * </tr>
   * <tr>
   * <td>array (any number of dimensions)</td>
   * <td>{@link ArraySerializer}</td>
   * </tr>
   * <tr>
   * <td>{@link Enum}</td>
   * <td>{@link EnumSerializer}</td>
   * </tr>
   * <tr>
   * <td>{@link Collection}</td>
   * <td>{@link CollectionSerializer}</td>
   * </tr>
   * <tr>
   * <td>{@link Map}</td>
   * <td>{@link MapSerializer}</td>
   * </tr>
   * <tr>
   * <td>{@link CustomSerialization}</td>
   * <td>{@link CustomSerializer}</td>
   * </tr>
   * <tr>
   * <td>class with {@link DefaultSerializer} annotation</td>
   * <td>serializer specified in annotiation</td>
   * </tr>
   * <tr>
   * <td>any other class</td>
   * <td>serializer returned by {@link #newDefaultSerializer(Class)}</td>
   * </tr>
   * </table>
   * @see #register(Class)
   */
  public Serializer newSerializer (Class type) {
    if (type == null) throw new IllegalArgumentException("type cannot be null.");
    Serializer serializer;
    if (type.isArray()) return arraySerializer;
    if (CustomSerialization.class.isAssignableFrom(type)) return customSerializer;
    if (Collection.class.isAssignableFrom(type)) return collectionSerializer;
    if (Map.class.isAssignableFrom(type)) return mapSerializer;
    if (Enum.class.isAssignableFrom(type)) return new EnumSerializer(type);
    if (type.isAnnotationPresent(DefaultSerializer.class)) {
      Class<? extends Serializer> serializerClass = ((DefaultSerializer)type.getAnnotation(DefaultSerializer.class)).value();
      try {
        try {
          return serializerClass.getConstructor(Kryo.class, Class.class).newInstance(this, type);
        } catch (NoSuchMethodException ex1) {
          try {
            return serializerClass.getConstructor(Kryo.class).newInstance(this);
          } catch (NoSuchMethodException ex2) {
            try {
              return serializerClass.getConstructor(Class.class).newInstance(this, type);
            } catch (NoSuchMethodException ex3) {
              return serializerClass.newInstance();
            }
          }
        }
      } catch (Exception ex) {
        throw new IllegalArgumentException("Unable to create serializer \"" + serializerClass.getName() + "\" for class: "
          + type.getName(), ex);
      }
    }
    return newDefaultSerializer(type);
  }

  /**
   * Called by {@link #newSerializer(Class)} when a serializer could not otherwise be determined. The default implementation
   * returns a new {@link FieldSerializer}.
   */
  protected Serializer newDefaultSerializer (Class type) {
    return new FieldSerializer(this, type);
  }

  /**
   * Returns the registration information for the specified class. If {@link #setRegistrationOptional(boolean) optional
   * registration} is true, {@link #handleUnregisteredClass(Class)} will be called if the class is not registered. Otherwise
   * IllegalArgumentException is thrown.
   */
  public RegisteredClass getRegisteredClass (Class type) {
    if (type == null) throw new IllegalArgumentException("type cannot be null.");
    RegisteredClass registeredClass = classToRegisteredClass.get(type);
    if (registeredClass != null) return registeredClass;

    // If a Proxy class, treat it like an InvocationHandler because the concrete class for a proxy is generated.
    if (Proxy.isProxyClass(type)) return getRegisteredClass(InvocationHandler.class);

    if (registrationOptional) {
      handleUnregisteredClass(type);
      registeredClass = classToRegisteredClass.get(type);
      if (registeredClass != null) return registeredClass;
    }

    // Failed to find registered class.
    if (type.isArray()) {
      Class elementClass = ArraySerializer.getElementClass(type);
      StringBuilder buffer = new StringBuilder(16);
      for (int i = 0, n = ArraySerializer.getDimensionCount(type); i < n; i++)
        buffer.append("[]");
      throw new IllegalArgumentException("Class is not registered: " + type.getName()
        + "\nNote: To register this class use: kryo.register(" + elementClass.getName() + buffer + ".class);");
    }
    throw new IllegalArgumentException("Class is not registered: " + type.getName());
  }

  /**
   * If {@link #setRegistrationOptional(boolean) optional registration} is true, this method is called the first time an
   * unregistered class is encountered. The default implementation registers the class to use the class name String in the
   * serialized bytes.
   * @see #register(Class, Serializer, boolean)
   */
  protected void handleUnregisteredClass (Class type) {
    register(type, newSerializer(type), true);
  }

  public RegisteredClass getRegisteredClass (int classID) {
    RegisteredClass registeredClass = idToRegisteredClass.get(classID);
    if (registeredClass == null) throw new IllegalArgumentException("Class ID is not registered: " + classID);
    return registeredClass;
  }

  /**
   * Sets the class loader used to resolve class names when class name Strings are encountered in the serialized bytes.
   */
  public void setClassLoader (ClassLoader classLoader) {
    this.classLoader = classLoader;
  }

  public ClassLoader getClassLoader () {
    return classLoader;
  }

  public Serializer getSerializer (Class type) {
    return getRegisteredClass(type).serializer;
  }

  public void setSerializer (Class type, Serializer serializer) {
    getRegisteredClass(type).serializer = serializer;
  }

  /**
   * Writes the specified class to the buffer. Either a String or an int will be written, depending on how the class was
   * {@link #register(Class, Serializer, boolean) registered}.
   * @param type Can be null (writes a special ID for a null object).
   * @return The registered information for the class that was written, or null of the specified class was null.
   */
  public RegisteredClass writeClass (ByteBuffer buffer, Class type) {
    if (type == null) {
      try {
        buffer.put(ID_NULL_OBJECT);
        if (TRACE) trace("kryo", "Wrote object: null");
        return null;
      } catch (BufferOverflowException ex) {
        throw new SerializationException("Buffer limit exceeded writing null object.", ex);
      }
    }
    try {
      RegisteredClass registeredClass = getRegisteredClass(type);
      IntSerializer.put(buffer, registeredClass.id, true);
      if (registeredClass.id == ID_CLASS_NAME) {
        Context context = Kryo.getContext();
        ClassReferences references = (ClassReferences)context.getTemp("classReferences");
        if (references == null) {
          // Use non-temporary storage to avoid repeated allocation.
          references = (ClassReferences)context.get("classReferences");
          if (references == null)
            context.put("classReferences", references = new ClassReferences());
          else
            references.reset();
          context.putTemp("classReferences", references);
        }
        Integer reference = references.classToReference.get(type);
        if (reference != null) {
          IntSerializer.put(buffer, reference, true);
          if (TRACE) trace("kryo", "Wrote class name reference " + reference + ": " + type.getName());
          return registeredClass;
        }
        buffer.put((byte)0);
        references.classToReference.put(type, references.referenceCount++);
        StringSerializer.put(buffer, type.getName());
        if (TRACE) trace("kryo", "Wrote class name: " + type.getName());
      } else {
        if (TRACE) trace("kryo", "Wrote class " + registeredClass.id + ": " + type.getName());
      }
      return registeredClass;
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferOverflowException.class))
        throw new SerializationException("Buffer limit exceeded writing class ID: " + type, ex);
      throw ex;
    } catch (BufferOverflowException ex) {
      throw new SerializationException("Buffer limit exceeded writing class ID: " + type, ex);
    }
  }

  /**
   * Reads the class from the buffer.
   * @return The registered information for the class that was read, or null if the data read from the buffer represented a null
   *         object.
   */
  public RegisteredClass readClass (ByteBuffer buffer) {
    int classID;
    try {
      classID = IntSerializer.get(buffer, true);
      if (classID == ID_NULL_OBJECT) {
        if (TRACE) trace("kryo", "Read object: null");
        return null;
      }
      if (classID == ID_CLASS_NAME) {
        Context context = Kryo.getContext();
        ClassReferences references = (ClassReferences)context.getTemp("classReferences");
        if (references == null) {
          // Use non-temporary storage to avoid repeated allocation.
          references = (ClassReferences)context.get("classReferences");
          if (references == null)
            context.put("classReferences", references = new ClassReferences());
          else
            references.reset();
          context.putTemp("classReferences", references);
        }

        Class type;
        int reference = IntSerializer.get(buffer, true);
        if (reference != 0) {
          type = (Class)references.referenceToClass.get(reference);
          if (type == null) throw new SerializationException("Invalid class name reference: " + reference);
          if (TRACE) trace("kryo", "Read class name reference " + reference + ": " + type.getName());
        } else {
          String className = StringSerializer.get(buffer);
          if (TRACE) trace("kryo", "Read class name: " + className);
          try {
            type = Class.forName(className, false, classLoader);
          } catch (ClassNotFoundException ex) {
            throw new SerializationException("Unable to find class: " + className, ex);
          }
          references.referenceToClass.put(references.referenceCount++, type);
        }
        return getRegisteredClass(type);
      }
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferUnderflowException.class))
        throw new SerializationException("Buffer limit exceeded reading class ID.", ex);
      throw ex;
    } catch (BufferUnderflowException ex) {
      throw new SerializationException("Buffer limit exceeded reading class ID.", ex);
    }
    RegisteredClass registeredClass = idToRegisteredClass.get(classID);
    if (registeredClass == null) throw new SerializationException("Encountered unregistered class ID: " + classID);
    if (TRACE) trace("kryo", "Read class " + classID + ": " + registeredClass.type.getName());
    return registeredClass;
  }

  /**
   * Writes the object's class to the buffer, then uses the serializer registered for that class to write the object to the
   * buffer.
   * @param object Can be null (writes a special ID for a null object instead).
   * @throws SerializationException if an error occurred during serialization.
   */
  public void writeClassAndObject (ByteBuffer buffer, Object object) {
    if (object == null) {
      try {
        buffer.put(ID_NULL_OBJECT);
        if (TRACE) trace("kryo", "Wrote object: null");
        return;
      } catch (BufferOverflowException ex) {
        throw new SerializationException("Buffer limit exceeded writing null object.", ex);
      }
    }
    RegisteredClass registeredClass = writeClass(buffer, object.getClass());
    if (registeredClass == null) return;
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      registeredClass.serializer.writeObjectData(buffer, object);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferOverflowException.class))
        throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
      throw new SerializationException("Unable to serialize object of type: " + object.getClass().getName(), ex);
    } catch (BufferOverflowException ex) {
      throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  /**
   * Uses the serializer registered for the object's class to write the object to the buffer.
   * @param object Can be null (writes a special ID for a null object instead).
   * @throws SerializationException if an error occurred during serialization.
   */
  public void writeObject (ByteBuffer buffer, Object object) {
    if (object == null) {
      try {
        buffer.put(ID_NULL_OBJECT);
        if (TRACE) trace("kryo", "Wrote object: null");
        return;
      } catch (BufferOverflowException ex) {
        throw new SerializationException("Buffer limit exceeded writing null object.", ex);
      }
    }
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      getRegisteredClass(object.getClass()).serializer.writeObject(buffer, object);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferOverflowException.class))
        throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
      throw new SerializationException("Unable to serialize object of type: " + object.getClass().getName(), ex);
    } catch (BufferOverflowException ex) {
      throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  /**
   * Uses the serializer registered for the object's class to write the object to the buffer.
   * @param object Cannot be null.
   * @throws SerializationException if an error occurred during serialization.
   */
  public void writeObjectData (ByteBuffer buffer, Object object) {
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      getRegisteredClass(object.getClass()).serializer.writeObjectData(buffer, object);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferOverflowException.class))
        throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
      throw new SerializationException("Unable to serialize object of type: " + object.getClass().getName(), ex);
    } catch (BufferOverflowException ex) {
      throw new SerializationException("Buffer limit exceeded writing object of type: " + object.getClass().getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  /**
   * Reads a class from the buffer and uses the serializer registered for that class to read an object from the buffer.
   * @return The deserialized object, or null if the object read from the buffer was null.
   * @throws SerializationException if an error occurred during deserialization.
   */
  public Object readClassAndObject (ByteBuffer buffer) {
    RegisteredClass registeredClass = readClass(buffer);
    if (registeredClass == null) return null;
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      return registeredClass.serializer.readObjectData(buffer, registeredClass.type);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferUnderflowException.class))
        throw new SerializationException("Buffer limit exceeded reading object of type: " + registeredClass.type.getName(),
          ex);
      throw new SerializationException("Unable to deserialize object of type: " + registeredClass.type.getName(), ex);
    } catch (BufferUnderflowException ex) {
      throw new SerializationException("Buffer limit exceeded reading object of type: " + registeredClass.type.getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  /**
   * Uses the serializer registered for the specified class to read an object from the buffer.
   * @return The deserialized object, or null if the object read from the buffer was null.
   * @throws SerializationException if an error occurred during deserialization.
   */
  public <T> T readObject (ByteBuffer buffer, Class<T> type) {
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      return getRegisteredClass(type).serializer.readObject(buffer, type);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferUnderflowException.class))
        throw new SerializationException("Buffer limit exceeded reading object of type: " + type.getName(), ex);
      throw new SerializationException("Unable to deserialize object of type: " + type.getName(), ex);
    } catch (BufferUnderflowException ex) {
      throw new SerializationException("Buffer limit exceeded reading object of type: " + type.getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  /**
   * Uses the serializer registered for the specified class to read an object from the buffer.
   * @return The deserialized object, never null.
   * @throws SerializationException if an error occurred during deserialization.
   */
  public <T> T readObjectData (ByteBuffer buffer, Class<T> type) {
    Context context = getContext();
    context.objectGraphLevel++;
    try {
      return getRegisteredClass(type).serializer.readObjectData(buffer, type);
    } catch (SerializationException ex) {
      if (ex.causedBy(BufferUnderflowException.class))
        throw new SerializationException("Buffer limit exceeded reading object of type: " + type.getName(), ex);
      throw new SerializationException("Unable to deserialize object of type: " + type.getName(), ex);
    } catch (BufferUnderflowException ex) {
      throw new SerializationException("Buffer limit exceeded reading object of type: " + type.getName(), ex);
    } finally {
      context.objectGraphLevel--;
      if (context.objectGraphLevel == 0) context.reset();
    }
  }

  public void addListener (Listener listener) {
    if (listener == null) throw new IllegalArgumentException("listener cannot be null.");
    synchronized (listenerLock) {
      Listener[] listeners = this.listeners;
      int n = listeners.length;
      for (int i = 0; i < n; i++)
        if (listener == listeners[i]) return;
      Listener[] newListeners = new Listener[n + 1];
      newListeners[0] = listener;
      System.arraycopy(listeners, 0, newListeners, 1, n);
      this.listeners = newListeners;
    }
    if (TRACE) trace("kryo", "Kryo listener added: " + listener.getClass().getName());
  }

  public void removeListener (Listener listener) {
    if (listener == null) throw new IllegalArgumentException("listener cannot be null.");
    synchronized (listenerLock) {
      Listener[] listeners = this.listeners;
      int n = listeners.length;
      if (n == 0) return;
      Listener[] newListeners = new Listener[n - 1];
      for (int i = 0, ii = 0; i < n; i++) {
        if (listener == listeners[i]) continue;
        if (ii == n - 1) return;
        newListeners[ii++] = listener;
      }
      System.arraycopy(listeners, 0, newListeners, 1, n);
      this.listeners = newListeners;
    }
    if (TRACE) trace("kryo", "Kryo listener removed: " + listener.getClass().getName());
  }

  /**
   * Notifies all listeners that the remote entity with the specified ID will no longer be available.
   * @see Context#getRemoteEntityID()
   * @see #addListener(Listener)
   */
  public void removeRemoteEntity (int remoteEntityID) {
    Listener[] listeners = this.listeners;
    if (TRACE) trace("kryo", "Remote ID removed: " + remoteEntityID);
    for (int i = 0, n = listeners.length; i < n; i++)
      listeners[i].remoteEntityRemoved(remoteEntityID);
  }

  /**
   * Returns an instance of the specified class. Serializers that want to allow object construction to be customized by a
   * subclass should use {@link Serializer#newInstance(Kryo, Class)} instead of calling this method directly.
   * @throws SerializationException if the class could not be constructed.
   */
  public <T> T newInstance (Class<T> type) {
    try {
      return type.newInstance();
    } catch (Exception ex) {
      try {
        // Try a private constructor.
        Constructor<T> constructor = type.getDeclaredConstructor();
        constructor.setAccessible(true);
        return constructor.newInstance();
      } catch (SecurityException ignored) {
      } catch (NoSuchMethodException ignored) {
        throw new SerializationException("Class cannot be created (missing no-arg constructor): " + type.getName(), ex);
      } catch (Exception privateConstructorException) {
        ex = privateConstructorException;
      }
      throw new SerializationException("Error constructing instance of class: " + type.getName(), ex);
    }
  }

  /**
   * Returns the thread local context for serialization and deserialization.
   * @see Context
   */
  static public Context getContext () {
    return contextThreadLocal.get();
  }

  /**
   * Holds the registration information for a class.
   */
  static public class RegisteredClass {
    final Class type;
    final int id;
    Serializer serializer;

    RegisteredClass (Class type, int id, Serializer serializer) {
      this.type = type;
      this.id = id;
      this.serializer = serializer;
    }

    public Class getType () {
      return type;
    }

    public Serializer getSerializer () {
      return serializer;
    }

    public void setSerializer (Serializer serializer) {
      this.serializer = serializer;
    }

    public int getID () {
      return id;
    }
  }

  /**
   * Provides notification of {@link Kryo} events.
   */
  static public interface Listener {
    /**
     * Called when a remote entity is no longer available. This allows, for example, a context to release any resources it may
     * be storing for the entity.
     * @see Context#getRemoteEntityID()
     * @see Kryo#removeListener(Listener)
     */
    public void remoteEntityRemoved (int id);
  }

  static class ClassReferences {
    public HashMap<Class, Integer> classToReference = new HashMap();
    public IntHashMap referenceToClass = new IntHashMap();
    public int referenceCount = 1;

    public void reset () {
      classToReference.clear();
      referenceToClass.clear();
      referenceCount = 1;
    }
  }
}
TOP

Related Classes of com.esotericsoftware.kryo.Kryo

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.