/**
* 插件定义信息接口,包括以下信息:<br/>
* <b>插件名称</b><br/>
* <b>插件路径</b><br/>
* <b>插件类型</b><br/>
* <b>插件提供商</b><br/>
* <b>插件激活器</b><br/>
* <b>插件依赖的父插件定义集</b><br/>
* <b>插件所有属性信息</b><br/>
*/
public interface IPluginDefinition extends Serializable{
/**
* 获取插件的唯一名称
* @return 插件的唯一名称
*/
public String getName();
/**
* 获取插件的URL路径
* @return 插件的URL路径
*/
public URL getPath();
/**
* 获取插件的提供商
* @return 插件的提供商
*/
public String getProvider();
/**
* 获取插件的激活器
* @return 插件的激活器
*/
public String getActivator();
/**
* 获取插件的类路径
* @return 插件类路径
*/
public IPluginClassPath getClassPath();
/**
* 获取插件配置文件中的所有属性信息
* @return 插件配置文件中的所有属性信息
*/
public Attributes getConfig();
}
public interface IPluginModule {
/**
* 加载插件
* @param pluginPath
*/
public void load(File pluginPath);
/**
* 卸载当前插件
*/
public void unload();
/**
* 插件目录
* @return
*/
public File getPluginPath();
/**
* 标识是否已经加载过
* @return
*/
public boolean isLoaded();
/**
* 获取插件激活器
*/
public IPluginActivator getPluginActivator();
/**
* 获取该插件的ClassLoader
* @return
*/
public ClassLoader getPluginLoader();
/**
* 获取插件的名字
* @return
*/
public String getPluginName();
/**
* 获取该插件的监控节点配置以及服务
* @return
*/
public IPluginDefinition getPluginDefinition();
}
/**
* 插件配置解析,生成PluginDefinition对象
*/
public class PluginResolver {
private static final String PROTOCOL_FILE = "file";
private static final String JAR = ".jar";
private static final String LIB = "lib";
private static final String CLASS_PATH = "classpath";
private static final String AGENT_PLUGIN_XML = "datacenter-plugin.xml";
public static PluginResolver INSTANCE = new PluginResolver();
private Map<String, IPluginResolver> resolvers = new HashMap<String, IPluginResolver>(4);
static {
INSTANCE.resolvers.put(PROTOCOL_FILE, new FolderPathResolver());
}
static interface IPluginResolver {
PluginDefinition resolvePlugin(XMLBuilder builder, URL url) throws Exception;
}
public PluginDefinition resolvePlugin(URL url) throws PluginResolveException {
String protocol = url.getProtocol();
IPluginResolver resolver = resolvers.get(protocol);
if (resolver == null) {
throw new PluginResolveException(String.format("Unsupport protocol [%s]", protocol));
}
try {
Document document = DocumentDescriptor.INSTANCE.loadPluginXML(url);
return resolver.resolvePlugin(XMLBuilder.createBuilder(document), url);
} catch (Exception e) {
throw new PluginResolveException("resolve config error!", e);
}
}
private static class FolderPathResolver implements IPluginResolver {
public PluginDefinition resolvePlugin(XMLBuilder builder, URL url) throws Exception {
String pluginName = loadPluginName(builder);
String provider = loadPluginProvider(builder);
String activator = loadPluginActivator(builder);
Attributes attributes = loadAttribtes(builder.findNode("/plugin/attributes"));
Assert.isNotNull(pluginName, "plugin Name must be not null");
Assert.isNotNull(activator, "activator must be not null");
IPluginClassPath classpath = new PluginClassPath(url, attributes.getString(CLASS_PATH));
return new PluginDefinition(pluginName, url, activator, provider, classpath , attributes);
}
private String loadPluginName(XMLBuilder builder) throws Exception {
String pluginName = XMLBuilder.getAttribute(builder.findNode("/plugin"), "name");
if(pluginName != null) {
return pluginName.trim();
}
return null;
}
private String loadPluginProvider(XMLBuilder builder) throws Exception {
String provider = XMLBuilder.getAttribute(builder.findNode("/plugin"), "provider");
if(provider != null) {
return provider.trim();
}
return null;
}
private String loadPluginActivator(XMLBuilder builder) throws Exception {
String activator = XMLBuilder.getAttribute(builder.findNode("/plugin//controller"), "class");
if(activator != null) {
return activator.trim();
}
return null;
}
private static Attributes loadAttribtes(Node parent) throws Exception {
Attributes attributes = new Attributes();
NodeList attributeNodes = parent.getChildNodes();
for (int i = 0; i < attributeNodes.getLength(); i++) {
Node node = attributeNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("attribute")) {
// get attributes
String name = XMLBuilder.getAttribute(node, "name");
String value = XMLBuilder.getAttribute(node, "value");
attributes.put(name, value);
}
}
return attributes;
}
}
}
/**
* 插件类路径信息定义实现类,包含对路径的解析和处理,包含目录和jar包两种
* 形式。
*
* 目录形式:
* 1. 该目录当作classpath
* 2. 该目录直接子层的jar包也纳入classpath
*
* jar包形式:
* 1. 直接将jar包的URL纳入classpath
*
* 默认打入插件下面的lib包
*/
public class PluginClassPath implements IPluginClassPath {
private static final String JAR = ".jar";
private static final String LIB = "lib";
private List<URL> pathURL = new ArrayList<URL>();
PluginClassPath(URL pluginPath, String path) throws PluginResolveException {
resolve(pluginPath); //解析默认
resolve(pluginPath, path);
}
@SuppressWarnings("deprecation")
protected void resolve(URL pluginPath) throws PluginResolveException {
File libPath = new File(pluginPath.getFile(), LIB);
if (libPath.exists()) {
File[] files = libPath.listFiles(new FileFilter() {
public boolean accept(File file) {
if (file.getName().endsWith(JAR)) {
return true;
}
return false;
}
});
for (File file : files) {
try {
pathURL.add(file.toURL());
} catch (MalformedURLException e) {
throw new PluginResolveException(e);
}
}
}
}
/**
* 解析类路径URL地址,首先基于“,”进行路径分割,分为两种类别处理:目录和jar包。
*
* 目录形式:
* 1. 该目录当作classpath
* 2. 该目录直接子层的jar包也纳入classpath
*
* jar包形式:
* 1. 直接将jar包的URL纳入classpath
*
* @param pluginDir 插件路径
* @param path classpath相对路径,以“,”分割
* @throws PluginResolveException 解析错误时抛出该异常
*/
protected void resolve(URL pluginDir, String path) throws PluginResolveException{
if(StringUtil.isNullOrEmpty(path)) {
//do nothing
} else {
String[] array = path.split(",");
List<URL> list = new ArrayList<URL>(array.length);
for(String p : array) {
if(StringUtil.isNullOrEmpty(p)) {
continue;
}
p = p.trim();
try {
URL pathURL = null;
File f = new File(pluginDir.getFile(), p);
if(f.exists()) {
if(f.isFile() && f.getName().endsWith(".jar")) {
pathURL = new URL(
pluginDir.getProtocol(), pluginDir.getHost(),
pluginDir.getPort(), pluginDir.getFile() + p);
} else {
pathURL = new URL(
pluginDir.getProtocol(), pluginDir.getHost(),
pluginDir.getPort(), pluginDir.getFile() + p + "/");
File[] jars = f.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
});
if(jars != null) {
for(File jar : jars) {
list.add(FileUtil.getFileURL( jar ));
}
}
}
list.add(pathURL);
} else {
throw new PluginResolveException("Can not find classpath in directory " + f.getAbsolutePath());
}
} catch (MalformedURLException e) {
throw new PluginResolveException(e);
}
}
pathURL.addAll(list);
//file:/F:/XX/datacenter/lib/gson-1.7.1.jar, file:/F:/xx/datacenter/lib/joe.jar, file:/F:/xxx/datacenter/lib/spring.jar, file:/F:/xxx/datacenter/bin/]
}
}
public URL[] getPath() {
return pathURL.toArray(new URL[0]);
}
}
public class PluginManager {
protected Logger logger = LoggerManager.getLogger(getClass());
public static final String PLUGIN_LISTS_XML = "plugin-lists.xml";
private static final String XML_PLUGIN = "/plugins//plugin";
private File pluginsPath = new File(DataCenterConfig.getHome(), "plugins");
private Map<String, IPluginModule> plugins = new ConcurrentHashMap<String, IPluginModule>();
private static final List<IPluginActuator> startActuators = new LinkedList<IPluginActuator>();
private static final List<IPluginActuator> stopActuators = new LinkedList<IPluginActuator>();
static {
// 1. 创建执行器实例
ExtensionActuator extension = new ExtensionActuator();
ActivateActuator activate = new ActivateActuator();
// 2. 注册启动执行器
startActuators.add(extension);
startActuators.add(activate);
// 3. 注册关闭执行器
stopActuators.add(activate);
stopActuators.add(extension);
}
/**
* 初始化的时候安装所有的插件,插件可以被卸载
*/
public void init() throws Exception {
installPlugins(new File(pluginsPath, PLUGIN_LISTS_XML));
}
public void start() throws Exception{
for (IPluginModule plugin : plugins.values()) {
startPlugin(plugin);
}
}
private void startPlugin(IPluginModule plugin) {
IPluginActivator activator = plugin.getPluginActivator();
if (activator == null) {
return;
}
int state = activator.getState();
if (state == IPluginActivator.RUNNING || state == IPluginActivator.STARTING) { // 只要插件的状态不是RESOLVER,
return; // 则不能启动该插件,因为该插件可能正在停止或者正在启动过程中或者已经启动
}
try {
for (IPluginActuator actuator : startActuators) {
actuator.start(plugin);
}
} catch (RuntimeException e) {
logger.log(Level.SEVERE, "Initialize plugin actuator fail", e);
throw e;
}
}
private void stopPlugin(IPluginModule plugin) {
logger.info("stoping plugin:" + plugin.getPluginName());
IPluginActivator activator = plugin.getPluginActivator();
if (activator == null) {
return;
}
int state = activator.getState();
if (state == IPluginActivator.STOPPED || state == IPluginActivator.STOPPING) { // 只要插件的状态不是RESOLVER,
return; // 则不能启动该插件,因为该插件可能正在停止或者正在启动过程中或者已经启动
}
try {
for (IPluginActuator actuator : stopActuators) {
actuator.stop(plugin);
}
} catch (RuntimeException e) {
logger.log(Level.SEVERE, "stop plugin actuator fail", e);
throw e;
}
logger.info("stoped plugin:" + plugin.getPluginName());
}
public void reloadPlugin(String pluginName) throws Exception {
IPluginModule plugin = plugins.get(pluginName);
if(plugin != null) {
reloadPlugin(plugin);
}
}
private void reloadPlugin(IPluginModule plugin) throws Exception {
String pluginName = plugin.getPluginName();
logger.info("reload plugin " + pluginName);
if (plugins.get(pluginName) != null) {
unInstallPluin(pluginName);
startPlugin(loadPlugin(plugin.getPluginPath()));
}
}
public void destory() throws Exception {
for (IPluginModule plugin : getPlugins()) {
stopPlugin(plugin);
plugin.unload();
}
plugins.clear();
}
public void installPlugin(String folder) throws Exception {
IPluginModule plugin = loadPlugin(folder, null);
if (plugin != null) {
plugins.put(plugin.getPluginName(), plugin);
PluginContext.addPluginModule(plugin);
}
}
public void unInstallPluin(String pluginName) throws Exception {
IPluginModule plugin = plugins.get(pluginName);
//先停止插件,然后卸载插件
stopPlugin(plugin);
if(plugin != null) {
plugin.unload();
}
plugins.remove(pluginName);
}
private void installPlugins(File path) throws Exception {
XMLBuilder builder = XMLBuilder.createBuilder(path);
NodeList pluginNodes = builder.findNodeList(XML_PLUGIN);
for (int i = 0; i < pluginNodes.getLength(); i++) {
Node node = pluginNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String folder = XMLBuilder.getAttribute(node, "folder");
String enable = XMLBuilder.getAttribute(node, "enable");
String filePath = XMLBuilder.getAttribute(node, "path");
if (Boolean.valueOf(enable)) {
IPluginModule plugin = loadPlugin(folder, filePath);
if(plugin != null) {
plugins.put(plugin.getPluginName(), plugin);
PluginContext.addPluginModule(plugin);
}
}
}
}
}
private IPluginModule loadPlugin(String folder, String filePath) {
if(StringUtil.isNullOrEmpty(filePath)) {
File pluginFolder = new File(pluginsPath, folder);
return loadPlugin(pluginFolder);
} else {
File pluginFolder = new File(filePath, folder);
return loadPlugin(pluginFolder);
}
}
private IPluginModule loadPlugin(File pluginFolder) {
logger.info("start load plugin:" + pluginFolder.getName());
if (pluginFolder.exists() && pluginFolder.isDirectory()) {
IPluginModule plugin = new PluginModule();
plugin.load(pluginFolder);
logger.info("plugin loaded:" + pluginFolder.getName());
return plugin;
} else {
logger.severe("plugin folder error:" + pluginFolder.getName());
return null;
}
}
public Collection<IPluginModule> getPlugins() {
return Collections.unmodifiableCollection(plugins.values());
}
public File getPluginsPath() {
return pluginsPath;
}
public void setPluginsPath(File pluginsPath) {
this.pluginsPath = pluginsPath;
}
}
public class PluginModule implements IPluginModule {
private static Logger logger = LoggerManager.getLogger(PluginModule.class);
private static final String JAR = ".jar";
private static final String LIB = "lib";
private static final String AGENT_PLUGIN_XML = "datacenter-plugin.xml";
private IPluginActivator activator;
private URLClassLoader pluginLoader;
private String pluginName;
private File pluginPath;
private boolean isLoaded = false;
private IPluginDefinition pluginDefinition;
public void load(File pluginPath) {
if (!isLoaded && pluginPath.isDirectory() && pluginPath.exists()) {
this.pluginPath = pluginPath;
loadPlugin(pluginPath);
}
}
public void unload() {
System.out.println(getPluginName() + ":stop");
unloadNativeLibs(); //卸载本地代码
}
private void unloadNativeLibs() {
ClassLoader loader = getPluginLoader();
Field field;
try {
field = ClassLoader.class.getDeclaredField("nativeLibraries");
field.setAccessible(true);
Vector libs = (Vector) field.get(loader);
Iterator it = libs.iterator();
Object o;
while (it.hasNext()) {
o = it.next();
Method finalize = o.getClass().getDeclaredMethod("finalize", new Class[0]);
finalize.setAccessible(true);
finalize.invoke(o, new Object[0]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("deprecation")
private void loadPlugin(File pluginPath) {
try {
pluginDefinition = PluginResolver.INSTANCE.resolvePlugin(pluginPath.toURL());
if(pluginDefinition == null) {
throw new PluginResolveException("resolve plugin config error!");
}
initClassLoader();
//实例化插件启动器
initPluginActivator();
isLoaded = true;
} catch (Exception e) {
logger.log(Level.SEVERE, String.format("init plungin[%s] fail.", pluginPath.getPath()), e);
}
}
private void initClassLoader() throws MalformedURLException {
URL[] urls = pluginDefinition.getClassPath().getPath(); //[file:/F:/xx/demo/bin/]
pluginLoader = new URLClassLoader(urls, ClassUtil.getClassLoader());
}
private void initPluginActivator() throws Exception {
String cls = pluginDefinition.getActivator();
Class clazz = pluginLoader.loadClass(cls);
if (IPluginActivator.class.isAssignableFrom(clazz)) {
activator = (IPluginActivator)clazz.newInstance();
}
// throw new Exception(cls + " is not a subclass of:" + IPluginActivator.class);
}
public String getPluginName() {
return pluginDefinition.getName();
}
public File getPluginPath() {
return pluginPath;
}
public boolean isLoaded() {
return isLoaded;
}
public ClassLoader getPluginLoader() {
return pluginLoader;
}
public IPluginActivator getPluginActivator() {
return activator;
}
public IPluginDefinition getPluginDefinition() {
return pluginDefinition;
}
}