/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* A simple programm illustrates dynamic bytecode loading, it does following job:
*
* 1. Load content of TestClass.class file into memory, as an array of bytes
*
* 2. Generate the object Class<TestClass> from this array of bytes
*
* 3. Instantiate an object of class TestClass generated in second step
*
* 4. Invoke method on the object
*
* @author <a href="hoang281283@gmail.com">Minh Hoang TO</a>
* @date 4/26/11
*/
public class TestLoadingClass
{
/**
* Load content of a .class file into memory as an array of bytes
*
* @param relativePath
* @return
*/
private static byte[] loadClassFileToMemory(String relativePath)
{
FileInputStream inputStream = null;
try
{
File file = new File(TestLoadingClass.class.getResource(relativePath).toURI());
inputStream = new FileInputStream(file);
int size = (int)inputStream.getChannel().size();
byte[] bytes = new byte[size];
inputStream.read(bytes);
return bytes;
}
catch(Exception ex)
{
ex.printStackTrace();
return null;
}
finally
{
try
{
inputStream.close();
}
catch(IOException IOEx)
{
}
}
}
/**
* Define the Class object from array of bytes. Reflection is used as defineXXX methods are protected
*
* @param className
* @param classLoader
* @param bytes
* @return
*/
private static Class defineClassFromBytes(String className, ClassLoader classLoader, byte[] bytes)
{
try{
//Don't know why this does not work on ClassLoader class, that forces me to use ugly loop
//Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, new Byte[0].getClass(), Integer.class, Integer.class);
Method[] declaredMethods = ClassLoader.class.getDeclaredMethods();
Method defineClassMethod = null;
for(Method method : declaredMethods)
{
if(method.getName().equals("defineClass") && method.getParameterTypes().length == 4)
{
defineClassMethod = method;
break;
}
}
defineClassMethod.setAccessible(true);
return (Class)defineClassMethod.invoke(classLoader, className, bytes, 0, bytes.length);
}
catch(IllegalAccessException IllegalAccesEx)
{
IllegalAccesEx.printStackTrace();
}
catch(InvocationTargetException InvocationEx)
{
InvocationEx.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception
{
byte[] contentOfLoadedClass = TestLoadingClass.loadClassFileToMemory("./TestClass.class");
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = defineClassFromBytes("TestClass", cl, contentOfLoadedClass);
if(clazz == null)
{
System.out.println("Could not convert array of bytes into Class object");
}
else
{
TestClass instance = (TestClass)clazz.newInstance();
instance.printClassMetaData();
}
}
}
/**
* Put the TestClass here so test doers need not care about classpath
*/
class TestClass
{
static
{
System.out.println("The static block is executing, the class loader is linking your TestClass class. Congrats!\n");
}
public void printClassMetaData()
{
System.out.println("Fully qualified name: " + this.getClass().getCanonicalName());
System.out.println("Class loader info: " + this.getClass().getClassLoader().toString());
}
}