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

io.atomix.catalyst.serializer.Serializer Maven / Gradle / Ivy

/*
 * Copyright 2015 the original author or authors.
 *
 * 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 io.atomix.catalyst.serializer;

import io.atomix.catalyst.buffer.Buffer;
import io.atomix.catalyst.buffer.BufferAllocator;
import io.atomix.catalyst.buffer.BufferInput;
import io.atomix.catalyst.buffer.BufferOutput;
import io.atomix.catalyst.buffer.InputStreamBufferInput;
import io.atomix.catalyst.buffer.OutputStreamBufferOutput;
import io.atomix.catalyst.buffer.PooledAllocator;
import io.atomix.catalyst.buffer.UnpooledHeapAllocator;
import io.atomix.catalyst.serializer.buffer.*;
import io.atomix.catalyst.serializer.util.PooledTypeSerializer;
import io.atomix.catalyst.util.LRUCache;
import io.atomix.catalyst.util.reference.ReferenceCounted;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Catalyst serializer.
 * 

* This class provides an interface for efficient serialization of Java objects. Serialization is performed by * {@link TypeSerializer} instances. Objects that can be serialized by {@link Serializer} must be registered. When objects * are serialized, Catalyst will write the object's type as an 16-bit unsigned integer. When reading objects, the * 16-bit identifier is used to construct a new object. *

* Serializable objects must either provide a {@link TypeSerializer}. implement {@link CatalystSerializable}, or implement * {@link java.io.Externalizable}. For efficiency, serializable objects may implement {@link ReferenceCounted} * or provide a {@link PooledTypeSerializer} that reuses objects during deserialization. * Catalyst will automatically deserialize {@link ReferenceCounted} types using an object pool. *

* Serialization via this class is not thread safe. * * @author Jordan Halterman */ public class Serializer { private final SerializerRegistry registry; private final Map, TypeSerializer> serializers = new HashMap<>(); private final Map, Integer> ids = new HashMap<>(); private final Map> types = new HashMap<>(); private final Map classLoaders = new HashMap<>(); private final Map, Optional>> enclosingClasses = new LRUCache<>(1024); private final BufferAllocator allocator; private final AtomicBoolean whitelistRequired; /** * Creates a new serializer instance with a default {@link UnpooledHeapAllocator}. *

* Catalyst will use a {@link UnpooledHeapAllocator} to allocate buffers for serialization. * Users can explicitly allocate buffers with the heap allocator via {@link Serializer#allocate(long)}. *

*

   *   {@code
   *      Serializer serializer = new Serializer(new PooledHeapAllocator());
   *      Buffer buffer = serializer.allocate(1024);
   *   }
   * 
*/ @SuppressWarnings("unchecked") public Serializer() { this(new UnpooledHeapAllocator(), Collections.EMPTY_LIST); } /** * Creates a new serializer instance with a buffer allocator. *

* The given {@link BufferAllocator} will be used to allocate buffers during serialization. * Users can explicitly allocate buffers with the given allocator via {@link Serializer#allocate(long)}. *

*

   *   {@code
   *      Serializer serializer = new Serializer(new PooledHeapAllocator());
   *      Buffer buffer = serializer.allocate(1024);
   *   }
   * 
*

* If a {@link PooledAllocator} is used, users must be careful to release buffers back to the * pool by calling {@link Buffer#release()} or {@link Buffer#close()}. * * @param allocator The serializer buffer allocator. */ @SuppressWarnings("unchecked") public Serializer(BufferAllocator allocator) { this(allocator, Collections.EMPTY_LIST); } /** * Creates a new serializer instance with a default {@link UnpooledHeapAllocator}. *

* The given {@link SerializableTypeResolver}s will be used to locate serializable types on the * classpath. By default, the {@link PrimitiveTypeResolver} and {@link JdkTypeResolver} * will be used to register common serializable types, and any additional types will be registered via provided type * resolvers thereafter. * * @param resolvers A collection of serializable type resolvers with which to register serializable types. */ public Serializer(SerializableTypeResolver... resolvers) { this(new UnpooledHeapAllocator(), resolvers); } /** * Creates a new serializer instance with a default {@link UnpooledHeapAllocator}. *

* The given {@link SerializableTypeResolver}s will be used to locate serializable types on the * classpath. By default, the {@link PrimitiveTypeResolver} and {@link JdkTypeResolver} * will be used to register common serializable types, and any additional types will be registered via provided type * resolvers thereafter. * * @param resolvers A collection of serializable type resolvers with which to register serializable types. */ public Serializer(Collection resolvers) { this(new UnpooledHeapAllocator(), resolvers); } /** * Creates a new serializer instance with a buffer allocator and type resolver(s). *

* The given {@link BufferAllocator} will be used to allocate buffers during serialization. * Users can explicitly allocate buffers with the given allocator via {@link Serializer#allocate(long)}. *

*

   *   {@code
   *      Serializer serializer = new Serializer(new PooledHeapAllocator());
   *      Buffer buffer = serializer.allocate(1024);
   *   }
   * 
*

* If a {@link PooledAllocator} is used, users must be careful to release buffers back to the * pool by calling {@link Buffer#release()} or {@link Buffer#close()}. *

* The given {@link SerializableTypeResolver}s will be used to locate serializable types on the * classpath. By default, the {@link PrimitiveTypeResolver} and {@link JdkTypeResolver} * will be used to register common serializable types, and any additional types will be registered via provided type * resolvers thereafter. * * @param allocator The serializer buffer allocator. * @param resolvers A collection of serializable type resolvers with which to register serializable types. */ @SuppressWarnings("unchecked") public Serializer(BufferAllocator allocator, SerializableTypeResolver... resolvers) { this(allocator, resolvers != null ? Arrays.asList(resolvers) : Collections.EMPTY_LIST); } /** * Creates a new serializer instance with a buffer allocator and type resolver(s). *

* The given {@link BufferAllocator} will be used to allocate buffers during serialization. * Users can explicitly allocate buffers with the given allocator via {@link Serializer#allocate(long)}. *

*

   *   {@code
   *      Serializer serializer = new Serializer(new PooledHeapAllocator());
   *      Buffer buffer = serializer.allocate(1024);
   *   }
   * 
*

* If a {@link PooledAllocator} is used, users must be careful to release buffers back to the * pool by calling {@link Buffer#release()} or {@link Buffer#close()}. *

* The given {@link SerializableTypeResolver}s will be used to locate serializable types on the * classpath. By default, the {@link PrimitiveTypeResolver} and {@link JdkTypeResolver} * will be used to register common serializable types, and any additional types will be registered via provided type * resolvers thereafter. * * @param allocator The serializer buffer allocator. * @param resolvers A collection of serializable type resolvers with which to register serializable types. */ public Serializer(BufferAllocator allocator, Collection resolvers) { if (allocator == null) throw new NullPointerException("allocator cannot be null"); this.allocator = allocator; this.whitelistRequired = new AtomicBoolean(false); registry = new SerializerRegistry(resolvers); } /** * Creates a new serializer instance with the given properties. * * @param properties The properties with which to configure the serializer. */ public Serializer(Properties properties) { this(new SerializerOptions(properties)); } @SuppressWarnings("unchecked") private Serializer(SerializerOptions properties) { this.allocator = properties.allocator(); this.whitelistRequired = new AtomicBoolean(properties.whitelist()); this.registry = new SerializerRegistry(); Map> types = properties.types(); Map> serializers = properties.serializers(); Map> abstractSerializers = properties.abstractSerializers(); Map> defaultSerializers = properties.defaultSerializers(); // Register default serializers. for (Map.Entry> entry : defaultSerializers.entrySet()) { registry.registerDefault(types.get(entry.getKey()), (Class) entry.getValue()); } // Register serializable types. for (Map.Entry> entry : types.entrySet()) { // Register a concrete type serializer. Class serializer = serializers.get(entry.getKey()); if (serializer != null) { registry.register(entry.getValue(), entry.getKey(), (Class) serializer); } // Register an abstract type serializer. serializer = abstractSerializers.get(entry.getKey()); if (serializer != null) { registry.registerAbstract(entry.getValue(), entry.getKey(), (Class) serializer); } else { registry.register(entry.getValue(), entry.getKey()); } } } private Serializer(SerializerRegistry registry, BufferAllocator allocator, AtomicBoolean whitelistRequired, Map classLoaders) { this.registry = registry; this.allocator = allocator; this.whitelistRequired = whitelistRequired; this.classLoaders.putAll(classLoaders); } /** * Enables whitelisting for serializable types. *

* When whitelisting is enabled, only types that are registered with the {@link SerializerRegistry} * can be serialized and deserialized, and classes will never be loaded by class names. This prevents * certain types of attacks in untrusted networks. * * @return The serializer. */ public Serializer enableWhitelist() { whitelistRequired.set(true); return this; } /** * Disables whitelisting for serializable types. *

* When whitelisting is disabled, types that are not registered may be serialized and deserialized * by this serializer. This can pose a security risk in an untrusted network. It's recommended that * users enable whitelisting and register serializable classes. * * @return The serializer. */ public Serializer disableWhitelist() { whitelistRequired.set(false); return this; } /** * Sets whether whitelisting of class names is required. * * @param whitelistRequired Indicates whether to require whitelisting of class names. * @return The serializer. */ public Serializer withWhitelistRequired(boolean whitelistRequired) { this.whitelistRequired.set(whitelistRequired); return this; } /** * Indicates whether whitelisting is enabled for the serializer. * * @return Whether whitelisting is enabled for the serializer. */ public boolean isWhitelistRequired() { return whitelistRequired.get(); } /** * Returns the serializer registry. * * @return The underlying serializable type registry. */ public SerializerRegistry registry() { return registry; } /** * Resolves serializable types with the given resolver. *

* This allows users to modify the serializable types registered to an existing {@link Serializer} instance. Types resolved * by the provided resolver(s) will be added to existing types resolved by any type resolvers provided to this object's * constructor or by previous calls to this method. * * @param resolvers The resolvers with which to resolve serializable types. * @return The serializer instance. */ public Serializer resolve(SerializableTypeResolver... resolvers) { registry.resolve(resolvers); return this; } /** * Resolves serializable types with the given resolver. *

* This allows users to modify the serializable types registered to an existing {@link Serializer} instance. Types resolved * by the provided resolver(s) will be added to existing types resolved by any type resolvers provided to this object's * constructor or by previous calls to this method. * * @param resolvers The resolvers with which to resolve serializable types. * @return The serializer instance. */ public Serializer resolve(Collection resolvers) { registry.resolve(resolvers); return this; } /** * Registers a serializable type. *

* The serializable type must be assignable from {@link CatalystSerializable} or * {@link java.io.Externalizable}. * * @param type The serializable type. This type must be assignable from {@link CatalystSerializable} * or {@link java.io.Externalizable}. * @return The serializer instance. * @throws java.lang.IllegalArgumentException If the serializable type ID is within the reserved range `128` to `255` */ public Serializer register(Class type) { registry.register(type); return this; } /** * Registers a ClassLoader for a class. * * @param type The class type. * @return The serializer instance. */ public Serializer registerClassLoader(Class type) { return registerClassLoader(type.getName(), type.getClassLoader()); } /** * Registers a ClassLoader for a class. * * @param className The class name. * @param classLoader The class loader. * @return The serializer instance. */ public Serializer registerClassLoader(String className, ClassLoader classLoader) { classLoaders.put(className, classLoader); return this; } /** * Returns the ClassLoader for a class. * * @param className The class name. * @return The ClassLoader instance to use for loading the class. */ public ClassLoader getClassLoader(String className) { return classLoaders.getOrDefault(className, this.getClass().getClassLoader()); } /** * Registers a serializable type with an identifier. *

* The serializable type must be assignable from {@link CatalystSerializable} or * {@link java.io.Externalizable}. * * @param type The serializable type. This type must be assignable from {@link CatalystSerializable} * or {@link java.io.Externalizable}. * @param id The type ID. This ID must be a number between `0` and `65535`. Serialization IDs between `128` and `255` * are reserved and will result in an {@link java.lang.IllegalArgumentException} * @return The serializer instance. * @throws java.lang.IllegalArgumentException If the serializable type ID is within the reserved range `128` to `255` */ public Serializer register(Class type, int id) { registry.register(type, id); return this; } /** * Registers a type serializer. *

* Because a custom {@link TypeSerializer} is provided, the registered {@code type} can be any class and does not have to * implement any particular interface. *

* Internally, the provided class will be wrapped in a {@link DefaultTypeSerializerFactory}. The serializer * class can be registered for more than one {@code type} class. The factory will instantiate a new * {@link TypeSerializer} instance once for each type for which the serializer is registered per {@link Serializer} * instance. If the {@code Serializer} instance is {@link Serializer#clone() cloned}, the serializer * factory will be copied and a new {@link TypeSerializer} will be instantiated for the clone. * * @param type The serializable type. * @param serializer The serializer to register. * @return The serializer instance. */ public Serializer register(Class type, Class serializer) { registry.register(type, serializer); return this; } /** * Registers a type serializer factory. *

* Because a custom {@link TypeSerializerFactory} is provided, the registered {@code type} can be any class and does not have to * implement any particular interface. *

* The serializer factory can be registered for more than one {@code type} class. The factory will be called on to * create a new {@link TypeSerializer} instance once for each type for which the serializer is * registered per {@link Serializer} instance. If the {@code Serializer} instance is {@link Serializer#clone() cloned}, * the serializer factory will be copied and a new {@link TypeSerializer} will be instantiated for the clone. * * @param type The serializable type. * @param factory The serializer factory to register. * @return The serializer instance. */ public Serializer register(Class type, TypeSerializerFactory factory) { registry.register(type, factory); return this; } /** * Registers a type serializer with an identifier. *

* The provided serializable type ID will be used to identify the serializable type during serialization and deserialization. * When objects of the given {@code type} are serialized to a {@link Buffer}, the given type * {@code id} will be written to the buffer in lieu of its class name. When the object is deserialized, the type {@code id} * will be used to look up the class. It is essential that the given {@code type} be registered with the same {@code id} * on all {@link Serializer} instances. *

* Because a custom {@link TypeSerializer} is provided, the registered {@code type} can be any class and does not have to * implement any particular interface. *

* Internally, the provided class will be wrapped in a {@link DefaultTypeSerializerFactory}. The serializer * class can be registered for more than one {@code type} class. The factory will instantiate a new * {@link TypeSerializer} instance once for each type for which the serializer is registered per {@link Serializer} * instance. If the {@code Serializer} instance is {@link Serializer#clone() cloned}, the serializer * factory will be copied and a new {@link TypeSerializer} will be instantiated for the clone. * * @param type The serializable type. * @param id The serializable type ID. * @param serializer The serializer to register. * @return The serializer instance. */ public Serializer register(Class type, int id, Class serializer) { registry.register(type, id, serializer); return this; } /** * Registers a type serializer with an identifier. *

* The provided serializable type ID will be used to identify the serializable type during serialization and deserialization. * When objects of the given {@code type} are serialized to a {@link Buffer}, the given type * {@code id} will be written to the buffer in lieu of its class name. When the object is deserialized, the type {@code id} * will be used to look up the class. It is essential that the given {@code type} be registered with the same {@code id} * on all {@link Serializer} instances. *

* Because a custom {@link TypeSerializerFactory} is provided, the registered {@code type} can be any class and does not have to * implement any particular interface. *

* The serializer factory can be registered for more than one {@code type} class. The factory will be called on to * create a new {@link TypeSerializer} instance once for each type for which the serializer is * registered per {@link Serializer} instance. If the {@code Serializer} instance is {@link Serializer#clone() cloned}, * the serializer factory will be copied and a new {@link TypeSerializer} will be instantiated for the clone. * * @param type The serializable type. * @param id The serializable type ID. * @param factory The serializer factory to register. * @return The serializer instance. */ public Serializer register(Class type, int id, TypeSerializerFactory factory) { registry.register(type, id, factory); return this; } /** * Registers an abstract type serializer for the given abstract type. *

* Abstract serializers allow abstract types to be serialized without explicitly registering a concrete type. * The concept of abstract serializers differs from {@link #registerDefault(Class, TypeSerializerFactory) default serializers} * in that abstract serializers can be registered with a serializable type ID, and types {@link #register(Class) registered} * without a specific {@link TypeSerializer} do not inheret from abstract serializers. *

   *   {@code
   *   serializer.registerAbstract(List.class, AbstractListSerializer.class);
   *   }
   * 
* * @param abstractType The abstract type for which to register the abstract serializer. Types that extend * the abstract type will be serialized using the given abstract serializer unless a * serializer has been registered for the specific concrete type. * @param serializer The abstract type serializer with which to serialize instances of the abstract type. * @return The serializer. * @throws NullPointerException if the {@code abstractType} or {@code serializer} is {@code null} */ public Serializer registerAbstract(Class abstractType, Class serializer) { registry.registerAbstract(abstractType, serializer); return this; } /** * Registers an abstract type serializer for the given abstract type. *

* Abstract serializers allow abstract types to be serialized without explicitly registering a concrete type. * The concept of abstract serializers differs from {@link #registerDefault(Class, TypeSerializerFactory) default serializers} * in that abstract serializers can be registered with a serializable type ID, and types {@link #register(Class) registered} * without a specific {@link TypeSerializer} do not inheret from abstract serializers. *

   *   {@code
   *   serializer.registerAbstract(List.class, AbstractListSerializer.class);
   *   }
   * 
* * @param abstractType The abstract type for which to register the abstract serializer. Types that extend * the abstract type will be serialized using the given abstract serializer unless a * serializer has been registered for the specific concrete type. * @param factory The abstract type serializer factory with which to serialize instances of the abstract type. * @return The serializer. * @throws NullPointerException if the {@code abstractType} or {@code serializer} is {@code null} */ public Serializer registerAbstract(Class abstractType, TypeSerializerFactory factory) { registry.registerAbstract(abstractType, factory); return this; } /** * Registers an abstract type serializer for the given abstract type. *

* Abstract serializers allow abstract types to be serialized without explicitly registering a concrete type. * The concept of abstract serializers differs from {@link #registerDefault(Class, TypeSerializerFactory) default serializers} * in that abstract serializers can be registered with a serializable type ID, and types {@link #register(Class) registered} * without a specific {@link TypeSerializer} do not inheret from abstract serializers. *

   *   {@code
   *   serializer.registerAbstract(List.class, AbstractListSerializer.class);
   *   }
   * 
* * @param abstractType The abstract type for which to register the abstract serializer. Types that extend * the abstract type will be serialized using the given abstract serializer unless a * serializer has been registered for the specific concrete type. * @param id The serializable type ID with which to serialize the abstract class. * @param serializer The abstract type serializer with which to serialize instances of the abstract type. * @return The serializer. * @throws NullPointerException if the {@code abstractType} or {@code serializer} is {@code null} */ public Serializer registerAbstract(Class abstractType, int id, Class serializer) { registry.registerAbstract(abstractType, id, serializer); return this; } /** * Registers an abstract type serializer for the given abstract type. *

* Abstract serializers allow abstract types to be serialized without explicitly registering a concrete type. * The concept of abstract serializers differs from {@link #registerDefault(Class, TypeSerializerFactory) default serializers} * in that abstract serializers can be registered with a serializable type ID, and types {@link #register(Class) registered} * without a specific {@link TypeSerializer} do not inheret from abstract serializers. *

   *   {@code
   *   serializer.registerAbstract(List.class, AbstractListSerializer.class);
   *   }
   * 
* * @param abstractType The abstract type for which to register the abstract serializer. Types that extend * the abstract type will be serialized using the given abstract serializer unless a * serializer has been registered for the specific concrete type. * @param id The serializable type ID with which to serialize the abstract class. * @param factory The abstract type serializer factory with which to serialize instances of the abstract type. * @return The serializer. * @throws NullPointerException if the {@code abstractType} or {@code serializer} is {@code null} */ public Serializer registerAbstract(Class abstractType, int id, TypeSerializerFactory factory) { registry.registerAbstract(abstractType, id, factory); return this; } /** * Registers a default type serializer for the given base type. *

* Default serializers are used to serialize types for which no specific {@link TypeSerializer} is provided. * When a serializable type is {@link #register(Class) registered} without a {@link TypeSerializer}, the * first default serializer found for the given type is assigned as the serializer for that type. Default * serializers are evaluated against registered types in reverse insertion order, so default serializers * registered more recently take precedence over default serializers registered earlier. *

   *   {@code
   *   serializer.registerDefault(Serializable.class, SerializableSerializer.class);
   *   serializer.register(SomeSerializable.class, 1);
   *   }
   * 
* If an object of a type that has not been {@link #register(Class) registered} is * {@link #writeObject(Object) serialized} and {@link #isWhitelistRequired() whitelisting} is disabled, * the object will be serialized with the class name and a default serializer if one is found. * * @param baseType The base type for which to register the default serializer. Types that extend the base * type and are registered without a specific {@link TypeSerializer} will be serialized * using the registered default {@link TypeSerializer}. * @param serializer The default type serializer with which to serialize instances of the base type. * @return The serializer. * @throws NullPointerException if either argument is {@code null} */ public Serializer registerDefault(Class baseType, Class serializer) { registry.registerDefault(baseType, serializer); return this; } /** * Registers a default type serializer for the given base type. *

* Default serializers are used to serialize types for which no specific {@link TypeSerializer} is provided. * When a serializable type is {@link #register(Class) registered} without a {@link TypeSerializer}, the * first default serializer found for the given type is assigned as the serializer for that type. Default * serializers are evaluated against registered types in reverse insertion order, so default serializers * registered more recently take precedence over default serializers registered earlier. *

   *   {@code
   *   serializer.registerDefault(Serializable.class, SerializableSerializer.class);
   *   serializer.register(SomeSerializable.class, 1);
   *   }
   * 
* If an object of a type that has not been {@link #register(Class) registered} is * {@link #writeObject(Object) serialized} and {@link #isWhitelistRequired() whitelisting} is disabled, * the object will be serialized with the class name and a default serializer if one is found. * * @param baseType The base type for which to register the default serializer. Types that extend the base * type and are registered without a specific {@link TypeSerializer} will be serialized * using the registered default {@link TypeSerializer}. * @param factory The default type serializer factory with which to serialize instances of the base type. * @return The serializer. * @throws NullPointerException if either argument is {@code null} */ public Serializer registerDefault(Class baseType, TypeSerializerFactory factory) { registry.registerDefault(baseType, factory); return this; } /** * Returns the underlying buffer allocator. * * @return The underlying buffer allocator. */ public BufferAllocator allocator() { return allocator; } /** * Allocates a new buffer with an arbitrary initial capacity and unlimited maximum capacity. *

* The buffer will be allocated via the {@link BufferAllocator} provided to this instance's constructor. * If no {@code BufferAllocator} was provided, the default {@link UnpooledHeapAllocator} will * be used. * * @return The allocated buffer. This will have an initial capacity that is dependent on the underlying {@link BufferAllocator}. */ public Buffer allocate() { return allocator.allocate(); } /** * Allocates a new buffer with an initial and an unlimited maximum capacity. *

* The buffer will be allocated via the {@link BufferAllocator} provided to this instance's constructor. * If no {@code BufferAllocator} was provided, the default {@link UnpooledHeapAllocator} will * be used. * * @param capacity The buffer capacity. * @return The allocated buffer. This will have an initial capacity of the given {@code capacity} */ public Buffer allocate(long capacity) { return allocator.allocate(capacity); } /** * Allocates a new buffer with a dynamic capacity. *

* The buffer will be allocated via the {@link BufferAllocator} provided to this instance's constructor. * If no {@code BufferAllocator} was provided, the default {@link UnpooledHeapAllocator} will * be used. * * @param initialCapacity The initial buffer capacity. * @param maxCapacity The maximum buffer capacity. * @return The allocated buffer. This will have an initial capacity of {@code initialCapacity} and a maximum capacity * of {@code maxCapacity} */ public Buffer allocate(long initialCapacity, long maxCapacity) { return allocator.allocate(initialCapacity, maxCapacity); } /** * Copies the given object. * * @param object The object to copy. * @param The object type. * @return The copied object. */ public T copy(T object) { return readObject(writeObject(object).flip()); } /** * Returns the serializer for the given type else {@code null} if no serializer or factory are registered for the * {@code type}. */ @SuppressWarnings("unchecked") private TypeSerializer getSerializer(Class type) { TypeSerializer serializer = (TypeSerializer) serializers.get(type); if (serializer == null) { TypeSerializerFactory factory = registry.factory(type); if (factory != null) { serializer = (TypeSerializer) factory.createSerializer(type); serializers.put(type, serializer); } } return serializer; } /** * Writes an object to a buffer. *

* Serialized bytes will be written to a {@link Buffer} allocated via the {@link BufferAllocator} * provided to this instance's constructor. Note that for consistency with {@link Serializer#writeObject(Object, Buffer)} * the returned buffer will not be flipped, so users should {@link Buffer#flip()} the buffer prior to reading. *

* The given object must have a {@link Serializer#register(Class) registered} serializer or implement {@link java.io.Serializable}. * If a serializable type ID was provided during registration, the type ID will be written to the returned * {@link Buffer} in lieu of the class name. Types with no associated type ID will be written * to the buffer with a full class name for reference during serialization. *

* Types that implement {@link java.io.Serializable} will be serialized using Java's {@link java.io.ObjectOutputStream}. * Types that implement {@link java.io.Externalizable} will be serialized via that interface's methods unless a custom * {@link TypeSerializer} has been registered for the type. {@link java.io.Externalizable} types can, * however, still take advantage of faster serialization of type IDs. * * @param object The object to write. * @param The object type. * @return The serialized object. * @throws SerializationException If no serializer is registered for the object. * @see Serializer#writeObject(Object, Buffer) */ public Buffer writeObject(T object) { return writeObject(object, allocator.allocate()); } /** * Writes an object to the given output stream. *

* The given object must have a {@link Serializer#register(Class) registered} serializer or implement {@link java.io.Serializable}. * If a serializable type ID was provided during registration, the type ID will be written to the given * {@link Buffer} in lieu of the class name. Types with no associated type ID will be written * to the buffer with a full class name for reference during serialization. *

* Types that implement {@link CatalystSerializable} will be serialized via * {@link CatalystSerializable#writeObject(BufferOutput, Serializer)} unless a * {@link TypeSerializer} was explicitly registered for the type. *

* Types that implement {@link java.io.Serializable} will be serialized using Java's {@link java.io.ObjectOutputStream}. * Types that implement {@link java.io.Externalizable} will be serialized via that interface's methods unless a custom * {@link TypeSerializer} has been registered for the type. {@link java.io.Externalizable} types can, * however, still take advantage of faster serialization of type IDs. * * @param object The object to write. * @param outputStream The output stream to which to write the object. * @param The object type. * @return The serialized object. * @throws SerializationException If no serializer is registered for the object. * @see Serializer#writeObject(Object) */ public OutputStream writeObject(T object, OutputStream outputStream) { writeObject(object, new OutputStreamBufferOutput(outputStream)); return outputStream; } /** * Writes an object to the given buffer. *

* Serialized bytes will be written to the given {@link Buffer} starting at its current * {@link Buffer#position()}. If the bytes {@link Buffer#remaining()} in * the buffer are not great enough to hold the serialized bytes, the buffer will be automatically expanded up to the * buffer's {@link Buffer#maxCapacity()}. *

* The given object must have a {@link Serializer#register(Class) registered} serializer or implement {@link java.io.Serializable}. * If a serializable type ID was provided during registration, the type ID will be written to the given * {@link Buffer} in lieu of the class name. Types with no associated type ID will be written * to the buffer with a full class name for reference during serialization. *

* Types that implement {@link CatalystSerializable} will be serialized via * {@link CatalystSerializable#writeObject(BufferOutput, Serializer)} unless a * {@link TypeSerializer} was explicitly registered for the type. *

* Types that implement {@link java.io.Serializable} will be serialized using Java's {@link java.io.ObjectOutputStream}. * Types that implement {@link java.io.Externalizable} will be serialized via that interface's methods unless a custom * {@link TypeSerializer} has been registered for the type. {@link java.io.Externalizable} types can, * however, still take advantage of faster serialization of type IDs. * * @param object The object to write. * @param buffer The buffer to which to write the object. * @param The object type. * @return The serialized object. * @throws SerializationException If no serializer is registered for the object. * @see Serializer#writeObject(Object) */ public Buffer writeObject(T object, Buffer buffer) { writeObject(object, (BufferOutput) buffer); return buffer; } /** * Writes an object to the given buffer. *

* Serialized bytes will be written to the given {@link Buffer} starting at its current * {@link Buffer#position()}. If the bytes {@link Buffer#remaining()} in * the buffer are not great enough to hold the serialized bytes, the buffer will be automatically expanded up to the * buffer's {@link Buffer#maxCapacity()}. *

* The given object must have a {@link Serializer#register(Class) registered} serializer or implement {@link java.io.Serializable}. * If a serializable type ID was provided during registration, the type ID will be written to the given * {@link Buffer} in lieu of the class name. Types with no associated type ID will be written * to the buffer with a full class name for reference during serialization. *

* Types that implement {@link CatalystSerializable} will be serialized via * {@link CatalystSerializable#writeObject(BufferOutput, Serializer)} unless a * {@link TypeSerializer} was explicitly registered for the type. *

* Types that implement {@link java.io.Serializable} will be serialized using Java's {@link java.io.ObjectOutputStream}. * Types that implement {@link java.io.Externalizable} will be serialized via that interface's methods unless a custom * {@link TypeSerializer} has been registered for the type. {@link java.io.Externalizable} types can, * however, still take advantage of faster serialization of type IDs. * * @param object The object to write. * @param buffer The buffer to which to write the object. * @param The object type. * @return The serialized object. * @throws SerializationException If no serializer is registered for the object. * @see Serializer#writeObject(Object) */ public BufferOutput writeObject(T object, BufferOutput buffer) { if (object == null) { return buffer.writeByte(Identifier.NULL.code()); } Class type = object.getClass(); // get the enclosing class from a cache. Class enclosingClass = enclosingClasses.computeIfAbsent(type, clazz -> Optional.ofNullable(clazz.getEnclosingClass())).orElse(null); // Enums that implement interfaces or methods are generated as inner classes. For this reason, // we need to get the enclosing class if it's an enum. if (enclosingClass != null && enclosingClass.isEnum()) type = enclosingClass; // Look up the serializer for the given object type. TypeSerializer serializer = getSerializer(type); // If no serializer was found, throw a serialization exception. if (serializer == null) { throw new SerializationException("cannot serialize unregistered type: " + type); } // Cache the serializable type ID if necessary. if (!ids.containsKey(type)) { ids.put(type, registry.id(type)); } // Lookup the serializable type ID for the type. int typeId = registry.id(type); if (typeId == 0) { return writeByClass(type, object, buffer, serializer); } return writeById(typeId, object, buffer, serializer); } /** * Writes an object to the buffer using the given serialization ID. */ @SuppressWarnings("unchecked") private BufferOutput writeById(int id, Object object, BufferOutput output, TypeSerializer serializer) { for (Identifier identifier : Identifier.values()) { if (identifier.accept(id)) { identifier.write(id, output.writeByte(identifier.code())); serializer.write(object, output, this); return output; } } throw new SerializationException("invalid type ID: " + id); } /** * Writes an object to the buffer with its class name. */ @SuppressWarnings("unchecked") private BufferOutput writeByClass(Class type, Object object, BufferOutput output, TypeSerializer serializer) { if (whitelistRequired.get()) throw new SerializationException("cannot serialize unregistered type: " + type); serializer.write(object, output.writeByte(Identifier.CLASS.code()).writeUTF8(type.getName()), this); return output; } /** * Reads an object from the given input stream. *

* During deserialization, the buffer will first be read to determine the type to be deserialized. If the object was * written using a serializable type ID, the given ID will be used to locate the serialized type. The type must have * been {@link Serializer#register(Class) registered} with this {@link Serializer} instance in order to * perform a reverse lookup. *

* If the type was written to the buffer with a fully qualified class name, the class name will be used to load the * object class via {@link Class#forName(String)}. Serializable types must implement a no-argument constructor to be * properly deserialized. *

* If the serialized type is an instance of {@link CatalystSerializable}, * {@link CatalystSerializable#readObject(BufferInput, Serializer)} will be used to * read the object attributes from the buffer. *

* If the type is a {@link java.io.Serializable} type serialized with native Java serialization, it will be read from * the buffer via {@link java.io.ObjectInputStream}. *

* For types that implement {@link ReferenceCounted}, the serializer will use an internal object pool * to automatically pool and reuse reference counted types for deserialization. This means that users must release * {@link ReferenceCounted} types back to the object pool via * {@link ReferenceCounted#release()} or {@link ReferenceCounted#close()} * once complete. * * @param inputStream The input stream from which to read the object. * @param The object type. * @return The read object. * @throws SerializationException If no type could be read from the provided buffer. */ public T readObject(InputStream inputStream) { return readObject(new InputStreamBufferInput(inputStream)); } /** * Reads an object from the given buffer. *

* The object will be read from the given buffer starting at the current {@link Buffer#position()}. *

* During deserialization, the buffer will first be read to determine the type to be deserialized. If the object was * written using a serializable type ID, the given ID will be used to locate the serialized type. The type must have * been {@link Serializer#register(Class) registered} with this {@link Serializer} instance in order to * perform a reverse lookup. *

* If the type was written to the buffer with a fully qualified class name, the class name will be used to load the * object class via {@link Class#forName(String)}. Serializable types must implement a no-argument constructor to be * properly deserialized. *

* If the serialized type is an instance of {@link CatalystSerializable}, * {@link CatalystSerializable#readObject(BufferInput, Serializer)} will be used to * read the object attributes from the buffer. *

* If the type is a {@link java.io.Serializable} type serialized with native Java serialization, it will be read from * the buffer via {@link java.io.ObjectInputStream}. *

* For types that implement {@link ReferenceCounted}, the serializer will use an internal object pool * to automatically pool and reuse reference counted types for deserialization. This means that users must release * {@link ReferenceCounted} types back to the object pool via * {@link ReferenceCounted#release()} or {@link ReferenceCounted#close()} * once complete. * * @param buffer The buffer from which to read the object. * @param The object type. * @return The read object. * @throws SerializationException If no type could be read from the provided buffer. */ public T readObject(Buffer buffer) { return readObject((BufferInput) buffer); } /** * Reads an object from the given buffer. *

* The object will be read from the given buffer starting at the current {@link Buffer#position()}. *

* During deserialization, the buffer will first be read to determine the type to be deserialized. If the object was * written using a serializable type ID, the given ID will be used to locate the serialized type. The type must have * been {@link Serializer#register(Class) registered} with this {@link Serializer} instance in order to * perform a reverse lookup. *

* If the type was written to the buffer with a fully qualified class name, the class name will be used to load the * object class via {@link Class#forName(String)}. Serializable types must implement a no-argument constructor to be * properly deserialized. *

* If the serialized type is an instance of {@link CatalystSerializable}, * {@link CatalystSerializable#readObject(BufferInput, Serializer)} will be used to * read the object attributes from the buffer. *

* If the type is a {@link java.io.Serializable} type serialized with native Java serialization, it will be read from * the buffer via {@link java.io.ObjectInputStream}. *

* For types that implement {@link ReferenceCounted}, the serializer will use an internal object pool * to automatically pool and reuse reference counted types for deserialization. This means that users must release * {@link ReferenceCounted} types back to the object pool via * {@link ReferenceCounted#release()} or {@link ReferenceCounted#close()} * once complete. * * @param buffer The buffer from which to read the object. * @param The object type. * @return The read object. * @throws SerializationException If no type could be read from the provided buffer. */ @SuppressWarnings("unchecked") public T readObject(BufferInput buffer) { int code = buffer.readByte(); Identifier identifier = Identifier.forCode(code); switch (identifier) { case NULL: return null; case CLASS: return readByClass(buffer); default: return readById(identifier.read(buffer), buffer); } } /** * Reads a serializable object. * * @param id The serializable type ID. * @param buffer The buffer from which to read the object. * @param The object type. * @return The read object. */ @SuppressWarnings("unchecked") private T readById(int id, BufferInput buffer) { Class type = (Class) registry.type(id); if (type == null) throw new SerializationException("cannot deserialize: unknown type"); TypeSerializer serializer = getSerializer(type); if (serializer == null) throw new SerializationException("cannot deserialize: unknown type"); return serializer.read(type, buffer, this); } /** * Reads a writable object. * * @param buffer The buffer from which to read the object. * @param The object type. * @return The read object. */ @SuppressWarnings("unchecked") private T readByClass(BufferInput buffer) { String name = buffer.readUTF8(); if (whitelistRequired.get()) throw new SerializationException("cannot deserialize unregistered type: " + name); Class type = (Class) types.get(name); if (type == null) { try { type = (Class) Class.forName(name); if (type == null) throw new SerializationException("cannot deserialize: unknown type"); types.put(name, type); } catch (ClassNotFoundException e) { throw new SerializationException("object class not found: " + name, e); } } TypeSerializer serializer = getSerializer(type); if (serializer == null) throw new SerializationException("cannot deserialize unregistered type: " + name); return serializer.read(type, buffer, this); } /** * Clones the object. */ @Override public final Serializer clone() { return new Serializer(registry, allocator, whitelistRequired, classLoaders); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy