站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Eclipse 专题利用 OSGi 解决 Eclipse 插件难题

利用 OSGi 解决 Eclipse 插件难题

摘要: 本文介绍如何在代码中为其他插件编写扩展,而不是用 Eclipse V3.2 的动态扩展 API 创建对其他插件的依赖性。可使用开放服务网关协议 (Open Services Gateway Initiative,OSGi) 服务 API 和动态 API 完成所有这些任务甚至更多任务。

本文介绍了一个采用 XML 的插件示例,以便为定义好的扩展点注册扩展。通过使插件能够感知 Extention Registry 并提供 OSGi 服务,我们可以完成这一完整的组件退耦操作。

插件、扩展点、OSGi

如您所知,Eclipse 的组件架构是基于插件 的 -- 这意味着将一组代码组件化为单一的组件,然后利用 Eclipse 框架注册为其组件之一,其他组件可以绑定该组件或调用该组件。扩展点 是插件允许其他插件向公开扩展点的插件提供附加功能的方法。现在利用所有这些插件并将其包装到受控的运行时,插件可在其中动态进出,并且您可以获得 OSGi(基本上来说)。

示例插件

让我们从公开扩展点的基本插件开始,这样可以为同义词服务注册新的字符串映射。此项服务允许其他服务注册一个词并将其映射到另一个词(同义词)。基本扩展包含非常简单的元素:一个词,当然还有一个新的同义词。此插件扩展点的基本结构如表 1 所示。


表 1. 示例插件的元素
<table border="0" cellpadding="0" cellspacing="0" class="data-table-1" summary="Elements of our sample plug-in" width=""><tr><th>插件名称</th><td class="tb-row">com.company.SynonymRegistry</td></tr><tr><th>扩展点</th><td class="tb-row">同义词</td></tr><tr><th>元素</th><td class="tb-row">词 -- 您想要为其添加同义词的词
同义词 -- 您想要注册的同义词</td></tr></table>

我们还要将插件注册为 OSGi 服务。这意味着它只在显式执行此操作时被加载,并将可供其他客户声明性地使用。为了使用该服务,其他客户只需了解 Interface 和 OSGi 类名称。在我们的示例中,我们不会真正调用该服务,因为扩展点是假设的。我们将使用 OSGi API 以告知我们此项服务出入的时间,所以我们可以正确地注册扩展点。

现在这只是一个示例,并且使用针对此概念的扩展点可能不是最好的方法。我们用此基本示例要达到的目的是如何动态注册新的扩展,同时说明使用 OSGi API 的插件生命周期事件。

Mediator 插件

下一个插件是第三方插件,该插件了解已知的服务和扩展点,但不想绑定到此插件,因为后面它将依靠该插件进行运行时解析。这意味着该插件可以驻留在所引用的插件 (com.company.SynonymRegistry) 可能不存在的机器上。因为我们现在生活在 OSGi 和动态运行时世界,所以我们想确保插件在不引起运行时故障或错误的情况下运行。我们的 mediator 插件将接受同义词的 XML 文件,并且通过使用提供的扩展点用 SynonymRegistry 插件注册每个同义词。


清单 1. 用于概念验证的示例 XML 文件
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

Synonyms.xml
<?xml version="1.0" encoding="UTF-8"?>






</td></tr></table>

Mediator 插件在其 start() 方法中做的第一件事是用 OSGi 服务注册为一个服务初始化侦听器。我们要在传入 start() 方法的 BundleContext 对象上调用 OSGi 服务方法 addServiceListener()。以下代码展示了一个通过传入代码和我们感兴趣的服务 ID 调用此 API 的示例。

<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
context.addServiceListener( this, "com.company.SynonymRegistry" );
</td></tr></table>

通过提供过滤器,可以告知 OSGi 服务注册中心只需通知您指定服务中的状态更改。在本例中,过滤器只是 SynonymRegistry 类的类名称。

您可能会寻根究底。答案就在启动序列中。在 OSGi 领域,我们不是总知道另一服务可用的时间,因此我们需要对此进行说明。通过注册为服务侦听器,我们可得知服务开始和停止的时间。如果服务不可用,则允许我们缓存同义词。当服务确实可用时,我们会得到通知并注册扩展。

注册新扩展

下面我们将讲述本文的核心内容。现在我们有了想为其提供动态扩展的数据 (Synonyms.xml) 和已知的扩展点 (com.company.SynonymRegistry. Synonym)。由于我们不知道何时初始化插件,也不知道是否初始化 Synonym 插件,所以我们只要在加载插件时尝试注册 XML 文件中的条目即可。请记住:这是一个展示概念的示例,不应在生产代码中这样实施。通常,我们尽可能多地以惰性方式(延迟或在需要时)执行初始化。

Eclipse V3.2 中的新特性是能够在运行时提供扩展。例如,客户可以编写一个包含某个视图的应用程序,该视图可以在单击按钮时创建一个透视图。透视图被添加到扩展注册表,然后在可用透视图的列表中显示。此功能的重要好处之一是它可以减轻插件之间的“硬”依赖性。插件 A 可供在插件 B 中定义的平台使用,无需依赖插件 B。而且,通过将此功能与 OSGi 框架结合,插件可以检查服务的存在性,如果存在,可从服务中定义的扩展点创建扩展。这在使用面向服务架构的原则同时,促进了真正动态的环境。

Eclipse V3.2 中新公开的是 addContribution() 方法,该方法在 IExtensionRegistry 接口中定义。清单 2 中的代码展示了可以通过 addContribution() API 添加扩展的方法。addContribution() 方法旨在采用普通 XML 作为第一个参数中的 InputStream


清单 2. 通过 addContribution() API 添加扩展的方法
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

IExtensionRegistry registry = RegistryFactory.getRegistry( );

Object key = ((ExtensionRegistry) registry).getTemporaryUserToken( );

ByteArrayInputStream is =
new ByteArrayInputStream( buffer.toString().getBytes() );

try {
registry.addContribution(is, bundle, null, null, key);
}
finally {
try {
is.close( );
}catch (IOException e) {

}
}
</td></tr></table>

编写本文的时候 -- 意味着这是一个更改 Eclipse 未来版本的好机会 -- 允许公众访问注册表的用户标记可以使用此内部 Eclipse 调用获得。下面的代码展示了内部 API (getTemporaryUserToken()) 的使用。

<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
Object key = ((ExtensionRegistry)registry).getTemporaryUserToken();
</td></tr></table>

但是,在里程碑式的下一版本 Eclipse V3.2 版本中,此标记不能公开访问。为了支持应用程序中的动态扩展,启动程序必须提供以下针对虚拟机的设置:

<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
-Declipse.registry.nulltoken=true
</td></tr></table>

此定义现在允许我们将 null 用作 addContribution() API 中的 User Token。现在,我们的代码看上去类似如下。有关此问题中的 Bugzilla 对话,请参见 Bugzilla bug 清单。


清单 3. getTemporaryUserToken()
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>

...
try {
registry.addContribution(is, bundle, null, null, null);

}
...
</td></tr></table>

上面显示的缓冲区变量表示实际的 XML 块。此 XML 是我们可以在 plugin.xml 文件内看到的精确副本。回到我们的 SynonymRegistry 示例,此扩展的 XML 将类似清单 4。


清单 4. SynonymRegistry 的 XML
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>






</td></tr></table>

客户可以考虑创建一个接受以下参数的包装工厂类,如扩展点 ID、扩展 ID、元素名称(本例中是同义词)、实际属性和资源包 ID。该包装类将参数格式化为类似上面代码的 XML 字符串。然后将此 XML 字符串读入将被传入到 IExtensionRegistry 接口的 addContribution() 方法的 ByteArrayInputStream 中。只有此方法的其他必需参数是用户标记和资源包 ID。值得注意的一点是,资源包 ID 应是做出该贡献的资源包的 ID,不是在其中定义扩展点的资源包的 ID。

警告和提示

在 M5(于 2006 年 2 月 17 日构建的 Eclipse)中引入的一个特性是,对 addContributions() 的调用是异步调用。这意味着该扩展不可立即使用,因为 Eclipse 启动了一项执行实际注册的作业。简单地说,您必须开始自己的作业并与之同步,以获得任何类型的同步行为。

为了使此项任务更容易,下面给出了三条提示:

当然,现在我们必须将调用代码与生成的作业结合起来,以获得同步调用。这只有在代码要求立即使用扩展时才得到保证。希望您的代码设计为惰性,这样初始化就变得不重要。

结束语

动态扩展的使用可以通过编程方式创建。通过使用 OSGi 框架侦听服务何时可用(加载或卸载),动态扩展增强了退耦功能。一起使用这些技术将允许声明性的贡献和组件之间 100% 退耦。


<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>


<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>

作者简介<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td>

</td><td></td><td width="100%">

Bob Balfe 是 IBM 的高级软件工程师和 WebSphere Everyplace Deployment 团队的门户托管客户机架构师。

</td></tr></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td>

</td><td></td><td width="100%">

Chuck Imperato 是 IBM 的咨询软件工程师和 WebSphere Everyplace Deployment 团队的开发人员。

</td></tr></table>

<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>


↑返回目录
前一篇: 了解 Eclipse 插件如何使用 OSGi
后一篇: 利用 Eclipse 开发基于 OSGi 的 Bundle 应用