package com.hortonworks.registries.common.util;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
public class ProxyUtil<O> {
private final Class<O> interfaceClazz;
private final ClassLoader parentClassLoader;
private final ConcurrentHashMap<String, ClassLoader> cachedClassLoaders;
public ProxyUtil(Class<O> interfaceClazz) {
this(interfaceClazz, ProxyUtil.class.getClassLoader());
}
public ProxyUtil(Class<O> interfaceClazz, ClassLoader parentClassLoader) {
this.interfaceClazz = interfaceClazz;
this.parentClassLoader = parentClassLoader;
this.cachedClassLoaders = new ConcurrentHashMap<>();
}
public O loadClassFromJar(String jarPath, String classFqdn)
throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
ClassLoader classLoader;
if (isClassLoadedFromParent(classFqdn)) {
classLoader = parentClassLoader;
} else {
classLoader = findCachedClassLoader(jarPath);
if (classLoader == null) {
classLoader = getJarAddedClassLoader(jarPath);
}
}
O actualObject = initInstanceFromClassloader(classFqdn, classLoader);
return createClassLoaderAwareProxyInstance(classLoader, actualObject);
}
private ClassLoader findCachedClassLoader(String jarPath) {
return cachedClassLoaders.get(jarPath);
}
private O createClassLoaderAwareProxyInstance(ClassLoader classLoader, O actualObject) {
InvocationHandler handler = new ClassLoaderAwareInvocationHandler(classLoader, actualObject);
return (O) Proxy.newProxyInstance(parentClassLoader, new Class<?>[]{interfaceClazz}, handler);
}
private O initInstanceFromClassloader(String classFqdn, ClassLoader classLoader) throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class parserClass = Class.forName(classFqdn, true, classLoader);
return (O) parserClass.newInstance();
}
private ClassLoader getJarAddedClassLoader(String jarPath) throws MalformedURLException {
ClassLoader classLoader = new MutableURLClassLoader(new URL[0], parentClassLoader);
URL u = (new File(jarPath).toURI().toURL());
((MutableURLClassLoader) classLoader).addURL(u);
ClassLoader oldCl = cachedClassLoaders.putIfAbsent(jarPath, classLoader);
if (oldCl != null) {
// discard and pick old thing
classLoader = oldCl;
}
return classLoader;
}
private boolean isClassLoadedFromParent(String className) {
try {
Class.forName(className, false, parentClassLoader);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}