/**
*
*/
package jframe.core.plugin.loader;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jframe.core.plugin.Plugin;
import jframe.core.plugin.annotation.InjectPlugin;
import jframe.core.plugin.annotation.Injector;
/**
* <p>
* 功能:
* <li>插件类加载</li>
* <li>动态库加载</li>
* </P>
*
* @author dzh
* @date Sep 12, 2013 1:26:44 PM
* @since 1.0
*/
public class PluginClassLoader extends URLClassLoader {
private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
private Plugin _plugin;
private PluginCase _case;
protected PluginClassLoaderContext plc;
/**
* to fix issue java.lang.ClassCircularityError 记录正在注入中的类名 TODO 需要验证
*/
private List<Class<?>> injectingClazz = new LinkedList<Class<?>>();
protected synchronized Class<?> findInjectingClass(String name) {
for (Class<?> clazz : injectingClazz) {
if (clazz.getName().equals(name))
return clazz;
}
return null;
}
/**
* @param pRef
*/
public PluginClassLoader(URL[] urls, PluginCase pc, PluginClassLoaderContext plc) {
super(urls, pc.getClass().getClassLoader());
this._case = pc;
if (getParent() == null) {
throw new NullPointerException("PluginClassLoader's parent is null");
}
try {
addURL(new URL("file:" + pc.getJarPath()));
for (String lib : pc.getPluginLib()) {
addURL(new URL("file:" + pc.getCacheLibPath() + File.separator + lib));
}
} catch (MalformedURLException e) {
LOG.error("Exception when create plugin:" + e.getLocalizedMessage());
dispose();
}
this.plc = plc;
this.plc.regPluginClassLoader(this);
}
public PluginClassLoaderContext getPluginClassLoaderContext() {
return plc;
}
public PluginClassLoader(PluginCase pc, PluginClassLoaderContext plc) {
this(new URL[] {}, pc, plc);
}
public void addURL(URL url) {
super.addURL(url);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// since 1.7
// synchronized (getClassLoadingLock(name)) {
// get loaded class
Class<?> c = findLoadedClass(name);
if (c == null) {
// get injecting class
c = findInjectingClass(name);
if (c != null)
return c;
// load import class
if (getPluginCase().getImportClass().contains(name)) {
PluginClassLoader pcl = getPluginClassLoaderContext().findImportClassLoader(name);
if (pcl != null)
return pcl.loadClass(name);
}
// load plug-in class
try {
c = findPluginClass(name);
if (isInjector(c)) {
injectingClazz.add(c);
injectAnnocation(c);
injectingClazz.remove(c);
}
} catch (Exception e) {
LOG.error(e.getMessage());
}
}
if (c == null) {
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException e) {
c = loadImportPlugin(name);
} catch (Exception e) {
LOG.warn(e.getMessage(), e.fillInStackTrace());
}
}
if (c == null)
throw new ClassNotFoundException(name);
if (resolve) {
resolveClass(c);
}
return c;
}
protected synchronized Plugin createPlugin(PluginCase pc) {
if (_plugin != null)
return _plugin;
try {
_plugin = (Plugin) loadClass(pc.getPluginClass()).newInstance();
} catch (Exception e) {
LOG.error("Create Plugin Error: " + e.getLocalizedMessage());
}
return _plugin;
}
@SuppressWarnings("unchecked")
protected Class<? extends Plugin> loadPluginClass(PluginCase pc) throws ClassNotFoundException {
return (Class<? extends Plugin>) loadClass(pc.getPluginClass());
}
public synchronized Plugin getPlugin() {
if (_plugin == null) {
createPlugin(getPluginCase());
}
return _plugin;
}
/**
*
* @param name
* @return
* @throws ClassNotFoundException
*/
protected Class<?> findPluginClass(String name) {
Class<?> c = null;
try {
// load from plug-in
c = findClass(name);
} catch (ClassNotFoundException e) {
}
return c;
}
/**
* load class from import-plugin
*
* @param name
* @return
*/
protected Class<?> loadImportPlugin(String name) {
if (LOG.isDebugEnabled()) {
LOG.debug("LoadImportPlugin -> {}", name);
}
Class<?> clazz = null;
for (String plugin : getPluginCase().getImportPlugin()) {
try {
PluginClassLoader pcl = plc.findPluginClassLoader(plugin);
if (pcl == null) // TODO
continue;
clazz = pcl.loadClass(name);
break;
} catch (ClassNotFoundException e) {
}
}
return clazz;
}
protected boolean isInjector(Class<?> clazz) {
try {
return clazz != null && clazz.isAnnotationPresent(Injector.class) ? true : false;
} catch (Exception e) {
}
return false;
}
/**
*
* @param clazz
* @throws Exception
*/
protected void injectAnnocation(Class<?> clazz) throws Exception {
if (clazz == null)
return;
for (Field f : clazz.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) {
if (f.isAnnotationPresent(InjectPlugin.class)) {
injectPlugin(f);
break;
}
}
}
}
protected void injectPlugin(Field f) {
try {
f.setAccessible(true);
f.set(null, getPlugin());
} catch (Exception e) {
LOG.error(e.getMessage());
}
if (LOG.isDebugEnabled()) {
LOG.debug("InjectPlugin {} -> {}", getPlugin(), f.getName());
}
}
@Override
protected String findLibrary(String libname) {
String path = _case.getCacheDllPath() + File.separator + libname;
if (new File(path).exists())
return path;
return null;
}
/**
* <p>
* 查询次序:
* <li>相對插件的cache目录</li>
* <li>绝对路径</li>
* <li>相對jar包</li>
* </p>
*/
@Override
public URL findResource(String name) {
try {
File f = new File(_case.getCachePath() + File.separator + name);
if (f.exists())
return f.toURI().toURL();
} catch (MalformedURLException e) {
LOG.warn(e.getLocalizedMessage());
}
try {
File f = new File(name);
if (f.exists())
return f.toURI().toURL();
} catch (MalformedURLException e) {
LOG.warn(e.getLocalizedMessage());
}
return super.findResource(name);
}
public PluginCase getPluginCase() {
return _case;
}
/**
* jdk1.7之后调用close
*/
public void dispose() {
// _case = null;
Method m = null;
try {
m = getClass().getMethod("close", new Class[0]);
if (m != null) {
m.invoke(this, new Object[0]);
}
} catch (Exception e) {
LOG.warn("Exception when PluginClassLoader close():" + e.getMessage());
}
injectingClazz = null;
plc.unregPluginClassLoader(this);
}
}