diff --git a/core/src/main/java/org/hibernate/util/SerializationHelper.java b/core/src/main/java/org/hibernate/util/SerializationHelper.java index c875db33cf..cf458cae6e 100644 --- a/core/src/main/java/org/hibernate/util/SerializationHelper.java +++ b/core/src/main/java/org/hibernate/util/SerializationHelper.java @@ -36,6 +36,7 @@ import java.io.ObjectInputStream; import org.hibernate.type.SerializationException; import org.hibernate.Hibernate; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,148 +59,187 @@ import org.slf4j.LoggerFactory; * @author Stephen Colebourne * @author Jeff Varszegi * @author Gary Gregory - * @since 1.0 * @version $Id: SerializationHelper.java 9180 2006-01-30 23:51:27Z steveebersole $ + * @since 1.0 */ public final class SerializationHelper { - private static final Logger log = LoggerFactory.getLogger(SerializationHelper.class); + private static final Logger log = LoggerFactory.getLogger( SerializationHelper.class ); - private SerializationHelper() {} + private SerializationHelper() { + } - // Clone - //----------------------------------------------------------------------- - /** - *

Deep clone an Object using serialization.

- * - *

This is many times slower than writing clone methods by hand - * on all objects in your object graph. However, for complex object - * graphs, or for those that don't support deep cloning this can - * be a simple alternative implementation. Of course all the objects - * must be Serializable.

- * - * @param object the Serializable object to clone - * @return the cloned object - * @throws SerializationException (runtime) if the serialization fails - */ - public static Object clone(Serializable object) throws SerializationException { - log.trace("Starting clone through serialization"); + // Clone + //----------------------------------------------------------------------- + + /** + *

Deep clone an Object using serialization.

+ * + *

This is many times slower than writing clone methods by hand + * on all objects in your object graph. However, for complex object + * graphs, or for those that don't support deep cloning this can + * be a simple alternative implementation. Of course all the objects + * must be Serializable.

+ * + * @param object the Serializable object to clone + * + * @return the cloned object + * + * @throws SerializationException (runtime) if the serialization fails + */ + public static Object clone(Serializable object) throws SerializationException { + log.trace( "Starting clone through serialization" ); if ( object == null ) { return null; } - return deserialize( serialize( object ), object.getClass().getClassLoader() ); - } + return deserialize( serialize( object ), object.getClass().getClassLoader() ); + } - // Serialize - //----------------------------------------------------------------------- - /** - *

Serializes an Object to the specified stream.

- * - *

The stream will be closed once the object is written. - * This avoids the need for a finally clause, and maybe also exception - * handling, in the application code.

- * - *

The stream passed in is not buffered internally within this method. - * This is the responsibility of your application if desired.

- * - * @param obj the object to serialize to bytes, may be null - * @param outputStream the stream to write to, must not be null - * @throws IllegalArgumentException if outputStream is null - * @throws SerializationException (runtime) if the serialization fails - */ - public static void serialize(Serializable obj, OutputStream outputStream) throws SerializationException { - if (outputStream == null) { - throw new IllegalArgumentException("The OutputStream must not be null"); - } + // Serialize + //----------------------------------------------------------------------- - if ( log.isTraceEnabled() ) { - if ( Hibernate.isInitialized( obj ) ) { - log.trace( "Starting serialization of object [" + obj + "]" ); - } - else { - log.trace( "Starting serialization of [uninitialized proxy]" ); - } - } + /** + *

Serializes an Object to the specified stream.

+ * + *

The stream will be closed once the object is written. + * This avoids the need for a finally clause, and maybe also exception + * handling, in the application code.

+ * + *

The stream passed in is not buffered internally within this method. + * This is the responsibility of your application if desired.

+ * + * @param obj the object to serialize to bytes, may be null + * @param outputStream the stream to write to, must not be null + * + * @throws IllegalArgumentException if outputStream is null + * @throws SerializationException (runtime) if the serialization fails + */ + public static void serialize(Serializable obj, OutputStream outputStream) throws SerializationException { + if ( outputStream == null ) { + throw new IllegalArgumentException( "The OutputStream must not be null" ); + } - ObjectOutputStream out = null; - try { - // stream closed in the finally - out = new ObjectOutputStream(outputStream); - out.writeObject(obj); + if ( log.isTraceEnabled() ) { + if ( Hibernate.isInitialized( obj ) ) { + log.trace( "Starting serialization of object [" + obj + "]" ); + } + else { + log.trace( "Starting serialization of [uninitialized proxy]" ); + } + } - } - catch (IOException ex) { - throw new SerializationException("could not serialize", ex); - } - finally { - try { - if (out != null) out.close(); - } - catch (IOException ignored) {} - } - } + ObjectOutputStream out = null; + try { + // stream closed in the finally + out = new ObjectOutputStream( outputStream ); + out.writeObject( obj ); - /** - *

Serializes an Object to a byte array for - * storage/serialization.

- * - * @param obj the object to serialize to bytes - * @return a byte[] with the converted Serializable - * @throws SerializationException (runtime) if the serialization fails - */ - public static byte[] serialize(Serializable obj) throws SerializationException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(512); - serialize(obj, baos); - return baos.toByteArray(); - } + } + catch ( IOException ex ) { + throw new SerializationException( "could not serialize", ex ); + } + finally { + try { + if ( out != null ) { + out.close(); + } + } + catch ( IOException ignored ) { + } + } + } - // Deserialize - //----------------------------------------------------------------------- - /** - * Deserializes an object from the specified stream using the Thread Context + /** + *

Serializes an Object to a byte array for + * storage/serialization.

+ * + * @param obj the object to serialize to bytes + * + * @return a byte[] with the converted Serializable + * + * @throws SerializationException (runtime) if the serialization fails + */ + public static byte[] serialize(Serializable obj) throws SerializationException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream( 512 ); + serialize( obj, byteArrayOutputStream ); + return byteArrayOutputStream.toByteArray(); + } + + // Deserialize + //----------------------------------------------------------------------- + + /** + * Deserializes an object from the specified stream using the Thread Context + * ClassLoader (TCCL). + *

+ * Delegates to {@link #doDeserialize} + * + * @param inputStream the serialized object input stream, must not be null + * + * @return the deserialized object + * + * @throws IllegalArgumentException if inputStream is null + * @throws SerializationException (runtime) if the serialization fails + */ + public static Object deserialize(InputStream inputStream) throws SerializationException { + return doDeserialize( inputStream, defaultClassLoader(), hibernateClassLoader(), null ); + } + + /** + * Returns the Thread Context ClassLoader (TCCL). + * + * @return The current TCCL + */ + public static ClassLoader defaultClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + public static ClassLoader hibernateClassLoader() { + return SerializationHelper.class.getClassLoader(); + } + + /** + * Deserializes an object from the specified stream using the Thread Context * ClassLoader (TCCL). If there is no TCCL set, the classloader of the calling * class is used. - *

- * Delegates to {@link #deserialize(java.io.InputStream, ClassLoader)} - * - * @param inputStream the serialized object input stream, must not be null - * @return the deserialized object - * @throws IllegalArgumentException if inputStream is null - * @throws SerializationException (runtime) if the serialization fails - */ - public static Object deserialize(InputStream inputStream) throws SerializationException { - return deserialize( inputStream, Thread.currentThread().getContextClassLoader() ); - } - - /** - * Deserializes an object from the specified stream using the Thread Context - * ClassLoader (TCCL). If there is no TCCL set, the classloader of the calling - * class is used. - *

- * The stream will be closed once the object is read. This avoids the need + *

+ * The stream will be closed once the object is read. This avoids the need * for a finally clause, and maybe also exception handling, in the application * code. - *

- * The stream passed in is not buffered internally within this method. This is + *

+ * The stream passed in is not buffered internally within this method. This is * the responsibility of the caller, if desired. - * - * @param inputStream the serialized object input stream, must not be null + * + * @param inputStream the serialized object input stream, must not be null * @param loader The classloader to use * - * @return the deserialized object + * @return the deserialized object * - * @throws IllegalArgumentException if inputStream is null - * @throws SerializationException (runtime) if the serialization fails - */ - public static Object deserialize(InputStream inputStream, ClassLoader loader) throws SerializationException { - if (inputStream == null) { - throw new IllegalArgumentException( "The InputStream must not be null" ); - } + * @throws IllegalArgumentException if inputStream is null + * @throws SerializationException (runtime) if the serialization fails + */ + public static Object deserialize(InputStream inputStream, ClassLoader loader) throws SerializationException { + return doDeserialize( inputStream, loader, defaultClassLoader(), hibernateClassLoader() ); + } + + public static Object doDeserialize( + InputStream inputStream, + ClassLoader loader, + ClassLoader fallbackLoader1, + ClassLoader fallbackLoader2) throws SerializationException { + if ( inputStream == null ) { + throw new IllegalArgumentException( "The InputStream must not be null" ); + } log.trace( "Starting deserialization of object" ); try { - CustomObjectInputStream in = new CustomObjectInputStream( inputStream, loader ); + CustomObjectInputStream in = new CustomObjectInputStream( + inputStream, + loader, + fallbackLoader1, + fallbackLoader2 + ); try { return in.readObject(); } @@ -213,7 +253,7 @@ public final class SerializationHelper { try { in.close(); } - catch (IOException ignore) { + catch ( IOException ignore ) { // ignore } } @@ -223,43 +263,48 @@ public final class SerializationHelper { } } - /** - * Deserializes an object from an array of bytes using the Thread Context + /** + * Deserializes an object from an array of bytes using the Thread Context * ClassLoader (TCCL). If there is no TCCL set, the classloader of the calling * class is used. *

* Delegates to {@link #deserialize(byte[], ClassLoader)} - * - * @param objectData the serialized object, must not be null - * @return the deserialized object - * @throws IllegalArgumentException if objectData is null - * @throws SerializationException (runtime) if the serialization fails - */ - public static Object deserialize(byte[] objectData) throws SerializationException { - return deserialize( objectData, Thread.currentThread().getContextClassLoader() ); - } + * + * @param objectData the serialized object, must not be null + * + * @return the deserialized object + * + * @throws IllegalArgumentException if objectData is null + * @throws SerializationException (runtime) if the serialization fails + */ + public static Object deserialize(byte[] objectData) throws SerializationException { + return doDeserialize( wrap( objectData ), defaultClassLoader(), hibernateClassLoader(), null ); + } - /** - * Deserializes an object from an array of bytes. + private static InputStream wrap(byte[] objectData) { + if ( objectData == null ) { + throw new IllegalArgumentException( "The byte[] must not be null" ); + } + return new ByteArrayInputStream( objectData ); + } + + /** + * Deserializes an object from an array of bytes. *

* Delegates to {@link #deserialize(java.io.InputStream, ClassLoader)} using a - * {@link ByteArrayInputStream} to wrap the array. - * - * @param objectData the serialized object, must not be null + * {@link ByteArrayInputStream} to wrap the array. + * + * @param objectData the serialized object, must not be null * @param loader The classloader to use * - * @return the deserialized object + * @return the deserialized object * - * @throws IllegalArgumentException if objectData is null - * @throws SerializationException (runtime) if the serialization fails - */ - public static Object deserialize(byte[] objectData, ClassLoader loader) throws SerializationException { - if ( objectData == null ) { - throw new IllegalArgumentException( "The byte[] must not be null" ); - } - ByteArrayInputStream bais = new ByteArrayInputStream( objectData ); - return deserialize( bais, loader ); - } + * @throws IllegalArgumentException if objectData is null + * @throws SerializationException (runtime) if the serialization fails + */ + public static Object deserialize(byte[] objectData, ClassLoader loader) throws SerializationException { + return doDeserialize( wrap( objectData ), loader, defaultClassLoader(), hibernateClassLoader() ); + } /** @@ -271,11 +316,19 @@ public final class SerializationHelper { * facilitate for that we allow passing in the class loader we should use. */ private static final class CustomObjectInputStream extends ObjectInputStream { - private final ClassLoader loader; + private final ClassLoader loader1; + private final ClassLoader loader2; + private final ClassLoader loader3; - private CustomObjectInputStream(InputStream in, ClassLoader loader) throws IOException { + private CustomObjectInputStream( + InputStream in, + ClassLoader loader1, + ClassLoader loader2, + ClassLoader loader3) throws IOException { super( in ); - this.loader = loader; + this.loader1 = loader1; + this.loader2 = loader2; + this.loader3 = loader3; } /** @@ -283,14 +336,29 @@ public final class SerializationHelper { */ protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException { String className = v.getName(); - log.trace("Attempting to locate class [" + className + "]"); + log.trace( "Attempting to locate class [" + className + "]" ); - // if we were given a classloader, attempt to use it to resolve the class... - if ( loader != null ) { + try { + return Class.forName( className, false, loader1 ); + } + catch ( ClassNotFoundException e ) { + log.trace( "Unable to locate class using given classloader" ); + } + + if ( different( loader1, loader2 ) ) { try { - return Class.forName( className, false, loader ); + return Class.forName( className, false, loader2 ); } - catch (ClassNotFoundException e) { + catch ( ClassNotFoundException e ) { + log.trace( "Unable to locate class using given classloader" ); + } + } + + if ( different( loader1, loader3 ) && different( loader2, loader3 ) ) { + try { + return Class.forName( className, false, loader2 ); + } + catch ( ClassNotFoundException e ) { log.trace( "Unable to locate class using given classloader" ); } } @@ -299,5 +367,14 @@ public final class SerializationHelper { // of the class which is calling this deserialization. return super.resolveClass( v ); } + + private boolean different(ClassLoader one, ClassLoader other) { + if ( one == null ) { + return other != null; + } + else { + return !one.equals( other ); + } + } } }