为 WebSphere Application Server 开发企业 OSGi 应用程序

转载地址:

http://www.ibm.com/developerworks/cn/websphere/techjournal/1007_robinson/1007_robinson.html

简介:  使用标准 Java™ EE 部署创建模块式、可扩展的 Web 应用程序有一定难度,但通常可以通过良好的设计实践和方法来完成。但是,当您想分离公共模块以便在多个企业应用程序之间共享,或者同时使用多个公共库版本时,您就会遇到真正的麻烦。OSGi 是多年以来一直在 IBM® WebSphere® Application Server 和 Eclipse 平台中使用的一种 Java 模块技术。WebSphere Application Server V7 Feature Pack for OSGi Applications and JPA 2.0 允许模块式企业应用程序直接使用 OSGi 技术来极大地简化它们的开发、组装和部署。这个特性包还提供了一个基础设施,其中,模块式设计不仅是一个最佳实践,而且是惟一的实践方法。 本文来自于 IBM WebSphere Developer Technical Journal 中文版

介绍

与软件开发相关的绝大部分成本并不是新应用程序的初始设计、开发和测试成本 — 尽管这些成本可能会比较高 — 而是 后续的维护和完善成本

从一致、版本化、可重用的模块(只能通过定义良好的接口访问)设计和构建应用程序和应用程序套件降低了复杂性,并为软件首次发布之后的维护和完善提供了极大的灵活性。模块式设计(其中模块提供定义良好的服务和接口)允许在多个团队之间分配大型项目,各个团队可以专注于各自的任务,不必在公共外部包范围之外理解其他团队的代码细节,从而降低了每个团队的工作的复杂程度,减少了完成任务的时间。另外,通过将应用程序的更改影响范围降到最小 — 假设影响范围能够确定,应用程序的维护成本也得以降低。如果一个模块更改只影响它的内部包,对它的外部包没有任何影响,那么可以只对一个单独的隔离模块进行维护,测试目标也更容易确定。这是显而易见的,但我们可以从开发工具和运行时基础设施的积极帮助中获益,以支持这样一种模块式设计方式。例如,如果有一种更简单的方法来部署和维护多个 EAR 使用的公共代码,难道这不是很好吗?

首先,什么模块才算得上好模块?

  • 该模块应该提供一致的 逻辑功能,并且应该足够的独立,表示一个实际的 “重用单元”。
  • 该模块应该通过定义良好的外部接口和依赖项与其他模块松散耦合
  • 该模块应该将它的内部包与其他模块隔离 ,以便模块的内部行为更改不会影响其他任何模块。

Java 类粒度太小,不能形成应用程序之间的逻辑重用单元,它们的隔离关注主要局限于实例数据的封装。类通常包装在一个代表一致功能的 JAR 文件中。作为一个部署单元(和文件系统中存在的工件),JAR 是一个非常实用的重用单元,但它缺乏作为一个好模块的其他一些特征。

考虑可见性:如果您在包 bar.foo 的类中有一个对其可用的方法,那么您需要使用一个公共访问限定符来声明该方法。您没有办法来表明这个方法是否也应该或不应该对该 JAR 之外的类可用。您可以应用惯例和最佳实践。可以使用一个包命名惯例将您的接口组织到外部和内部包中。例如,专用于 WebSphere Application Server 的接口包含在 com.ibm.websphere 或 com.ibm.wsspi 的子包中,而专用于应用程序的内部接口主要位于 com.ibm.ws 的子包中。如果在不同的模块上工作的不同团队遵守模块外部包的定义和用法,那么这些模块将只通过定义的外部包保持耦合。当然,您的模块中总是有一些 ++ 函数(plus-plus function)不可或缺,但它们不是模块接口的一部分。因此,当另一团队发现这也会使他们的工作更轻松,那么允许他们继续使用这些函数又有什么危害呢?毕竟,你们都在相同的总体项目上工作,且他们将使用的方法都拥有公共 Java 访问能力,因此不必更改代码就可以允许他们使用这些函数 — 如果他们一定要问的话。现在,您无意中打破了 bundle 之间的松散耦合。您的 bundle 的内部包的更改 “影响范围” 现在可能会超出您的 bundle 范围,这使得确定什么受到影响,进而确定需要测试什么更加困难。

那么,现在有一点很清楚:尽管 JAR 是一个非常实用的重用单元,但它缺乏区分内部包和外部包的能力,因此没有办法隔离外部包。比这更糟的是:尽管在另一个环境中提供这个 JAR(您的重用单元)相当容易,但您如何确定这个新环境是否能够满足其拥有的所有依赖项的要求呢?如果您在这上面犯错误,可能就相当于您的手上拿着一个定时炸 弹,即它可能一连数天正常运行,然后需要加载这个新环境没有的一个类,这时炸 弹爆炸 — ClassNotFoundException。这里的问题是,除了不能提供隔离外,JARs 还不能声明它们自己的依赖项。

看来,JAR 与良好的可重用模块的要求比较接近,但缺乏一些基本的模块特征。这就是 OSGi bundle 发挥作用的时候了。

OSGi bundle 一个 JAR,但它在 JAR 清单文件中拥有附加头部。在没有处理这个附加元数据机制的纯 JVM 中,这个 bundle 的表现与普通 JAR 一致。但在包含一个 OSGi 框架的 JVM 中,这个元数据 被该框架处理,额外的模块特征得到应用。清单 1 展示了一个示例 MANIFEST.MF。


清单 1. 一个 bundle 清单文件中的 OSGi 头部

				
Manifest-Version: 1.0 

Bundle-ManifestVersion: 2 

Bundle-Name: MyService bundle   

Bundle-SymbolicName: com.sample.myservice 

Bundle-Version: 1.0.0 

Import-Package: com.something.i.need;version="[1.0,2.0)" 

Export-Package: com.myservice.api;version=1.0.0

这里需要注意的一些头部是:

  • Export-Package 列出将从这个 bundle 导出的一个或多个包(处于特定版本)。只有导出包在 bundle 外部可见;这里没有列出的包都只在 bundle 内部可见。
  • Import-Package 列出 bundle 需要的一个或多个包(处于特定版本或版本范围)。

我们现在先不涉及版本控制的细节(将在稍后讨论),先来看看这两个头部的作用。还记得吗,前面介绍过,我们需要建立一些开发最佳实践来定义模块的外部接口并确保没有任何内部包被任何客户模块使用。借助 OSGi,现在可以通过在运行时实施来支持这个最佳实践,并可以向模块所有者提供一个机制来适当封装它的内部包。另外,您的包清单文件中包含一些元数据,明确指定 bundle 需要的、需要由其他 bundle 提供的所有包。在一个包含许多 bundle 的复杂应用程序中,这将向您提供一个更确定的方法,以确定一个 bundle 更改的影响,并理解对系统其余部分的可能影响,从而减小软件开发周期中的风险和成本。

元数据处理和可见性规则的运行时实施由 OSGi 框架解算器 负责。当每个 bundle 启动时,这个解算器针对此框架中已经安装的其他 bundle 声明的导出协调该 bundle 的每个导入,并为每个 bundle 计算一个单独的类路径。如果某个 bundle 有一个无法解算的依赖项,该 bundle 将不会启动。前面提到过的滴答作响的定时炸 弹(针对一个 JAR 被移动到一个不能满足其所有依赖项要求的新环境的情况)被通过移除保险丝而拆除。相比处理一个无法启动的应用程序而言,处理一个正常工作一段时间、然后突然失败并抛出一个 ClassNotFoundException 的应用程序要困难得多。

此前我们所讨论的内容都不是特定于一个企业运行时的 — 那么这些内容如何与企业 Java 应用程序或企业应用程序服务器相关呢?

假设我们有一个企业应用程序服务器能够处理 OSGi bundle 元数据,那么 OSGi 的第一个明显的优势就是包含大量模块的复杂企业应用程序的适当模块化。但 OSGi 的优势远不止这些,它还能解决企业环境中普遍的几个其他问题。我们已经简单接触了这些问题中的一个:在部署了数十或数百个 EAR 的许多大型企业部署中,往往存在这样一种情况:每个 EAR 都是自足的,其程度达到应用程序使用的许多公共库被打包到需要它们的每个 EAR 中。这些库副本将塞满部署这些 EAR 的文件系统和知识库,并在应用程序启动时填满内存。尽管企业应用程序通常可以被部署到各种供应商的企业环境,且管理依赖项已针对独立于这些 EAR 安装的共享库配置,但这些机制因供应商不同而不同,限制了可移植性。另外,共享库配置通常与部署过程本身分离,需要独立的、部署后管理配置,这增加了端到端部署过程的复杂性。

OSGi 元数据和 bundle 知识库向您提供了一个机会,允许您只在应用程序存档中包含特定于应用程序的模块,从而极大地简化共享公共库的企业应用程序套件的部署。在理解 OSGi 元数据之后,企业部署过程变得更加强大,能够针对部署环境中配置的 bundle 知识库的内容解算 bundle 依赖项。所有公共库都可以在一个中央 bundle 知识库中管理,该知识库然后成为企业(单元)配置的一部分。

OSGi 还向您提供了一个机会,允许您在企业环境中更好地执行版本控制工作。当今企业应用程序通常包含对公共库有相似依赖性的第三方框架和库。如果不同框架需要不同版本的公共库,那么这将是个令人头痛的问题。当您遇到下面的情况时,这个问题将变得尤其严重:以前,您的应用程序一切正常,但现在您需要更新一个供应商框架,结果却发现办不到,原因是该框架在应用程序中的另一个框架使用的一个库的不兼容的新版本上有一个依赖项。

OSGi 版本控制元数据和类加载能够消除这个问题。在上面的清单 1 中,Import-Package 头部表明包 com.something.i.need 上的一个依赖项,版本范围 为 “[1.0,2.0)”。这表明这个依赖项的需求范围为 1.0 ≤ version < 2.0 的任何包版本。因此,version 1.0 或 1.5.0 或 1.9 都能满足这个依赖项的需求,但 version 2.0 不能。OSGi 的版本控制机制支持包提供者在版本的主要部分中使用一个标明,一个不兼容更新的更改来表明一个包的新版本是否向前兼容前面的版本。包使用者可以指出他们能够使用的版本或版本范围。(请参阅 这份 PDF 白皮书 了解关于 OSGi 版本控制的更多信息。)重要的是,如果一个应用程序中的两个 bundle 依赖同一个包的不同版本,那么这些依赖项的需求可以同时得到满足,因为 OSGi 解算器可以为这两个 bundle 计算不同的类路径。

OSGi 平台规范,以及参考实现和依从性测试,由 OSGi Alliance 生成,10 多年来一直被广泛使用。2010 年 3 月,OSGi V4.2 Enterprise Specification 出版物包含了企业环境。该出版物定义了企业 Java 技术的一些 OSGi 语义,比如事务、持久性和 Web 组件。这个重要的规范定义了一些标准机制来将结合 Java EE 和 OSGi 世界,包含了 bundle 元数据来声明该 bundle 包含一个 web.xmm 文件的 Web bundle、包含一个 persistence.xml 文件的持久性 bundle、还是包含一个 blueprint.xml 文件的 Blueprint bundle。Web 和持久性 bundle 只是包含额外 OSGi 清单文件头部的常见 Java EE bundle;而 Blueprint bundle 更像 Spring 模块,但包含一个标准化的 bean 定义 XML。

作为一种技术,OSGi 在独立应用程序和客户端应用程序技术中已经流行了多年,并在许多企业应用程序服务器(比如 WebSphere Application Server)的实现中内部使用。由于缺乏企业应用程序的 OSGi 标准,以及缺乏广泛可用的专用工具和企业运行时支持,在这些应用程序服务器平台上运行的企业应用程序对 OSGi 的直接利用最近受到抑制。随着 OSGi Enterprise Specification 的发布,以及可以选择在越来越多的企业应用程序服务器上将应用程序部署为 OSGi bundle,这种情况得到了改观。

本文余下部分将讨论开发和部署 WebSphere Application Server V7 Feature Pack for OSGi Applications and JPA 2.0 中引入的企业 OSGi 应用程序的运行时和工具化支持:


OSGi 应用程序特性包

WebSphere Application Server V7 Feature Pack for OSGi Applications and JPA 2.0 在 WebSphere Application Server 中引入了 OSGi 应用程序支持。与其他 WebSphere Application Server 特性包一样,这是一个 免费提供的下载 ,可以以附加方式在一个现有 WebSphere Application Server V7.0.0.9 或更高版本上安装和卸载。这个特性包实际上包含两个可安装特性:OSGi 应用程序特性JPA 2.0 特性 。这两个特性可以单独安装,也可以同时安装;同时使用时,它们提供一个简化的、基于 POJO 的组件模型、高性能持久性框架、以及简化 Web 应用程序开发和单元测试的模块化开发系统。本文只关注 OSGi 应用程序特性。

WebSphere Application Server OSGi 应用程序特性支持以模块化方式开发和部署企业应用程序,将可配置的 OSGi bundle 知识库引入 WebSphere Application Server 管理流程。这允许将公共 bundle 从单独的企业应用程序存档中分离出来,并在一个 bundle 知识库中集中管理。一个 bundle 知识库可以管理多个 bundle 版本,与单独的企业应用程序关联的适当版本可以在那些应用程序的元数据中指定。

我们来看看成为 WebSphere Application Server 中的一个 OSGi 应用程序意味着什么?

在最基本的层面上,一个 OSGi 应用程序可能是部署在一个 Java EE 存档(EAR)中的相同的模块集合,但带有支持将这些模块加载为 OSGi bundle 的额外的 OSGi 元数据。尽管使用 Java EE 类加载器按照一个标准 Java EE 应用程序运行这个应用程序和使用 OSGi 类加载器按照一个 OSGi 应用程序运行该应用程序产生的最终结果没有什么区别,但由于以下几个原因,您可能会选择开发和部署一个 OSGi 应用程序:

  • 应用程序可以部署在只包含应用程序特有内容(理想情况)的存档中,并使用元数据引用任何共享库(bundle)。应用程序存档变得更小,且只有单个公共库副本被加载到内存中。
  • 使用标准 OSGi 机制,可以同时在同一个应用程序中加载多个类版本。
  • 已部署的应用程序可以以模块化方式、在 bundle 层面上更新。
  • 在开发时,IBM Rational® Application Developer 中的 OSGi 项目实施 OSGi 可见性规则,以便这些项目只能访问来自其他一些项目的包,那些项目将这些包显式声明为项目外部包的一部分,从而为开发最佳实践提供环境支持。
  • 应用程序可以设计为通过使用 OSGi 服务提高可扩展能力并实现动态更新。
  • 在运行时,应用程序仅当它的所有依赖项能够被解算时才能成功启动,从而减少应用程序在处理一个工作负载时出现 ClassNotFoundExceptions 的几率。
  • 应用程序可以使用与服务器运行时不同的、自己的公共实用程序类版本,而不必配置 PARENT_LAST 模式这样的 Java EE 类加载器策略。

当您的应用程序复杂性增加,或者已部署的应用程序套件的规模增大时,管理对应用程序的模块和它们使用的实用工具库的更新就会变得更困难。这时,使用 OSGi 的好处就会变得甚至更加明显。


开始使用 OSGi 应用程序

有几种方法可以开始在 WebSphere Application Server 上部署您的首个 OSGi 应用程序。最常见的方法也许是从您已经熟悉的一个现有 Java EE Web 应用程序开始,或者从 OSGi 应用程序特性中包含的样例应用程序开始。无论采用哪种方式,都需要将 OSGi 应用程序打包为一种可以部署的格式。

与其他任何应用程序一样,OSGi 通过 wsadmin 或 WebSphere Application Server 管理控制台部署,但被打包为一种称为企业 bundle 存档 (enterprise bundle archive,EBA)的新型存档。这种存档与 EAR 相似,但它的模块作为 bundle 部署到理想的目标服务器。一个 EBA 代表包含一个或多个应用程序模块的单个独立 OSGi 应用程序,是一个企业 OSGi 应用程序的部署单元。与 EAR 文件类似,一个 EBA 可以包含组成应用程序的所有组成模块或 bundle,相反,也可以只包含从一个已配置的 bundle 知识库定位那些 bundle 需要的元数据。这种元数据采用一个 EBA 级的 APPLICATION.MF 文件形式,该文件描述应用程序的内容以及应用程序是否公开任何外部服务和引用。就像 bundle 清单文件描述 bundle 的模块特征一样,应用程序清单文件描述应用程序的模块特征,以及该应用程序可部署的内容。

清单 2 显示了一个简单 OSGi 应用程序的完整应用程序清单文件。Application-Content 头目描述组成应用程序的主要 bundle,以及应用程序部署时将部署的 bundle。部署应用程序需要一个 “内容列表” 的原因是并非所有这个内容都需要包含在 EBA 中的应用程序清单文件中;这些 bundle 当然可以被打包到存档中,但它们中的全部或部分同样可以通过 WebSphere Application Server 部署流程从一个已配置的 bundle 知识库提供。例如,如果 com.example.common.audit bundle 为一个 IT 组织管理的所有应用程序提供公共审计服务,该 bundle 应该安装到一个公共 bundle 知识库中而不是部署为每个应用程序 EBA 的一部分。


清单 2. 一个 OSGi 应用程序的简单 APPLICATION.MF

				
Application-Name: MyApp

Application-SymbolicName: com.example.app

Application-Version: 1.0

Application-Content: com.example.app.web;version=1.0.0,

com.example.app.persistence;version=1.0.0,

com.example.common.audit; version=1.0.0

通过 OSGi 应用程序特性,WebSphere Application Server 提供一个内置 OSGi bundle 知识库,该知识库的内容可以通过 WebSphere Application Server 管理控制台管理,如图 1 所示。WebSphere Application Server 还允许选择使用一些外部 OSGi bundle 知识库,这些知识库通过一个已配置的知识库 URL 访问。


图 1. 管理控制台中的 OSGi bundle 知识库

在 OSGi 应用程序部署过程中,WebSphere Application Server 管理控制台为应用程序计算所有包和 OSGi 服务级别依赖项,以便该应用程序能够由 EBA 和已配置的 bundle 知识库一起完整提供。应用程序清单文件本身可以由开发人员创建,也可以由 Rational Application Developer 这样的工具生成。重要的是要理解,应用程序清单文件只需在 Application-Content 中列示主要的应用程序 bundle,而不必列示这些主 bundle 依赖的所有包提供的和服务提供的 bundle。WebSphere Application Server 部署流程解算主 bundle 的所有依赖项,以计算一个短暂关闭的应用程序内容列表,并在解算过程检测到依赖项缺失时阻止应用程序部署。还需要理解的一个重点是,配置按例外管理(by-exception),因此,如果 Application-Content 全部包含在 EBA 中,则应用程序清单文件完全不必要。构成 Application-Content 一部分的每个模块,以及其全部已计算依赖项,部署为一个 OSGi bundle。如果一个不带 OSGi 元数据的 Web 模块包含在 Application-Content 中,那么 WebSphere Application Server 部署流程将在部署过程中自动将这个模块转换为一个 Web 应用程序 bundle (WAB 模块)。按例外配置(configuration-by-exception)以及 WAR 文件的自动检测和转换提供一种最快捷的方法来开始使用 OSGi 应用程序 — 可以将一个只包含 Web 模块的 Java EE EAR 部署为一个 OSGi 应用程序,只需将该存档的文件扩展名 .ear 更改为 .eba。


OSGi 服务和 Blueprint 组件模型

OSGi (通过 OSGi 服务)引入的另一个特性是一个标准可扩展性模型。您可以设计您的应用程序以利用 OSGi 丰富的基于服务的动态模型,从而使用来自 OSGi 服务注册表的服务。这样,OSGi 服务就成为可以设计到您的应用程序中的扩展点,应用程序将来可以通过单独的 bundle 引入的服务提供商实现来进行扩展,且不必为原始应用程序代码付费。OSGi 定义一些 Java API 来注册和发现 OSGi 服务,但 OSGi 服务的一个声明性方法更加简单。这就是企业 OSGi V4.2 Blueprint 容器发挥作用的地方:Blueprint 容器管理 POJO bean 组件的生命周期和依赖项注入,并通过一个应用程序级别 XML bean 定义文件配置,这个文件是 Spring bean 定义 XML 的基于标准的演变。通过标准化 OSGi Alliance 中的 bean 定义 XML 架构和 bena 生命周期管理语义,就有可能将依赖项注入容器从应用程序(其中通常打包了 Spring 框架库)分离出来并重构到中间件中。在安装 OSGi 应用程序特性时,来自 Apache Aries 项目的企业 OSGi v4.2 Blueprint 容器实现被集成并扩展为 WebSphere Application Server 运行时的一部分。

Blueprint 为 OSGi 应用程序提供了一个细粒度的 bean 组装模型,以及一种简单的声明性方法来发布由 POJO bean 组件提供的服务。例如,清单 3 中显示的 bean 定义代码段定义了一个 bloggingServiceComponent bean(通过 BloggingServiceImpl 类实现),一个 BloggingService 服务在 OSGi 服务注册表中注册了这个 bean。


清单 3. Blueprint bean 定义

				
<blueprint>
  <bean id="bloggingServiceComponent"
       class="com.ibm.ws.eba.example.blog.BloggingServiceImpl">
<property name="blogEntryManager" ref="blogEntryManager"/>
<property name="blogAuthorManager" ref="blogAuthorManager"/>
<property name="blogCommentManager" ref="blogCommentManager"/>
</bean>
<service ref="bloggingServiceComponent"
interface="com.ibm.ws.eba.example.blog.api.BloggingService"/>
  ...
</blueprint>

在这个示例中,这个容器在被实例化时将 3 个属性(可能是对 bean 定义文件 blueprint.xml 中其他位置定义的 OSGi 服务或 beans 的引用)注入到 bloggingServiceComponent bean 中。Blueprint 配置的 beans 向 OSGi 应用程序提供了一种方便的方法来将它们的业务逻辑封装到一些 POJO 组件中,这些 POJO 组件已经通过 Blueprint 容器在其中注入了它们的依赖项和配置。由于这些 POJO bean 组件在应用程序服务器上没有 Java 依赖项,因此在一个纯 Java SE 或 Eclipse 环境中对业务逻辑进行单元测试非常简单。


一个样例 OSGi 应用程序

我们现在来看看 OSGi 应用程序特性自带的一个样例,以了解这样的应用程序通常是如何开发和部署的。这个样例是一个简单的博客发布应用程序,演示如何在一个 OSGi 应用程序中联合使用 Web、Blueprint 和持久性技术。(如果您想深入研究其代码,请查看 OSGi Applicantion 特性包自带的这个博客应用程序的源代码。)


图 2. 博客样例应用程序

这个博客样例展示了企业应用程序的典型架构。该架构包含 4 个松耦合 bundle:一个 Web 层、一个业务逻辑层、一个持久化层、以及一个 API bundle。

首先,注意如何将这些 API 拉到它们各自的 bundle 中。这是保持耦合松散的一个 OSGi 最佳实践。如果必要,一个实现可以在部署时或甚至在运行时轻松交换为另一个实现。

bundle 之间的耦合利用一个基本 OSGi 构造 — 服务(在图 2 中表示为三角形),服务在 bundle 之间维护理想的松散耦合并允许以对应用程序其余部分的最小影响更轻松地替换 bundle 实现。如 OSGi 服务和 Blueprint 组件模型 所述,OSGi 应用程序不必直接与 OSGi 服务注册表交互,相反,可以通过简单 POJO beans 的声明性 Blueprint 配置这样做。blog-biz 和 blog-persistence bundle 都是用 Blueprint 配置的 beans 来封装业务逻辑,并通过管理它们的生命周期的 Blueprint 容器将它们的依赖项和配置注入自身。Blueprint 容器将 blog-biz bundle 中的 beans 连接到一起,并将 blog-biz bundle 中的组件连接到 blog-persistence bundle 提供的 Blog persistence 服务。

应用程序的前端是一个使用我们熟悉的 Java EE servelet 组件的 Web 模块。为演示结合 Java EE 编程风格和基于服务的 OSGi 风格是多么简单,样例 blog-web bundle 遵循一个纯 Java EE 编程模型并使用 JNDI 来访问 Blueprint 发布的 OSGi 服务。企业 OSGi 规范为 JNDI 客户机定义了一个标准机制来获取 OSGi 服务的引用,在两种编程风格之间架起了一个天然的桥梁。

blog-persistence-jpa bundle 将 JPA 用作持久性框架,条目通过该框架持久存储到一个数据库并从该数据库检索。它利用 Blueprint 容器的功能来管理持久性上下文和全局事务,以确保业务逻辑的开发和单元测试尽可能保持简单。

最后,这些博客 JAR 被打包到一个 EBA 中并部署到 WebSphere Application Server。

我们下面仔细看看这个应用程序的每个元素。

API bundle

API bundle 是最简单的组件,这也许不会令人感到奇怪。它是一个简单的 OSGi bundle,不利用任何企业特性。如前所述,正是 JAR 清单文件中的 OSGi 元数据向 JAR 赋予 “bundle 特征”。在这个博客 API 示例中,这个 bundle 声明导出 com.ibm.ws.eba.example.blog.api、com.ibm.ws.eba.example.blog.comment.persistence.api 和 com.ibm.ws.eba.example.blog.persistence.api 三个包,版本号为 1.0.0。


清单 4. 博客样例 API bundle 清单文件

				
Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: blog

Bundle-SymbolicName: com.ibm.ws.eba.example.blog.api

Bundle-Version: 1.0.0

Export-Package: 

com.ibm.ws.eba.example.blog.api;version=1.0.0,

com.ibm.ws.eba.example.blog.comment.persistence.api;version=1.0.0,

com.ibm.ws.eba.example.blog.persistence.api;version=1.0.0

Web 应用程序 bundle

在很多方面,Web bundle 都与标准的 Java EE Web 存档相似,它在一个 web.xml 文件中声明它的 servlet 映射,将其代码打包到 WEB-INF/类中。但是,也有一些区别。对于一个 OSGi bundle,您将与其清单文件声明一些导入 — 本例中为 API bundle 导出的博客 API。Web-ContextPath 在清单文件,而不是 EAR 的 application.xml 文件中声明。这个区别的实际原因是 OSGi 应用程序不是一个 EAR,因此没有 application.xml 文件。但是,还有一个更重要的动机。将 Web 模块的所有配置信息都保存在 Web bundle 本身中使 Web 存档的模块性和独立性更高,从而保持了 OSGi 的精髓。


清单 5. 博客样例 Web bundle 清单文件

				
Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-ClassPath: WEB-INF/classes

Bundle-Name: blog.web

Bundle-SymbolicName: com.ibm.ws.eba.example.blog.web

Web-ContextPath: blog

Bundle-Version: 1.0.0

Import-Package: 

com.ibm.ws.eba.example.blog.api;version="[1.0.0,1.1.0)",

com.ibm.json.java;version="[1.0.0,2.0.0)"

WebSphere Application Server 中的 OSGi 框架将这个存档识别为一个 Web bundle 并将其传递给一个 Web 容器,以管理 servlets 的生命周期。Java EE Web 容器和 OSGi 容器之间的集成通过联合的 JNDI 查找实现,这种查找是企业 OSGi 规范的一个特性。所有 OSGi 服务都自动在 JNDI 中注册,并可以以访问 Java EE 组件的熟悉方式访问。例如,这个博客 servlet 访问 BloggingService 的一个实现,如清单 6 所示。


清单 6. 从 Web 组件访问 OSGi 服务

				
InitialContext ic = new InitialContext();

return (BloggingService) ic.lookup("osgi:service/"

+ BloggingService.class.getName());

同样,也可以使用一个 @Resource 注释来注入这个引用,作为使用这个 JNDI Context API 的替代方法。

Blueprint bundle

业务逻辑被实现为一个 POJO 集合,并通过一个声明性 blueprint.xml 配置文件将 bean 定义和引用与 OSGi 服务关联起来。Blueprint 容器负责与 OSGi 服务注册表交互,以管理与服务的交互和服务的生命周期。清单 3 中展示的 blueprint.xml 来自这个博客样例业务 bundle,展示了声明一个 bean,注入其依赖项(可能是服务和其他 beans 的引用)并将这个 bean 发布为一个服务的一个典型模式。

持久性 bundle

持久性 bundle 使用企业 OSGi 的另一个标准特性 — JPA 集成 — 来利用托管持久性。JPA 持久性单元同样通过一个 persistence.xml 文件来配置。持久性 bundle 的 bundle 清单文件需要一个 Meta-Persistence 头部来表明它是一个持久性 bundle,如清单 7 所示。


清单 7. 博客样例持久性清单文件

				
Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: blog.persistence

Bundle-SymbolicName: com.ibm.ws.eba.example.blog.persistence

Bundle-Version: 1.0.0

Meta-Persistence: 

Import-Package:

com.ibm.ws.eba.example.blog.persistence.api;version="[1.0.0,1.1.0)",

javax.persistence;version=1.0.0

持久性 bundle 实际上不仅仅获取一个托管 JPA,而是一个容器 托管 JPA,因为它是一个 Blueprint bundle(以及一个持久性 bundle);Blueprint 容器全面管理 JPA PersistenceContext 并将其注入为 blogExample 持久性单元注释的托管 bean(BlogPersistenceServiceImpl)中。


清单 8. 基于注释的 JPA EntityManager 注入

				
private EntityManager em;

@PersistenceContext(unitName = "blogExample")

public void setEntityManager(EntityManager e) {

em = e;

}

作为注释这个 bean Java 代码的一种替代方法,容器托管的 PersistenceContext 能够通过在 Blueprint bean 定义中使用 <jpa:context> 元素来声明,如清单 9 所示。


清单 9. Blueprint 容器托管的 Transaction 和 Persistence 配置

				
<bean id="persistenceManager" 
	class="com.ibm.ws.eba.example.blog.persistence.BlogPersistenceServiceImpl">
<tx:transaction method="*" value="Required"/>
<jpa:context property="em" unitname="blogExample"/>
</bean>

清单 9 还展示了一些 Blueprint 容器托管的事务。在这个示例中,persistenceManager bean 的所有方法都在 Blueprint 容器建立(或连接)的一个全局事务下运行。WebSphere Blueprint 容器支持的容器托管的事务值集合与 EJB 容器支持的集合相同。(参见 事务和 OSGi 应用程序 了解更多信息。)

应用程序组装

这个样例博客应用程序的最后一个部分是 EBA。如 前文 所述,EBA 是可部署的单元,包含一个描述应用程序内容的应用程序清单文件。对于这个博客样例,应用程序清单文件看起来如清单 10 所示。


清单 10. 博客样例 APPLICATION MANIFEST

				
Application-Name: Blog

Application-SymbolicName: com.ibm.ws.eba.example.blog.app

Application-Version: 1.0

Application-Content:

com.ibm.ws.eba.example.blog.api;version=1.0.0,

com.ibm.ws.eba.example.blog.persistence;version="[1.0.0, 2.0.0)",

com.ibm.ws.eba.example.blog.web;version=1.0.0,

com.ibm.ws.eba.example.blog;version=1.0.0

Use-Bundle: com.ibm.json.java;version=1.0.0

这个应用程序级元数据已在 前文 中介绍过。这个博客样例清单文件中有两点需要注意:

  • 为应用程序部署的 API、博客和 Web bundle 的版本必须是 1.0.0 版或更高版本,但持久性bundle的版本范围必须为 1.0.0 到 2.0.0(不包括)。这意味着,尽管所有 bundle 在应用程序初始部署时都可以处于 1.0.0 版,但应用程序已被组装为支持这些 bundle 的未来更新。如果在应用程序部署之后的某个时间,持久性 bundle 的一个 1.1.0 版变得可用,那么 WebSphere Application Server 管理员可以(通过 wsadmin 或管理控制台)更新应用程序中的那个 bundle。这个博客样例提供博客持久性 bundle 的一个附加 1.1.0 版来演示这个管理更新功能。
  • Use-Bundle 头部独立于 Application-Content 列示 com.ibm.json.java bundle。这表明这个 bundle 是可以与其他应用程序共享的。Application-Content 中列出的所有 bundle 将在一个 OSGi 框架实例中运行,这个框架将这些 bundle 与同一个应用程序服务器中的其他 OSGi 应用程序隔离。这样,一个 OSGi 应用程序就不会对另一个 OSGi 应用程序产生不必要的副作用,这只是因为该 OSGi 应用程序被部署到同一个目标服务器。由于 OSGi 应用程序支持的目标之一是以一种与部署过程集成的方式简化应用程序之间的模块共享,Use-Bundle 头部提供了一种机制来识别应该被共享的模块。除了显示 Use-Bundle 内容外,其包或服务在应用程序部署时被隐式解算的任何 bundle 也视为正在提供共享内容。当应用程序在目标服务器上启动时,被识别为正在被共享的任何 bundle 都将被加载到一个服务器范围的父 OSGi 框架中,该框架的内容对每个隔离的应用程序框架都可见。

这个博客样例被打包为演示 WebSphere Application Server bundle 知识库的使用:一个必要的 bundle com.ibm.json.java 独立于 blog.eba 存档提供。这个共享 bundle 必须在 blog.eba 部署之前安装到 WebSphere Application Server bundle 知识库中。部署过程根据已部署的 EBA 存档、已配置的 bundle 知识库和 WebSphere Application Server 提供的 API/SPI 包(比如 Java EE 包和 com.ibm.websphere 包)的内容计算这个包以及所有应用程序 bundle 的依赖项。如果所有依赖项都被解算,则应用程序将被成功部署到已配置的目标服务器。在 WebSphere Application Server 管理控制台中,已安装的 OSGi 应用程序作为 Java EE 和 SCA 应用程序出现在相同的 Business-level applications (BLA) 集合视图中。(请参见 部署 OSGi 应用程序的管理任务 了解更多细节。)


SCA 构成

WebSphere Application Server V7 Feature Pack for SCA v1.0.1.5 或更高版本被安装后,OSGi 应用程序特性可以与 WebSphere Application Server 的 Service Component Architecture (SCA) 支持联合使用。SCA 提供一个组装模型来将可能异构的组件合成为一些粗粒度的复合(composite),这些复合定义一些外部服务和引用,可以为它们配置各种不同的 bundle。关于使用显式声明的外部包将一些组件组装为一个一致的模块的概念方面,SCA 和 OSGi 共享一些公共概念。联合使用这些技术非常自然,尽管它们确实处理服务组装的不同和独特方面。

OSGi 应用程序本身可以从一个 OSGi bundle 集合组装,每个 OSGi bundle(比如这个博客样例应用程序的 Web、业务和持久性 bundle)都执行应用程序中的一个一致功能。

  • 在 bundle 内部 ,细粒度组件使用一个 Blueprint bean 定义实现为 POJO beans 并连接在一起;一个 bundle 的内部包对其他 bundle 不可见。
  • 在 OSGi 应用程序内部 ,bundle通过 OSGi 服务或外部包依赖项连接在一起;一个 OSGi 应用程序的内部包对其他应用程序不可见。

上面是两种不同级别的模块性。SCA 提供另一个级别:

  • 在一个 SCA 复合内部 ,SCA 组件通过 SCA 服务连接在一起;一个 SCA 复合的内部包对其他复合不可见。

一个 OSGi 应用程序可以组装为一个粗粒度的 SCA 复合中的一个 SCA 组件,并可以有选择地公开它实现的 OSGi 服务,使用 SCA 来为这些服务提供远程绑定。OSGi 应用程序提供的 SCA 组件实现的 SCA 组件类型为 implementation.osgiapp 。它可以将其 Blueprint 配置的任何 OSGi 服务远程公开为 SCA 服务,并为这些服务配置远程绑定。

例如,假设您想将这个博客样例 OSGi 应用程序作为一个 SCA 组件,以及其他一些实现类型不同的组件(比如 EJB 组件)组合为一个 SCA 复合,然后将清单 3 中的代码段中定义的 bloggingServiceComponent 公开为一个带有默认 SCA bundle 的外部服务。您首先需要更新包含这个代码段的 Blueprint 服务定义,以表明这个服务是远程可用的。这需要在这个服务定义中添加一个标准 OSGi 属性 service.exported.interfaces,如清单 11 所示。


清单 11. 声明一个服务远程可用

				
<blueprint>
  <bean id="bloggingServiceComponent"
       class="com.ibm.ws.eba.example.blog.BloggingServiceImpl">
    ...
</bean>
<service ref="bloggingServiceComponent" 
interface="com.ibm.ws.eba.example.blog.api.BloggingService">
<service-properties>
<entry key="service.exported.interfaces" value="*"/>
</service-properties>
</service>
  ...
</blueprint>

清单 10 中展示的应用程序清单文件需要表明这个服务应该在 OSGi 应用程序外部可见。为此,向应用程序清单文件添加 Application-ExportService 头部,如清单 12 所示。


清单 12. 从 OSGi 应用程序导出服务

				
Application-Name: Blog

Application-SymbolicName: com.ibm.ws.eba.example.blog.app

Application-Version: 1.0

...

Application-ExportService: com.ibm.ws.eba.example.blog.api.BloggingService

现在您可以配置一个其实现为 OSGi 应用程序并提供一个 SCA 服务的 SCA 组件,如清单 13 所示。


清单 13. 配置一个 SCA 组件

				
<composite name="SocialMediaComposite">
<component name="BlogComponent">
<scafp:implementation.osgiapp 
applicationSymbolicName="com.ibm.ws.eba.example.blog.app"
applicationVersion="1.0.0"/>
<service name="bloggingServiceComponent">
<binding.sca>
</service>
</component>
</composite>

在这个示例中,显示了一个默认 SCA bundle,但也可以指定其他 bundle(比如 Web 服务的 binding.ws 或 JMS 的 binding.jms),具体取决于需要如何公开服务。

与导出远程服务的方式相似,implementation.osgiapp SCA 组件可以导入远程服务引用。关于联合使用 OSGi 和 SCA 的场景和模式的详细讨论超出了本文的范围。(请参见 在 SCA 复合中组装和部署 OSGi 组件的相关文档 了解更多信息。)


开发工具

用于构建企业 OSGi 应用程序的大部分开发行为和开发工具与 Java EE 工具类似,但有一些新的考虑。这些考虑主要围绕编译时类路径,以及 OSGi bundle、应用程序清单文件及可选的 Blueprint bean 定义文件的编写。Rational Application Developer 附件支持 开发 OSGi 应用程序工具,为 OSGi bundle 项目和应用程序项目引入了新的项目类型,并自动生成清单文件和基于表单的编辑器来修改它们。OSGi 模块化语义在项目构建路径中受到支持,以便只有在项目的 bundle 清单文件中显式导入和导出的包在项目之间共享。Rational Application Developer 的基于面的配置支持将 OSGi 项目配置为 OSGi Web 项目或 OSGi JPA 项目,并集成一些工具来编写 web.xml、persistence.xml 和 blueprint.xml。Rational Application Developer 的新 bundle Explorer 可用于可视化 OSGi 应用程序中的 bundle 之间的关系,如图 3 所示。OSGi 应用程序项目可以导出到 .eba 存档或从中导入,并可以从 Rational Application Developer 工作空间运行,该工作空间可以位于 Rational Application Developer 可选安装的 WebSphere Application Sever V7 中,也可以位于包含 OSGi 应用程序特性的远程 WebSphere Application Server V7 环境中。


图 3. Rational Application Developer bundle Explorer

除了集成的 Rational Application Developer 工具外,还有几个开源工具可以帮助生成 OSGi bundle 清单文件。另外,Apache Aries 社区开发的 EBA Maven Plugin 也可以从作为一个构建的一部分的 Maven pom 配置生成一个 OSGi 应用程序清单文件。


操作细节

目前为止我们已经讨论了如何开发、组装和部署 OSGi 应用程序。本小节简单介绍一个部署的结果,以及后续可以采取的一些管理操作。

WebSphere Application Server OSGi 应用程序特性包提供的一个有用的实用程序是安装在 install_dir/feature_packs/aries/bin/ 目录中的 osgiApplicationConsole 脚本。这是一个 wsadmin 客户机应用程序,旨在为指定应用程序服务器提供一个远程 OSGi 控制台。命令行参数如清单 14 所示。


清单 14. 使用 OSGi 应用程序控制台实用程序

				
-h The host name of the target machine.

-o The port number of the SOAP port of the target server

-u The user ID, if the wsadmin connection is secured. 

-p The password, if the wsadmin connection is secured.

example: 

install_dir/feature_packs/aries/bin/osgiApplicationConsole -h server1.acme.com -o 8880

在命令提示中,可以键入 help() 显示一个交互命令列表。如果您在部署和启动这个博客样例应用程序之后运行这个命令并在命令提示中键入 list() ,您将看到目标服务器上的两个 OSGi 框架的条目。一个是 OSGi 框架,这个博客样例应用程序就安装在该框架中;另一个是服务器范围的共享框架。要了解每个框架的 bundle、服务和包的相关细节,需要首先连接到想要的框架:从命令提示符处键入 connect(1) 以连接到应用程序框架。要列示这个框架中安装的所有 bundle,并查看它们的 bundle 状态,键入 ss() 。您应该能够看到类似清单 15 的结果。


清单 15. 交互式 OSGi 应用程序控制台

				
wsadmin>ss()

ID State bundle  

0 ACTIVE org.eclipse.osgi_3.5.2.R35x_v20100126

1 ACTIVE com.ibm.ws.eba.example.blog.app_1.0.0

2 ACTIVE com.ibm.ws.eba.example.blog.persistence_1.0.0

3 ACTIVE com.ibm.ws.eba.example.blog.web_1.0.0

4 ACTIVE com.ibm.ws.eba.example.blog.api_1.0.0

5 ACTIVE com.ibm.ws.eba.example.blog_1.0.0

WebSphere Application Server Information Center 记录了一些交互式命令和过滤器 ,用于获取关于正在运行的应用程序的信息。这个控制台可以帮助您很轻松地以可视方式查看如何为分离的 OSGi 应用程序维护隔离,通过隔离,一个应用程序的内部 bundle 和包将对其他应用程序不可见。要隔离一个应用程序和另一个应用程序,您不必将它们部署在不同的服务器上。需要在应用程序之间共享的 bundle(例如,此前介绍的链接到 Application Assembly 的 Use-Bundle 内容)被安装到共享框架中。

最后,我们看看已部署应用程序中的 bundle 的管理更新。

应用程序安装后,可以导航到管理控制台的 Assets 视图,查看每个 bundle 的已安装版本。如果任何已配置 bundle 知识库中有一个或多个 bundle 的更新版本可用,且那些版本处于为 OSGi 应用程序清单文件中的 bundle 定义的版本范围中,那么您有机会以管理方式将那些 bundle 中的全部或一部分更新到想要的版本。为了便于演示,这个博客样例提供了它的持久性 bundle 的一个额外的 1.1.0 版。如果您在这个博客样例部署之后将这个博客持久性 bundle 的 1.1.0 版添加到 WebSphere Application Server 内部 bundle 知识库,则可以通过面板中的 Application Assets 视图进行导航,如图 4 所示。


图 4. bundle 的管理更新

您现在可以选择这个持久性 bundle 的 1.1.0 版并选择 Preview 按钮来重新解算应用程序,并检查任何不一致。如果预览没有生成错误,那么您可以选择 Commit 按钮来更新应用程序并保存配置。


结束语

模块式应用程序设计方法的好处包括降低复杂性、减少交付时间和提高服务能力。尽管开发人员非常理解这些好处,经常采取一些最佳实践来鼓励模块式设计,但 Java EE 基础设施自身实施或鼓励模式式设计的能力有限。

企业 OSGi 将 OSGi 的模块性原理和基础设施与用于企业应用程序及其运行的服务器环境的一个熟悉的 Java EE 编程模型结合起来。

WebSphere Application Server V7 Feature Pack for OSGi Applications and JPA 2.0 的 OSGi 应用程序特性为将 OSGi 应用程序部署到 WebSphere Application Server 提供了综合的运行时和集成管理支持。通过使应用程序相互隔离,同时支持共享应用程序组装元数据标示的特定 bundle,应用程序完整性得到维护。应用程序部署流程被扩大为支持从特定于应用程序的存档和共享的 OSGi bundle 知识库提供应用程序内容,从而减小了应用程序存档大小以及磁盘和内存占用。类似于 Spring 的声明式组装和依赖项注入,以及简化应用程序外部的单元测试的好处,通过由 OSGi 标准管理并集成到服务器运行时的 Blueprint 容器提供。OSGi 应用程序到异构复合的组装和 OSGi 服务的远程绑定通过 OSGi 应用程序的一个新 SCA 组件实现提供。

OSGi 开发人员的工具化通过 Rational Application Developer 提供,包括 OSGi 元数据的生成器和编辑器、开发环境中的 OSGi 模块化约束的实施、企业 bundle 存档的导入/导出、以及在服务器中运行和调试 OSGi 应用程序的工作空间集成功能。

现在就试试吧!

<!-- CMA ID: 547954 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->

参考资料

学习

猜你喜欢

转载自marsvaadin.iteye.com/blog/1392040