HHH-4786 - SerializableType + custom Serializable class + L2 cache causes problems
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18544 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
9256bc30e5
commit
fca1248487
|
@ -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
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <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
|
||||
/**
|
||||
* <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();
|
||||
}
|
||||
|
||||
// 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/>
|
||||
* 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() );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
@ -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.
|
||||
* <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() );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue