第一个自定义插件的实现
把自己的一些粗糙想法写出来,即是对自己的一个总结,也能对其他人有些帮助。简单随笔,若在实现中遇到问题,具体再聊。
Eclipse中的插件都是XML文件来进行描述,比如:
这段XML描述了插件的绝大多数信息,包括插件的ID,name,版本,启动类,同时所有的扩展节点也都在这里定义,该插件对外提供的库以及资源也都要定义在这个文件中。
?
文件的名称为“plugin.xml”Eclipse启动的时候会扫描“plugins”目录下的所有 “plugin.xml”文件,进而装载所有的插件,因此,需要用XML Parser 将这些信息Parser出来,形成插件的基本信息,具体选用DOM、SAX、还是Pull Parser 都无所谓。
?Eclipse采用微内核+插件的模式构架,也就是说,除了一个微小的核儿之外,所有的东西都是插件(All are plugins)。
Eclipse Plugin Framework 最核心的概念就要算“Extension Point(扩展点)”了。打个比方,“Extension Point”就像我们日常生活中的插销板,而Extension 就是能够插入到这个插销板上面的插销。
系统的开放性很大程度上取决于系统究竟给你多少Extension Point。
对于Eclipse来说,因为采用微内核+插件的方式,因此,定义扩展点是很重要的步骤,在扩展功能的同时,你可以在任何你觉得可能被扩展的地方定义扩展点,来方便其他人扩展系统的功能。
举个例子,Eclipse UI中,工具栏、视图都留有扩展点,这样可以方便的进行扩展。
Eclipse的插件扩展点都定义在plugin.xml,每个插件要扩展那些扩展点也定义在这个文件中。你可以搜索eclipse的安装目录,会发现数以百计的plugin.xml文件
了解Eclipse的Plugin Framework 需要对ClassLoader(类装载器)有比较深的理解。
ClassLoader——顾名思义,就是Java中用来装载类的部分,要将一个类的名字装载为JVM中实际的二进制类数据。在JVM中,任何一个类被加载,都是通过ClassLoader来实现的,同时,每个Class对象都有一个引用指向装载他的ClassLoader,你可以通过getClassLoader()方法得到它。
?
ClassLoader是一个抽象类,你可以定义自己的ClassLoader来实现特定的Load功能。Eclipse Plugin Framework就实现了自己的ClassLoader。
?
ClassLoader使用的是“Delegation Model(双亲委托模型)”来查找,定位类资源。每一个ClassLoader都有一个父ClassLoader实例(在构造的时候传入),当这个ClassLoader被要求加载一个类时,他首先会询问自己的父ClassLoader,看看他能否加载,如果不能,则自己加载。
最后来看一下代码:
可见,ClassLoader首先会查找该类是否已经被装载,如果没有,就询问自己的父ClassLoader,如果还不能装载,就调用findClass()方法来装载类。所以,一般简单的自定义ClassLoader只需要重写findClass方法就可以了。
简单demo准备,实现过程。
我们模拟几个重要的类是:
Plugin:插件类,描述每个具体的插件。
PluginDesciptor: 插件描述符,记录了插件的ID,Name,Version,依赖,扩展点等。
PluginManager:插件管理器,负责所有插件资源的管理,包括插件的启动,停止等。
PluginRegistry:插件注册表,提供了一个由插件ID到plugin的映射。
?
package com.bjrongzhi.nms.client.plugins.core;
?
import org.java.plugin.Plugin;
?
public class PluginCore extends Plugin {
?
??? protected void doStart() throws Exception {
??? }
?
??? protected void doStop() throws Exception {
??? }
?
}
?
?
<?xml version="1.0" ?>
<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 1.0" "http://jpf.sourceforge.net/plugin_1_0.dtd">
<plugin id="com.bjrongzhi.nms.client.plugins.core" version="1.0.0" >
?? <runtime>
????? <library id="core" path="/" type="code">
??????? <export prefix="*" />
????? </library>
?? </runtime>
?
<!--? 定义两个扩展点PopupMenu和MainMenu-->
?? <extension-point id="PopupMenu">
????? <parameter-def id="class"/>
????? <parameter-def id="name"/>
?? </extension-point>
?? <extension-point id="MainMenu">
????? <parameter-def id="class"/>
????? <parameter-def id="name"/>
?? </extension-point>
</plugin>
?
package com.bjrongzhi.nms.client.plugins.popupmenu;
?
/**
?* 仿真管理系统右键菜单
?*
?* @author zhangdy
?*
?*/
public class ResMgrPopupMenu extends AbstractPopupMenu {
?????? //省略……..
?????? JMenuItem mi_deleteSubnet = new JMenuItem("删除子网");
?????? JMenuItem mi_createLable = new JMenuItem("创建标签");
?????? public ResMgrPopupMenu() {
??????
????????????? mi_createPerfSnmp.addActionListener(new DoCreatePerfSnmp());/
/省略……..
?????????????
????????????? // 站点界面的右键菜单
????????????? table_menu.put(ClientMainPane.class.getName(), new JMenuItem[] {
??????????????????????????? mi_createSdhNe,
??????????????????????????? // mi_createShelf,
??????????????????????????? // mi_createIPNe,
??????????????????????????? mi_createPerfmonitortask, mi_createPerfSnmp, // mi_createPerfTest
????????????? // // 测试
??????????????????????????? });
??????
?
}
?
<?xml version="1.0" ?>
<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 1.0" "http://jpf.sourceforge.net/plugin_1_0.dtd">
<plugin id="com.bjrongzhi.nms.client.plugins.popupmenu" version="1.0.0">
?? <!--
??????? 该插件依赖的插件在这里定义???
?? -->
?? <requires>
????? <import plugin-id="com.bjrongzhi.nms.client.plugins.core"/>
?? </requires>
?? <runtime>
??
?? <!-- PluginClassLoader会将根目录都加入到URLClassLoader的类搜索路径中去,
?? ?????? 这样,就可以用这个类加载器来加载相应的插件类和资源了。-->
????? <library id="popupmenu" path="/" type="code">
????? ?? <export prefix="*" />
????? </library>
?? </runtime>
??
?? <!-- 这是core关于PopupMenu的扩展点,这是"ResMgrPopupMenu"插件中对此扩展点的一个扩展声明,声明了自己扩展的类和名称。Name
??????????? 只是定义该扩展点的名称,而其中的"class"则是为了加载真正的功能。
?? -->
??? <extension plugin-id="com.bjrongzhi.nms.client.plugins.core" point-id="PopupMenu" id="resmgrpopupmenu">
????? <parameter id="class" value="com.bjrongzhi.nms.client.plugins.popupmenu.ResMgrPopupMenu"/>
????? <parameter id="name" value="ResMgrPopupMenu"/>
?? </extension>
</plugin>
?
package com.bjrongzhi.nms.client.topo.menu;
?
import com.bjrongzhi.nms.client.topo.ClientMainPane;
?
public class PluginTools {
?
?????? public static PluginTools instance = new PluginTools();
?????? public static PluginManager pluginManager;
?????? public static PluginDescriptor core;
?????? // TODO
?????? private TNetwork network = ClientMainPane.network;
?
?????? static {
????????????? try {
???????????????????? ExtendedProperties config = new ExtendedProperties(System
?????????????????????????????????? .getProperties());
???????????????????? PluginsCollector collector = new DefaultPluginsCollector();
???????????????????? collector.configure(config);
?
???????????????????? pluginManager = ObjectFactory.newInstance(config).createManager();
???????????????????? Collection<PluginLocation> pluginLocations = collector
?????????????????????????????????? .collectPluginLocations();
?
???????????????????? pluginManager.publishPlugins((PluginLocation[]) pluginLocations
?????????????????????????????????? .toArray(new PluginLocation[pluginLocations.size()]));
???????????????????? // Create instance of plug-in manager.
?
???????????????????? /*
???????????????????? ?* 得到插件描述符,描述符中记录了插件的ID,Name,Version,依赖,扩展点等。
???????????????????? ?* 通过Plugin.xml文件得知,关于core的扩展点有两个:PopupMenu和MainMenu。这两个扩展点都声
???????????????????? ?* 明了自己扩展的类和名称.Name 只是定义该扩展点的名称,Class则为了加载真正的功能。
???????????????????? ?*/
???????????????????? core = pluginManager.getRegistry().getPluginDescriptor(
?????????????????????????????????? "com.bjrongzhi.nms.client.plugins.core");
????????????? } catch (JpfException e) {
text-align: justify; margin: 0cm