/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.startup;
import java.lang.instrument.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.jar.*;
public final class InstrumentationHolder implements Instrumentation
{
public static Instrumentation inst;
private static InstrumentationHolder wrappedInst;
public static String hostJREClassName;
@SuppressWarnings("unused")
public static void agentmain(String agentArgs, Instrumentation instrumentation)
{
set(instrumentation, null);
}
static void setHostJREClassName(String className)
{
hostJREClassName = className;
Class<?> regularHolderClass = getHolderClassFromSystemClassLoaderIfThisIsCustomClassLoader();
if (regularHolderClass != null) {
try {
Field field = getField(regularHolderClass, "hostJREClassName");
field.set(null, className);
}
catch (IllegalAccessException e) { throw new RuntimeException(e); }
}
}
private static Field getField(Class<?> aClass, String fieldName)
{
try { return aClass.getDeclaredField(fieldName); }
catch (NoSuchFieldException e) { throw new RuntimeException(e); }
}
public static InstrumentationHolder get()
{
if (inst == null) {
recoverInstrumentationFromHolderClassInSystemClassLoaderIfAvailable();
}
return wrappedInst;
}
private static void recoverInstrumentationFromHolderClassInSystemClassLoaderIfAvailable()
{
Class<?> regularHolderClass = getHolderClassFromSystemClassLoaderIfThisIsCustomClassLoader();
if (regularHolderClass != null) {
inst = readStaticField(regularHolderClass, "inst");
if (inst != null) {
hostJREClassName = readStaticField(regularHolderClass, "hostJREClassName");
invokeClearTransformersFromSystemCL(regularHolderClass);
wrappedInst = new InstrumentationHolder();
}
}
}
private static Class<?> getHolderClassFromSystemClassLoaderIfThisIsCustomClassLoader()
{
ClassLoader systemCL = ClassLoader.getSystemClassLoader();
if (InstrumentationHolder.class.getClassLoader() == systemCL) {
return null;
}
try { return Class.forName(InstrumentationHolder.class.getName(), true, systemCL); }
catch (ClassNotFoundException e) { return null; }
}
private static <T> T readStaticField(Class<?> aClass, String fieldName)
{
Field field = getField(aClass, fieldName);
try {
@SuppressWarnings("unchecked") T value = (T) field.get(null);
return value;
}
catch (IllegalAccessException e) { throw new RuntimeException(e); }
}
private static void invokeClearTransformersFromSystemCL(Class<?> regularHolderClass)
{
try {
Method method = regularHolderClass.getDeclaredMethod("clearTransformers");
method.invoke(null);
}
catch (NoSuchMethodException e) { throw new RuntimeException(e); }
catch (IllegalAccessException e) { throw new RuntimeException(e); }
catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); }
}
public static void clearTransformers()
{
for (ClassFileTransformer transformer : transformers) {
inst.removeTransformer(transformer);
}
transformers.clear();
}
boolean wasRecreated() { return transformers.isEmpty(); }
static Instrumentation set(Instrumentation instrumentation, String hostJREClassName)
{
if (wrappedInst != null) {
clearTransformers();
}
inst = instrumentation;
wrappedInst = instrumentation == null ? null : new InstrumentationHolder();
InstrumentationHolder.hostJREClassName = hostJREClassName;
return wrappedInst;
}
public static List<ClassFileTransformer> transformers;
private InstrumentationHolder()
{
Class<?> regularHolderClass = getHolderClassFromSystemClassLoaderIfThisIsCustomClassLoader();
if (regularHolderClass == null) {
transformers = new ArrayList<ClassFileTransformer>();
}
else {
transformers = readStaticField(regularHolderClass, "transformers");
}
}
@Override
public void addTransformer(ClassFileTransformer transformer, boolean canRetransform)
{
removePreviouslyAddedTransformersOfSameType(transformer);
inst.addTransformer(transformer, canRetransform);
transformers.add(transformer);
}
@Override
public void addTransformer(ClassFileTransformer transformer)
{
removePreviouslyAddedTransformersOfSameType(transformer);
inst.addTransformer(transformer);
transformers.add(transformer);
}
private void removePreviouslyAddedTransformersOfSameType(ClassFileTransformer transformer)
{
Class<?> transformerClass = transformer.getClass();
ClassLoader transformerCL = transformerClass.getClassLoader();
String transformerName = transformerClass.getName();
for (Iterator<ClassFileTransformer> itr = transformers.iterator(); itr.hasNext(); ) {
ClassFileTransformer previouslyAdded = itr.next();
Class<?> previousTransformerClass = previouslyAdded.getClass();
if (
previousTransformerClass.getClassLoader() != transformerCL &&
previousTransformerClass.getName().equals(transformerName)
) {
inst.removeTransformer(previouslyAdded);
itr.remove();
}
}
}
@Override
public boolean removeTransformer(ClassFileTransformer transformer)
{
transformers.remove(transformer);
return inst.removeTransformer(transformer);
}
@Override
public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException
{
inst.retransformClasses(classes);
}
@Override
public void redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException, UnmodifiableClassException
{
inst.redefineClasses(definitions);
}
@Override
public boolean isRetransformClassesSupported() { return true; }
@Override
public boolean isRedefineClassesSupported() { return true; }
@Override
public boolean isModifiableClass(Class<?> theClass) { return inst.isModifiableClass(theClass); }
@Override
public Class[] getAllLoadedClasses() { return inst.getAllLoadedClasses(); }
@Override
public Class[] getInitiatedClasses(ClassLoader loader) { return inst.getInitiatedClasses(loader); }
@Override
public long getObjectSize(Object objectToSize) { return inst.getObjectSize(objectToSize); }
@Override
public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {}
@Override
public void appendToSystemClassLoaderSearch(JarFile jarfile) {}
@Override
public boolean isNativeMethodPrefixSupported() { return false; }
@Override
public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {}
}