新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   >>中国XML论坛<<     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 在这里讨论其他W3C规范
    [返回] 中文XML论坛 - 专业的XML技术讨论区W3CHINA.ORG讨论区 - Web新技术讨论『 其他W3C规范 』 → 使用 XML: 定义和加载扩展点 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 21188 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 使用 XML: 定义和加载扩展点 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     oceans 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:10
      积分:173
      门派:XML.ORG.CN
      注册:2005/2/28

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给oceans发送一个短消息 把oceans加入好友 查看oceans的个人资料 搜索oceans在『 其他W3C规范 』的所有贴子 引用回复这个贴子 回复这个贴子 查看oceans的博客楼主
    发贴心情 使用 XML: 定义和加载扩展点

    扩展 Eclipse 插件使其更通用
    级别: 高级

    Beno&Icirc;t Marchal (bmarchal@pineapplesoft.com)
    顾问, Pineapplesoft
    2005 年 2 月

    在这一篇文章中,Beno&Icirc;t 将进一步集成简单的内容管理解决方案 XM 和 Eclipse。除了 XML 之外,发布 Web 站点需要处理很多文件类型,因此围绕着可扩展的核心设计一个发布系统是合情合理的。Eclipse 插件非常合适这一点。Beno&Icirc;t 说明了如何使 XM 插件变得能够扩展,以便适应多种文件类型。在本文的讨论论坛中与作者和其他读者分享您对本文的看法。(您也可以单击本文顶部或底部的讨论来访问论坛)。
    在使用 XML 系列的前两期文章中,主要关注的是一位老朋友:XM,这是一种易用的文档发布解决方案。XM 以 XML 和 XSLT 为基础来管理 Web 站点和印刷(PDF)发布。在这一系列文章中,我已经更新了核心发布引擎的大部分,使其更加灵活,并且和 Eclipse IDE 集成在一起,以获得更加智能化的构建和更好的错误报告能力。到目前为止,XM 插件为平台增加一些固定的特性,即处理 XML 和 XSLT 文件的能力。现在我将说明如何使 XM 插件本身能够扩展,以便能够处理任何文件。

    虽然 XM 主要依靠 XML,但是也需要处理其他文档类型,如图像、办公文档和 PDF 文件。从一开始我就围绕着一个可扩展的核心来组织 XM。有一个接口(最初是 Mover,现在改为 Batch)组织了所有关于文件格式的知识。为了支持新的格式(如 PDF),只需要实现一个新的 Batch,同时向引擎注册该 Batch,然后重新编译。如果您愿意花时间修改源代码,这种方法当然很好,但并不一定要如此。通过 Eclipse,您可以将 Batch 功能和主引擎从物理上分开。新的实现可以为放在另一个插件中,这极大地增强了灵活性。

    扩展点
    这种体系结构包括通过扩展点协作的两个或多个插件(请参阅导入和扩展点)。如果您一直阅读本系列的文章,那么您对扩展点可能已经熟悉了。前面的文章中已经实现了几个扩展点(尤其是 使用 XML:用 Eclipse 和 XM 构建项目 和 使用 XML:利用重构 XM 得来的经验 这两期文章中,开发的 builder 扩展是 XM 插件的重要组成部分)。

    导入和扩展点
    Eclipse 插件不需要扩展点来协同工作。插件可以直接从其他插件中导入类(通过 plugin.xml 中的 requires 标签)。通过导入,开发人员只能导入他们在编译时已经知道的服务,而扩展点允许开发人员在能够使用甚至编写之前就指定服务,这是一种更加灵活的方法。使用导入的时候由服务提供者定义 API,而使用扩展点则由服务的用户定义 API。

    继续讨论之前,首先要澄清扩展点和扩展之间的差别。扩展点是一个端口的定义,即其他插件提供服务的入口。在 Java 语言中最接近的东西是接口。与接口一样,扩展点定义了用户与服务提供者之间的契约。。

    扩展实现就是真正的服务,通过特殊的打包方式以便能够在扩展点调用。如果将扩展点看作接口,那么扩展就是实现该接口的类。插件可以同时实现扩展(为其他插件提供服务)和定义扩展点(请求其他插件的服务)。

    虽然 Eclipse 平台提供了很多标准插件和对应的标准扩展点,扩展点机制是完全开放的。Eclipse 提供的插件没有使用秘密的后门或者特殊的服务,它们仅仅是插件。用于标准插件的扩展选项同样可用于其他插件。

    定义扩展点
    与 Eclipse 的多数特性一样,扩展点也首先从 plugin.xml 文件开始。插件通过 extension-point 标签描述自己支持的扩展点,类似于 Batch 扩展点中的定义:

    <extension-point id="batch" name="Batch" schema="schema/batch.exsd"/>

    extension-point 标签需要三个参数:

    id 是扩展点标识符。Eclipse 将它与插件 id 连在一起,作为平台提供的惟一标识符。
    name 是用户友好的名称。
    schema 只想描述扩展标记的 XML Schema。扩展实现者在自己的插件中使用 plugin.xml 文件中的模式。
    多数扩展点都提供一个或多个 Java 接口以实现扩展。

    Eclipse 插件体系结构的好处
    Java 语言一直支持动态加载类。Eclipse 插件建立在 Java 动态加载的基础上,但是带了两个重要的好处。

    首先,平台是插件的媒介。无论如何使用,扩展点和扩展实现必须要发现对方 —— 不是通过重新编译。Eclipse 管理扩展注册来实现这种机制。

    其次,插件在 plugin.xml 文件中包含大量的描述信息,还可以通过模式增加信息!因此,在很多情况下,扩展点是用标记来判断是否需要加载扩展。

    不要低估了第二方面的好处。很多基于插件的应用程序似乎都一直不停地加载,因为它们在一开始就初始化和加载所有的插件。(Adobe&reg; Photoshop&reg; 是最为声名狼藉的一个例子。)Eclipse IDE 完全建立在插件之外,因此不允许在启动的时候将其全部加载。比如,假设用户已经安装了用于 Java、C++ 和 XML 开发的插件,而目前正在处理 Java 项目。加载 C++ 和 XML 插件就是不必要的,只会拖后腿。Eclipse 直到最后一刻才加载必要的插件。

    扩展模式
    Eclipse 直到最后一分钟才加载插件,使用 plugin.xml 描述文件中的数据来判断是否要加载给定的插件(请参阅 Eclipse 插件体系结构的好处)。但是如何判断是否需要加载一个扩展点呢?不同的扩展点有不同的条件。

    为了解决这个问题,可以扩展 plugin.xml,使站点设计人员能够添加带有适当信息的标记。其中包括扩展点加载扩展所需要的信息,如类名,以及不 加载扩展所需要的信息,如确定插件是否适用的条件。您可能还记得上一期文章中编写的 XM builder,它在 plugin.xml 中提供了类名(以便加载插件)和项目特性(确定是否需要加载该插件)。您可能也注意到,每个扩展都使用不同的标记。

    扩展点在 XML Schema 中指定数据。该模式必须声明 extension 元素(带有三个属性:id、name 和 point)。Eclipse 平台需要该元素及其三个属性,以便标识扩展。但是,extension 元素的内容是由开发人员决定的。因为多数文件类型都有惟一的扩展名,我决定采用基于文件名的简单过滤器。清单 1 包括了 Batch 扩展点的模式定义。

    清单 1. Batch 模式
    <?xml version='1.0' encoding='UTF-8'?>
    <schema targetNamespace="org.ananas.xm.eclipse">
    <annotation>
       <appInfo>
          <meta.schema plugin="org.ananas.xm.eclipse" id="batch" name="Batch"/>
       </appInfo>
       <documentation>Adds a file type to XM.</documentation>
    </annotation>
    <element name="run">
       <annotation>
          <documentation>implementation class</documentation>
       </annotation>
       <complexType>
          <sequence/>
          <attribute name="class" type="string" use="required"/>
       </complexType>
    </element>
    <element name="target">
       <annotation>
          <documentation>filtering to recognize the file type</documentation>
       </annotation>
       <complexType>
          <sequence/>
          <attribute name="pattern" type="string" use="required"/>
          <attribute name="targetAdded" type="boolean"/>
          <attribute name="targetModified" type="boolean"/>
          <attribute name="targetRemoved" type="boolean"/>
          <attribute name="targetUnchanged" type="boolean"/>
       </complexType>
    </element>
    <element name="batch">
       <complexType>
          <sequence>
             <element ref="run"/>
             <element ref="target" minOccurs="0"/>
          </sequence>
       </complexType>
    </element>

    <element name="extension">
       <complexType>
          <sequence><element ref="batch"/></sequence>
          <attribute name="point" type="string" use="required">
             <annotation>
                <documentation>
                   should be org.ananas.xm.eclipse.batch
                </documentation>
             </annotation>
          </attribute>
          <attribute name="id" type="string">
             <annotation>
                <documentation>identifier</documentation>
             </annotation>
          </attribute>
          <attribute name="name" type="string">
             <annotation>
                <documentation>name</documentation>
             </annotation>
          </attribute>
       </complexType>
    </element>
    </schema>

    要注意,Eclipse 仅支持模式定义的一个子集。具体而言,这里只能使用全局元素(直接定义在 schema 元素下,通过 ref 属性来引用)。模式必须从一个特殊的注释开始,meta.schema 指向插件。

    调用扩展点
    现在已经声明了扩展点,还需要加载它。窍门是只有绝对需要时才加载它。

    发现扩展
    扩展点背后的假设是:扩展点的实现在编译时还不能使用,因此简单的 new 是不够的。Eclipse 平台管理扩展实现的注册。加载扩展需要从平台上(通过恰当命名的 Platform 对象)来访问注册(通过 IExtensionRegistry 接口),然后查询所关心的插件的扩展点。平台返回一个 IExtensionPoint 对象。

    IExtensionPoint 返回一个 IConfigurationElement 对象数组,用它表示 plugin.xml 中的扩展标签。对于每个实现扩展点的插件,您都会收到一个 IConfigurationElement。IConfigurationElement 提供了 getChildren() 和 getAttribute() 之类的方法,以便从 XML 标记中检索数据。最后,createExecutableExtension() 返回实现扩展的 Java 类。它从 XML 标记的一个属性得到 Java 类的名称。

    请参见清单 2 中的 loadExtensions() 方法。

    代理模式
    加载扩展点的最佳解决方案是使用代理模式。这种模式中,一个对象(代理)处理对另一个对象(因为没有更好的名称,暂时称之为实际对象)的访问。这样,代理可以监控对实际对象的请求,并在过滤请求和需要请求的情况下重新组织这些请求。代理有很多用处,比如包装遗留系统、调整库的接口、管理实际对象的副本等。我将使用代理把加载隔离出来。

    要记住,除非绝对必要,否则最好不要加载插件。比如,加载处理没有使用的文件类型的插件是没有意义的。代理根据 plugin.xml 文件中的数据(由 IConfigurationElement 返回)过滤调用。

    这里的实现根据文件名过滤文件,但是每次调用都检查需要插件是否会带来很大的不便。另一种做法是,把加载管理放在代理对象中,让它来判断是否需要加载组件,以及何时加载插件是合理的。清单 2 显示了加载 Batch 扩展点的代理对象。

    清单 2. 加载扩展点的代理
    package org.ananas.xm.eclipse;

    import org.ananas.xm.core.Batch;
    import org.ananas.xm.core.Location;
    import org.ananas.xm.core.Filename;
    import org.ananas.xm.core.Messenger;
    import org.ananas.xm.core.XMException;
    import org.eclipse.core.runtime.Platform;
    import org.eclipse.core.runtime.CoreException;
    import org.eclipse.core.runtime.IExtensionPoint;
    import org.eclipse.core.runtime.IExtensionRegistry;
    import org.eclipse.core.runtime.IConfigurationElement;

    public class ExtensionBatch
       implements Batch
    {
       protected BatchExtensionPoint extension;
       protected IConfigurationElement element;
       protected String pattern;
       protected boolean targetAdded,
                         targetModified,
                         targetUnchanged,
                         targetRemoved;
       protected Messenger messenger;
       public ExtensionBatch(IConfigurationElement element)
       {
          this.element = element;
          extension = null;
          messenger = null;
          System.out.println(element.getName());
          IConfigurationElement children[] = element.getChildren("target");
          if(children == null || children.length == 0)
          {
             pattern = null;
             targetAdded = true;
             targetModified = true;
             targetUnchanged = false;
             targetRemoved = false;
          }
          else
          {
             pattern = children[0].getAttribute("pattern");
             String st = children[0].getAttribute("targetAdded");
             targetAdded = st == null ?
                true : Boolean.valueOf(st).booleanValue();
             st = children[0].getAttribute("targetModified");
             targetModified = st == null ?
                true : Boolean.valueOf(st).booleanValue();
             st = children[0].getAttribute("targetUnchanged");
             targetUnchanged = st == null ?
                true : Boolean.valueOf(st).booleanValue();
             st = children[0].getAttribute("targetRemoved");
             targetRemoved = st == null ?
                true : Boolean.valueOf(st).booleanValue();
          }
       }
       public void setMessenger(Messenger messenger)
          throws XMException
       {
          this.messenger = messenger;
          if(extension != null)
             extension.setMessenger(messenger);
       }
       public Messenger getMessenger()
       {
          return messenger;
       }
       public boolean isTargetAdded()
       {
          return targetAdded;
       }
       public boolean isTargetModified()
       {
          return targetModified;
       }
       public boolean isTargetUnchanged()
       {
          return targetUnchanged;
       }
       public boolean isTargetRemoved()
       {
          return targetRemoved;
       }
       public String getName()
       {
          return element.getAttribute("name");
       }
       public int appliesTo(Filename filename)
          throws XMException
       {
          if(filename.nameMatches(pattern))
          {
             loadExtension(filename);
             return extension.confirmAppliesTo(filename);
          }
          return 0;
       }
       public boolean process(Filename publish,Filename file)
          throws XMException
       {
          loadExtension(file);
          return extension.process(publish,file);
       }
       protected void loadExtension(Filename file)
          throws XMException
       {
          try
          {
             Object o = null;
             IConfigurationElement children[] = element.getChildren("run");
             if(children != null && children.length != 0)
                o = children[0].createExecutableExtension("class");
             if(o == null || !(o instanceof BatchExtensionPoint))
                messenger.fatal(
                   new XMException(messenger.getResourceString(
                      "eclipse.noextension",element.getAttribute("id")),
                      new Location(file,
                         Location.UNKNOWN_POSITION,
                         Location.UNKNOWN_POSITION)));
             else
             {
                extension = (BatchExtensionPoint)o;
                extension.setMessenger(messenger);
             }
          }
          catch(CoreException x)
          {
             messenger.fatal(new XMException(x));
          }
       }
       static public ExtensionBatch[] loadExtensions(Messenger messenger)
          throws XMException
       {
          IExtensionRegistry registry = Platform.getExtensionRegistry();
          IExtensionPoint extensionPoint =
             registry.getExtensionPoint("org.ananas.xm.eclipse.batch");
          IConfigurationElement points[] =
             extensionPoint.getConfigurationElements();
          ExtensionBatch batches[] = new ExtensionBatch[points.length];
          for(int i = 0;i < points.length;i++)
          {
             batches[i] = new ExtensionBatch(points[i]);
             batches[i].setMessenger(messenger);
          }
          return batches;
       }
    }

    结束语
    Eclipse 不仅仅是一个 IDE。它包含成熟的插件解决方案,这使它成为一个真正的开发平台。您可以将 Eclipse 用于任何应用程序,包括发布解决方案,就像本系列文章所介绍的那样。Eclipse 成功的关键之一是预置到平台中的东西很少。这为开发人员提供了一个非常灵活的工具,从而可以根据任务塑造成最佳的形式。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    [url=http://www.id-consulting.com.cn]管理咨询[/url] [url=http://www.jinlingdry.com]干燥设备[/url] [url=http://www.tiaoma.com.cn]条码扫描器[/url] [url=http://www.sanxia.org.cn]三峡之窗[/url] [url=http://www.cpmi.org.cn]项目管理[/url]

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/3 15:23:00
     
     hoolzun 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:4
      积分:73
      门派:XML.ORG.CN
      注册:2005/3/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给hoolzun发送一个短消息 把hoolzun加入好友 查看hoolzun的个人资料 搜索hoolzun在『 其他W3C规范 』的所有贴子 引用回复这个贴子 回复这个贴子 查看hoolzun的博客2
    发贴心情 
    好!
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/4/4 23:26:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 其他W3C规范 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/1 1:04:17

    本主题贴数2,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    390.137ms