[hibernate-commits] Hibernate SVN: r19542 - core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/util.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue May 18 13:04:38 EDT 2010


Author: stliu
Date: 2010-05-18 13:04:38 -0400 (Tue, 18 May 2010)
New Revision: 19542

Modified:
   core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/util/SerializationHelper.java
Log:
JBPAPP-4326 HHH-4786 Hibernate SerializableType + Custom Serializable Class Causes CL Problems

Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/util/SerializationHelper.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/util/SerializationHelper.java	2010-05-18 16:58:33 UTC (rev 19541)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/util/SerializationHelper.java	2010-05-18 17:04:38 UTC (rev 19542)
@@ -1,55 +1,26 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
- * reserved.
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
  *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
  *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
  *
- * 3. The end-user documentation included with the redistribution, if
- *    any, must include the following acknowledgement:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgement may appear in the software itself,
- *    if and wherever such third-party acknowledgements normally appear.
- *
- * 4. The names "The Jakarta Project", "Commons", and "Apache Software
- *    Foundation" must not be used to endorse or promote products derived
- *    from this software without prior written permission. For written
- *    permission, please contact apache at apache.org.
- *
- * 5. Products derived from this software may not be called "Apache"
- *    nor may "Apache" appear in their names without prior written
- *    permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
  */
 package org.hibernate.util;
 
@@ -63,11 +34,12 @@
 import java.io.ObjectStreamClass;
 import java.io.ObjectInputStream;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.hibernate.type.SerializationException;
 import org.hibernate.Hibernate;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
+
 /**
  * <p>Assists with the serialization process and performs additional functionality based
  * on serialization.</p>
@@ -87,148 +59,187 @@
  * @author Stephen Colebourne
  * @author Jeff Varszegi
  * @author Gary Gregory
+ * @version $Id$
  * @since 1.0
- * @version $Id$
  */
 public final class SerializationHelper {
 
-	private static final Log log = LogFactory.getLog(SerializationHelper.class);
+	private static final Log log = LogFactory.getLog( SerializationHelper.class );
 
-    private SerializationHelper() {}
+	private SerializationHelper() {
+	}
 
-    // Clone
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Deep clone an <code>Object</code> using serialization.</p>
-     *
-     * <p>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 <code>Serializable</code>.</p>
-     *
-     * @param object  the <code>Serializable</code> 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
+	//-----------------------------------------------------------------------
+
+	/**
+	 * <p>Deep clone an <code>Object</code> using serialization.</p>
+	 *
+	 * <p>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 <code>Serializable</code>.</p>
+	 *
+	 * @param object the <code>Serializable</code> 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
-    //-----------------------------------------------------------------------
-    /**
-     * <p>Serializes an <code>Object</code> to the specified stream.</p>
-     *
-     * <p>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.</p>
-     *
-     * <p>The stream passed in is not buffered internally within this method.
-     * This is the responsibility of your application if desired.</p>
-     *
-     * @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 <code>outputStream</code> is <code>null</code>
-     * @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]" );
-		    }
-	    }
+	/**
+	 * <p>Serializes an <code>Object</code> to the specified stream.</p>
+	 *
+	 * <p>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.</p>
+	 *
+	 * <p>The stream passed in is not buffered internally within this method.
+	 * This is the responsibility of your application if desired.</p>
+	 *
+	 * @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 <code>outputStream</code> is <code>null</code>
+	 * @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 );
 
-    /**
-     * <p>Serializes an <code>Object</code> to a byte array for
-     * storage/serialization.</p>
-     *
-     * @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
-	 * ClassLoader (TCCL).  If there is no TCCL set, the classloader of the calling
-	 * class is used.
-     * <p/>
-	 * 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 <code>inputStream</code> is <code>null</code>
-     * @throws SerializationException (runtime) if the serialization fails
-     */
-    public static Object deserialize(InputStream inputStream) throws SerializationException {
-		return deserialize( inputStream, Thread.currentThread().getContextClassLoader() );
-    }
+	/**
+	 * <p>Serializes an <code>Object</code> to a byte array for
+	 * storage/serialization.</p>
+	 *
+	 * @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();
+	}
 
-    /**
-     * Deserializes an object from the specified stream using the Thread Context
+	// Deserialize
+	//-----------------------------------------------------------------------
+
+	/**
+	 * Deserializes an object from the specified stream using the Thread Context
+	 * ClassLoader (TCCL).
+	 * <p/>
+	 * Delegates to {@link #doDeserialize}
+	 *
+	 * @param inputStream the serialized object input stream, must not be null
+	 *
+	 * @return the deserialized object
+	 *
+	 * @throws IllegalArgumentException if <code>inputStream</code> is <code>null</code>
+	 * @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.
-     * <p/>
-     * The stream will be closed once the object is read. This avoids the need 
+	 * <p/>
+	 * 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.
-     * <p/>
-     * The stream passed in is not buffered internally within this method.  This is
+	 * <p/>
+	 * 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 <code>inputStream</code> is <code>null</code>
-     * @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 <code>inputStream</code> is <code>null</code>
+	 * @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();
 			}
@@ -242,7 +253,7 @@
 				try {
 					in.close();
 				}
-				catch (IOException ignore) {
+				catch ( IOException ignore ) {
 					// ignore
 				}
 			}
@@ -252,70 +263,118 @@
 		}
 	}
 
-    /**
-     * Deserializes an Object from an array of bytes.
+	/**
+	 * 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.
 	 * <p/>
 	 * Delegates to {@link #deserialize(byte[], ClassLoader)}
-     *
-     * @param objectData  the serialized object, must not be null
-     * @return the deserialized object
-     * @throws IllegalArgumentException if <code>objectData</code> is <code>null</code>
-     * @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 <code>objectData</code> is <code>null</code>
+	 * @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.
 	 * <p/>
 	 * 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 <code>objectData</code> is <code>null</code>
-     * @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 <code>objectData</code> is <code>null</code>
+	 * @throws SerializationException (runtime) if the serialization fails
+	 */
+	public static Object deserialize(byte[] objectData, ClassLoader loader) throws SerializationException {
+		return doDeserialize( wrap( objectData ), loader, defaultClassLoader(), hibernateClassLoader() );
+	}
 
 
 	/**
-	 * Custom ObjectInputStream implementation to more appropriately handle classloading
-	 * within app servers (mainly jboss - hence this class inspired by jboss's class of
-	 * the same purpose).
+	 * By default, to resolve the classes being deserialized JDK serialization uses the
+	 * classes loader which loaded the class which initiated the deserialization call.  Here
+	 * that would be hibernate classes.  However, there are cases where that is not the correct
+	 * class loader to use; mainly here we are worried about deserializing user classes in
+	 * environments (app servers, etc) where Hibernate is on a parent classes loader.  To
+	 * 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;
 		}
 
+		/**
+		 * {@inheritDoc}
+		 */
 		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 ( 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" );
+				}
+			}
+
+			// By default delegate to normal JDK deserialization which will use the class loader
+			// 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 );
+			}
+		}
 	}
 }



More information about the hibernate-commits mailing list