com.alee.utils.reflection.Unsafe Maven / Gradle / Ivy
The newest version!
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.utils.reflection;
import com.alee.utils.ReflectUtils;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.WeakHashMap;
/**
* {@code sun.misc.Unsafe} wrapper.
*
* @author Mikle Garin
* @author Joe Walnes
*/
public final class Unsafe
{
/**
* {@link WeakHashMap} that caches serialized data.
*/
private static Map serializedDataCache;
/**
* Returns new {@link Class} instance.
* Uses {@code sun.misc.Unsafe} whenever it is possible to avoid using {@link Class} constructor.
* If unable to access {@code sun.misc.Unsafe} - {@link Class} will be instantiated through other means.
*
* @param clazz {@link Class} to be instantiated
* @param {@link Class} type
* @return new {@link Class} instance
*/
public static T allocateInstance ( final Class clazz )
{
if ( clazz != null )
{
T instance;
try
{
instance = allocateInstanceThroughUnsafe ( clazz );
}
catch ( final Exception e )
{
try
{
instance = allocateInstanceThroughReflection ( clazz );
}
catch ( final Exception ex )
{
if ( Serializable.class.isAssignableFrom ( clazz ) )
{
try
{
instance = allocateInstanceThroughSerialization ( clazz );
}
catch ( final Exception exx )
{
throw new ReflectionException ( "Unable to instantiate class: " + clazz, ex );
}
}
else
{
throw new ReflectionException ( "Unable to instantiate class: " + clazz, ex );
}
}
}
return instance;
}
else
{
throw new ReflectionException ( "Class type must not be null" );
}
}
/**
* Returns new {@link Class} instance created using {@code sun.misc.Unsafe}.
* This will allow us to skip constructors usage to avoid unwanted behavior.
*
* @param clazz {@link Class} to be instantiated
* @param {@link Class} type
* @return new {@link Class} instance created using {@code sun.misc.Unsafe}
* @throws ClassNotFoundException if {@code sun.misc.Unsafe} class cannot be found
* @throws NoSuchFieldException if {@code sun.misc.Unsafe#theUnsafe} field cannot be found
* @throws IllegalAccessException if access was denied for one of the reflection calls
* @throws NoSuchMethodException if {@code sun.misc.Unsafe#allocateInstance(Class)} cannot be found
* @throws InvocationTargetException if {@code sun.misc.Unsafe#allocateInstance(Class)} invocation failed
*/
private static T allocateInstanceThroughUnsafe ( final Class clazz )
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
final Class> unsafe = ReflectUtils.getClass ( "sun.misc.Unsafe" );
final Object theUnsafe = ReflectUtils.getStaticFieldValue ( unsafe, "theUnsafe" );
return ReflectUtils.callMethod ( theUnsafe, "allocateInstance", clazz );
}
/**
* Returns new {@link Class} instance created using default constructor if available.
* This is not a perfect solution, but might be necessary if {@code sun.misc.Unsafe} is not available.
*
* @param clazz {@link Class} to be instantiated
* @param {@link Class} type
* @return new {@link Class} instance created using default constructor if available
* @throws InstantiationException if {@link Class} instance creation failed
* @throws IllegalAccessException if cannot access {@link Class} or its default constructor
* @throws InvocationTargetException if calling {@link Class} default constructor failed
* @throws NoSuchMethodException if there is no default {@link Class} constructor
*/
private static T allocateInstanceThroughReflection ( final Class clazz )
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
return ReflectUtils.createInstance ( clazz );
}
/**
* Returns new {@link Class} instance using JDK serialization-deserialization.
* This might work in a similar way {@code sun.misc.Unsafe} allocation works depending on implementation.
* This method was borrowed from XStream {@link com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider}.
*
* @param clazz {@link Class} to be instantiated
* @param {@link Class} type
* @return new {@link Class} instance using JDK serialization-deserialization
* @throws IOException if an IO operation failed
* @throws ClassNotFoundException if read instance {@link Class} cannot be found
* @author Joe Walnes
*/
private synchronized static T allocateInstanceThroughSerialization ( final Class clazz )
throws IOException, ClassNotFoundException
{
// Ensure data cache map is initialized
if ( serializedDataCache == null )
{
serializedDataCache = new WeakHashMap ();
}
// Retrieving or creating serialized dummy data
byte[] data = serializedDataCache.get ( clazz );
if ( data == null )
{
final ByteArrayOutputStream bytes = new ByteArrayOutputStream ();
final DataOutputStream stream = new DataOutputStream ( bytes );
stream.writeShort ( ObjectStreamConstants.STREAM_MAGIC );
stream.writeShort ( ObjectStreamConstants.STREAM_VERSION );
stream.writeByte ( ObjectStreamConstants.TC_OBJECT );
stream.writeByte ( ObjectStreamConstants.TC_CLASSDESC );
stream.writeUTF ( clazz.getName () );
stream.writeLong ( ObjectStreamClass.lookup ( clazz ).getSerialVersionUID () );
stream.writeByte ( 2 ); // classDescFlags (2 = Serializable)
stream.writeShort ( 0 ); // field count
stream.writeByte ( ObjectStreamConstants.TC_ENDBLOCKDATA );
stream.writeByte ( ObjectStreamConstants.TC_NULL );
data = bytes.toByteArray ();
serializedDataCache.put ( clazz, data );
}
// Creating object input stream to read object instance from a dummy data
final ObjectInputStream in = new ObjectInputStream ( new ByteArrayInputStream ( data ) )
{
@Override
protected Class resolveClass ( final ObjectStreamClass desc ) throws ClassNotFoundException
{
return Class.forName ( desc.getName (), false, clazz.getClassLoader () );
}
};
// Reading object instance
return ( T ) in.readObject ();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy