package rocks.inspectit.agent.java.sensor.method.special;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rocks.inspectit.agent.java.config.impl.SpecialSensorConfig;
import rocks.inspectit.agent.java.hooking.ISpecialHook;
import rocks.inspectit.agent.java.util.ClassLoadingUtil;
/**
* Hook for the class loading delegation. In the
* {@link #beforeBody(long, Object, Object[], SpecialSensorConfig)} we check if the class should/can
* be loaded by our class loader.
*
* @author Ivan Senic
*
*/
public class ClassLoadingDelegationHook implements ISpecialHook {
/**
* Fully qualified name of the reflect ASM Access class loader.
*/
private static final String REFLECTASM_ACCESS_CLASS_LOADER_FQN = "com.esotericsoftware.reflectasm.AccessClassLoader";
/**
* Logger of the class.
*/
private static final Logger LOG = LoggerFactory.getLogger(ClassLoadingDelegationHook.class);
/**
* {@inheritDoc}
*/
@Override
public Object beforeBody(long methodId, Object object, Object[] parameters, SpecialSensorConfig ssc) {
return loadClass(object, parameters);
}
/**
* {@inheritDoc}
*/
@Override
public Object afterBody(long methodId, Object object, Object[] parameters, Object result, SpecialSensorConfig ssc) {
return null;
}
/**
* Loads class with the given parameters that have been passed to the target class loader.
* <p>
* Loading will be delegated only if parameters are of size 1 and that single parameter is
* String type.
*
* @see #loadClass(String)
* @param classLoader
* Class loader loading the class (object where the method is executed).
* @param params
* Original parameters passed to class loader.
* @return Loaded class or <code>null</code>.
*/
private Class<?> loadClass(Object classLoader, Object[] params) {
if ((null != params) && (params.length == 1)) {
Object p = params[0];
if (p instanceof String) {
return loadClass(classLoader, (String) p);
}
}
return null;
}
/**
* Delegates the class loading to the {@link #inspectItClassLoader} if the class name starts
* with {@value #CLASS_NAME_PREFIX}. Otherwise loads the class with the target class loader. If
* the inspectIT class loader throws {@link ClassNotFoundException}, the target class loader
* will be used.
*
* @param classLoader
* Class loader loading the class (object where the method is executed).
* @param className
* Class name.
* @return Loaded class or <code>null</code> if it can not be found with inspectIT class loader.
*/
private Class<?> loadClass(Object classLoader, String className) {
if (loadWithInspectItClassLoader(classLoader, className)) {
try {
return getInspectITClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
LOG.warn("Class " + className + " could not be loaded with the inspectIT class loader, although it has the correct prefix.", e);
return null;
}
} else {
return null;
}
}
/**
* Defines if the class should be loaded with our class loader. Only if the class starts with
* the inspectIT prefix and the class loader trying to load the class is not the
* {@value #REFLECTASM_ACCESS_CLASS_LOADER_FQN}.
*
* @param classLoader
* Class loader loading the class (object where the method is executed).
* @param className
* Name of the class to load.
* @return True if we should try to load this with the inspectIT class loader.
*/
private boolean loadWithInspectItClassLoader(Object classLoader, String className) {
return (ClassLoadingUtil.isInspectITClass(className) || ClassLoadingUtil.isOpenTracingClass(className)) && !REFLECTASM_ACCESS_CLASS_LOADER_FQN.equals(classLoader.getClass().getName());
}
/**
* Returns inspectIT class loader.
*
* @return Returns inspectIT class loader.
*/
private ClassLoader getInspectITClassLoader() {
return getClass().getClassLoader();
}
}