package org.hotswap.agent.util.classloader;
import org.hotswap.agent.logging.AgentLogger;
import org.hotswap.agent.util.scanner.ClassPathScanner;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
/**
* Add agent JAR URL to a classloader. This works only for webapp classloader of URLClassLoader type
* and webapp first precedence.
*
* @author Jiri Bubnik
*/
@Deprecated
public class ClassLoaderURLPatcher implements ClassLoaderPatcher {
private static AgentLogger LOGGER = AgentLogger.getLogger(ClassLoaderURLPatcher.class);
@Override
public boolean isPatchAvailable(ClassLoader classLoader) {
return classLoader instanceof URLClassLoader;
}
@Override
public void patch(ClassLoader classLoaderFrom, String pluginPath, ClassLoader classLoader, ProtectionDomain protectionDomain) {
Set<ClassLoader> patchedClassLoaders = new HashSet<ClassLoader>();
if (classLoader != getClass().getClassLoader() && !patchedClassLoaders.contains(classLoader)) {
synchronized (patchedClassLoaders) {
if (!patchedClassLoaders.contains(classLoader)) {
patchedClassLoaders.add(classLoader);
if (classLoader instanceof URLClassLoader) {
doPatchUrlClassLoader((URLClassLoader) classLoader);
} else {
LOGGER.debug("Unable to patch ClassLoader {} of type {}. Only URLClassLoader(s) can be currently patched.",
classLoader, classLoader.getClass().getName());
}
}
}
}
}
private static final String CHECK_RESOURCE = "org/hotswap/agent/config/PluginManager.class";
private void doPatchUrlClassLoader(URLClassLoader classLoader) {
Class<URLClassLoader> classLoaderClass = URLClassLoader.class;
try {
String url = getClass().getClassLoader().getResource(CHECK_RESOURCE).toURI().toString();
url = url.substring(0, url.length() - CHECK_RESOURCE.length());
url = removeJarPrefixSuffix(url);
Field field = classLoaderClass.getDeclaredField("ucp");
field.setAccessible(true);
Object ucp = field.get(classLoader);
Method method = classLoaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(classLoader, new Object[]{new URL(url)});
LOGGER.debug("Classloader {} patched - Added URL '{}' to classpath.", classLoader, url);
} catch (Throwable e) {
LOGGER.error("Error patching classloader {}", e, classLoader);
}
}
/**
* Resolve JAR file path in format: "jar:file:/J:/HotSwapAgent/target/HotSwapAgent-1.0-SNAPSHOT.jar!/"
* to a valid URL file:/J:/HotSwapAgent/target/HotSwapAgent-1.0-SNAPSHOT.jar
*
* @param url URL to resolve
* @return modified URL
*/
private String removeJarPrefixSuffix(String url) {
if (url.startsWith(ClassPathScanner.JAR_URL_PREFIX)) {
url = url.substring(ClassPathScanner.JAR_URL_PREFIX.length());
}
if (url.endsWith(ClassPathScanner.JAR_URL_SEPARATOR)) {
url = url.substring(0, url.length() - ClassPathScanner.JAR_URL_SEPARATOR.length());
}
return url;
}
}