JBI规范1.0 (五)
 

2009-11-30 作者:Juset 来源:Juset的blog

 

[7]——组件框架

1 组件框架(Component Framework)

JBI组件(包括服务引擎和绑定组件)将JBI提供的接口封装之后,向外界提供调用JBI的接口。JBI和组件间的主要消息和处理机制已将在前面章节中讨论过了,本章将详细论述组件的框架。

JBI实现基于底层的Java环境,建立了一个组件环境。JBI框架为使用组件提供了以下的功能:

  • 组件安装环境(Component installation context):为实现组件初始化必须提供的功能,它使组件在安装和卸载的时候可以访问JBI所提供的资源和组件安装信息。
  • 组件环境(Component context):为实现组件初始化必须提供的功能,它使组件在正常执行的时候可以访问JBI所提供的资源。
  • 加载类(Class loading):JBI保证用来创建组件中的初始化类和成员类实例的类加载器可以在初始化和执行时正确的设置。这使组件可以访问标准的类加载器、组件和JBI库。
  • 错误提示(Error indication):异常类为JBI指定的组建错误给出提示,使组件可以区分不同的错误类型。

另外,组件还为实现以下功能提供了特别的接口:

  • 引导程序(Bootstrap):在JBI的安装(卸载)配置功能之外提供了扩展的功能。
  • 组件管理接口(Component):该接口所提供的方法可以用来调用组件的生命周期接口,提供服务元数据,调用组件的服务单元管理器。
  • 组件生命周期接口(Component life cycle):提供组件的生命周期控制。正如第六章中所述,它可以被componentLifeCycleMBean所使用。
  • 服务单元管理器接口(Service unit manager):将服务单元部署到组件上,并进行生命周期控制。这是一个可选的接口,不用部署的组件无需提供此接口。

以上这些功能都是与管理活动有关的,如果想更深入了解它们是怎样被使用的请参照第六章内容。

1.1 组件提供的管理功能(Component-Supplied Management Functions)

1.1.1 引导程序(Bootstrapper)

组件的安装组要是由JBI管理系统来处理的(详情参看第六章),它将安装镜像复制到JBI实例的文件系统中。安装(或卸载)组件的过程可以由组件来订制,这使得可以在复制管理系统提供的二进制镜像之外执行更进一步的安装处理。例如,在组件初始化阶段可执行在数据库中创建表的操作。

在组件的安装包中必须提供实现Bootstrap接口的类的名字,这个名字必须是严格符合命名要求的(关于安装包的详细内容参见第六章)。在安装或卸载组件之前,一定要创建并初始化一个在安装包中指定的Bootstrap类的实例(通过调用Bootstrap.init ( )方法),详细说明见第六章。

1.1.2 组件接口(Component Interface)

每个组件都必须实现一个组件接口,用以实现这一功能的类的名字是通过安装描述符的形式提交给系统的。此描述符在第六章中介绍。

本章中讲述了JBI实现是怎样利用组件接口这一独立的功能来对其他组件接口的进行查找的。通过组件接口可以访问以下资源:

  • 组件的生命周期记录,具体过程将在后续内容介绍。
  • 组件服务单元管理器,可部署的组件需要提供。建议在组件每次调用服务单元管理器的getServiceUnitManager ( )方法的时候,不要返回null,而是返回一个与服务单元管理器类型相同的实例。

在组件初始化之后,为了对其进行更进一步的管理,JBI必须获得一个实现了该组件接口的实例。可遵循以下原则创建一个javax.jbi.component.Component的实例来完成。

  • 类名必须采用“组件-类-名字”的形式,正如在组件安装描述符中声明的那样。
  • 必须用组件类加载器创建。

JBI实现在组件启动或重起时,必须创建该组件的一个实例,并且只能创建一个。

1.1.3 组件生命周期(Component Life Cycle)

每个组件都必须实现ComponentLifeCycle接口,JBI可以通过Component接口访问它。

应注意这里的生命周期与通过ComponentLifeCycleMBean提供给管理工具的生命周期不同。JBI为实现这个MBean必须利用ComponentLifeCycle接口将组件的状态做必要的转换。关于组件的MBean生命周期的详细讨论见第六章。

组件在安装之后就可以进行常规的生命周期控制。组件的基本生命周期状态以及状态键的转换如下图所示。注意“Paused”和“Running”两个扩展状态是对基本JBI实现的生命周期状态的扩充,所有的扩展状态都是标准状态“Started”的子状态。

组件在第一次安装之后处于“Shutdown”状态。在使用组件提供的除了初始化之外的其他服务之前,必须先得到并初始化组件的生命周期记录。这必须使用javax.jbi.component.Component来实现。JBI一定要获得该组件的ComponentLifeCycle对象。

图31 带有扩展子状态的组件生命周期状态图

当组件处于“Shutdown”状态时,调用LifeCycleMBean.start()方法,就会使JBI调用将该组件生命周期初始化的init()方法,并向其传递一个有效的组件环境,然后调用生命周期的start()方法,如下图所示。初始化之后,再调用start()方法须在“Stopped”状态之下,但此时不会再使JBI框架调用init()方法,并且不会对停止和关闭操作产生影响。

图32 初始化组件起始消息序列图

图33 组件重启消息序列图

JBI必须保留在最初开始阶段或其他需要的时候所创建的Component对象,通过它来访问组件的生命周期等组件信息。

1.1.3.1 系统重启(System Restart)

所谓系统重启就是指在系统关闭或崩溃之后再重新启动的操作。重启时,JBI实现必须保存每个组件的运行状态以便将其尽量恢复到重启前的运行状态。

所有组件扩展状态都被认为是“Started”状态的子状态。当组件处于“Started”状态重启时,必须将所它的扩展子状态保存起来。

1.1.4 服务单元管理(Service Unit Management)

组件以实现ServiceUnitManager接口的方式提供服务单元管理的功能,如果一个组件未实现此功能那就意味着它并不打算部署的服务单元上。

在服务部署阶段,JBI实现必须调用ServiceUnitManager方法,正如第六章所述的那样。

1.2 JBI环境的特性(JBI-Supplied Environment Features)

1.2.1 组件上下文(Component Context)

ComponentContext接口使组件可以查找其所在的运行环境中由JBI所支持的一些功能。这些可供组件生命周期接口通过init()方法调用。

运行环境必须支持以下这些功能:

  • 组件名(Component Name):在组件安装时必须为其指定唯一的名字。
  • 安装根目录(Installation Root):组件的安装根目录。
  • 工作空间根目录(Workspace Root):可以被组件随意使用的文件空间的根目录。
  • MBean命名服务(MBean Naming Service):为组件中MBean所产生的JBX MBean对象起名。
  • MBean服务器(MBean Server):用于注册JBI系统中的所有MBean。
  • 命名上下文环境(Naming Context):可供组件使用的JNDI命名上下文环境。
  • 传输通道(Delivery Context):提供引擎与消息路由器的交互通道,参见第五章。
  • 端点激活/解除激活(Endpoint Activation/deactivation):允许组件声明他所提供的端点是否可供JBI环境中的消费者使用。参见第五章。
  • 端点注册/撤销注册(Endpoint Registration/deregistration):允许绑定声明外部端点是否可作为外部端点引用。参见第五章。

另外,运行环境还提供以下的功能用于与NMR交互。

  • 将组件所提供服务的端点激活或撤销激活。
  • 为某个特殊的借口活或服务查找可用的端点。

1.2.1.1 查找元数据(Querying Metadata)

ComponentContext接口允许组件查找JBI系统所提供的元数据服务。此功能可用来支持动态服务发现和选择服务提供者。

  • getEndpoints()方法:JBI实现必须能列出所有的支持指定接口类型的端点。
  • getEndpointsForService()方法:JBI实现必须能列出所有的提供指定服务的端点。
  • getEndpointDescriptor()方法:JBI实现必须能返回给定端点的元数据描述符。
  • getExternalEndpoints()方法:JBI实现必须能列出所有的被注册为可以提供指定接口类型的外部端点。
  • getExternalEndpointsForService():JBI实现必须能列出所有的被注册为可以提供指定服务的外部端点。

只要找到端点的描述符(Descriptor),就可以使用getDescription()方法得到服务元数据(端点的WSDL描述),该方法的返回值是DOM形式的端点描述。

1.2.1.2 端点激活和解除激活(Endpoint Activation and Deactivation)

作为服务提供者的JBI组件必须将目前可用的端点告诉NBR(一个组件可支持多个端点)。这样的组件可以用下面的ComponentContext方法动态的(随意的)将端点激活或解除激活。

  • activateEndpoint (serviceName, endpointName)方法:组件可使用此方法告知NMR哪个已命名的端点可以被用来处理消息交换。但组件在调用该方法之前必须先预备好要向该端点提供服务数据,并准备好处理由该端点传输的消息。
  • deactivateEndpoint(endpoint)方法:组件可以使用此方法告知NMR已经从服务中提取出一个命名好的端点。在端点被停用之后它的服务便不可再调用。此时正在传输的消息将会等待后续的处理。

1.2.2 安装(引导)(Installation[Bootstrap])

安装的全过程已经在第六章中介绍。在整个安装过程中,JBI实现利用安装上下文环境信息(InstallationContext)来初始化组件。安装上下文环境提供了安装和卸载组件时所需的系统环境信息(安装上下文环境信息可供Bootstrap对象的init()方法调用)。

1.2.2.1 安装上下文环境(Installation Context)

为了使组件在初始化阶段能够访问JBI环境中的关键资源,Bootstrap方法被传输给InstallationContext接口。安装上下文环境必须能够访问以下一些资源:

  • 组件名(Component name):在安装描述符中为组件指定的唯一的名字。
  • 组件类名(Component class name):实现组件SPI(javax.jbi.component.Component)的类的名字。它可在组件初始化时读出和写入,最初应按组件安装描述符提供的“组件-类-名字”的形式设置。
  • 类路径元素(Class path elements):组件执行时所需jar文件的路径列表。并且实现Component SPI的类也必须位于此路径上,以便在组件初始化阶段访问。最初应按组件安装描述符提供的“组件-类-名字”的形式设置
  • 组件上下文环境(Component context):JBI组件的全局环境。与通过组件生命周期的init()方法访问组建上下文环境类似,只不过它是全局的可供所有安装环境实例访问。在安装阶段可访问它的方法有:ComponentName(), getInstallRoot(), getMBeanNames(), getMBeanServer(), getNamingContext(), getTransactionManager()。调用其它方法一定会返回null值或JBIException。
  • 安装根目录(Installaion root directory):组件安装的根目录的完整路径。
  • 组件安装描述符(Component installation descriptor):支持对安装描述符(jbi.xml)扩展数据的访问,具体已在第六章中安装包部分讨论过。它允许在初始化时访问扩展的安装信息。

除非另有声明,否则这些属性必须为只读的。

在安装时可以使用setter方法改变类的路径。

1.2.3 日志(Loggers)

组件可以使用ComponentContext创建一个标准的java.util.logging.Logger实例。getLogger()方法有两个参数:一个附加字符串(可以为空),一个指定资源的名字(可以为空)。getLogger()方法必须保证以下几点:

  • 组件的记录名字必须是唯一的。创建组件时所起的名字一定不要和已有的组件名或JBI实现内部所使用的记录名相冲突。
  • 在同一组件内使用同样附加字符串调用getLogger方法,必定会返回同样的记录示例。

参数中的指定资源的名字如果不为空的话,那就意味着一定是用该资源创建了该记录实例,由此就能够将日志信息定位。在初始化组件的类加载器时,该资源一定可以被加载(它一定是在组件本身的类路径上或共享库中)。下面的7.3节将介绍JBI环境加载类和资源的细节。

1.3 类的加载(Class Loading)

JBI实现须为在安装和执行时创建最初的,由组件提供的对象而提供类加载器。本部分将会详细讨论类加载器怎样保证在所有JBI实现上的加载类操作的一致性,而这种一致性是实现组件可移植性的关键。

在组件的生命周期中分别有两个阶段需要使用两种不同的类加载器:

  • 安装阶段(Installation time)。组件必须实现在安装描述中所指明的初始化类,JBI实现须使用初始化类加载器来创建初始化实例。
  • 执行阶段(Execution time)。组件必须实现在安装描述中所指明的用来实现Component接口的组件类,JBI实现须使用执行类加载器来创建这个组件类。

两种类型的类加载器都必须提供以下资源:

  • 适当的平台类和资源
  • JBI类
  • 在类路径上可访问的类或资源:
    • 在初始化类加载器中,类路径是指组件安装描述中指明的初始化类路径;
    • 在执行类加载器中,类路径是指组件的安装上下文环境(InstallationContext)中提供的类路径。

在后面的7.3.2节中还会提到执行类加载器还额外提供了共享库。

1.3.1 类和资源的加载方式和加载器排序(Class and Resource Loading Styles and Loader Ordering)

Java2的类加载器可以被用来加载Java类和资源。类加载器可以有其父类加载器,由此多个类加载器可以形成一个有序的层次结构。类加载器通常会先让它的父加载器去加载某个类(或资源),如果失败了,自己再去加载那个类(或资源)。JBI的术语将此行为称作“优先授权父类加载器”。

另一方面,在有些情况下也需要与此相反的行为,JBI术语称为“优先授权自身加载器”。这是由组件和共享库的安装描述中的配置数据所控制的。当类加载器被调用时,是先授权给父加载器还是先自己处理,取决于它的授权方式。

  • 优先授权父加载器方式(parent-first):加载器必须先调用它的父加载器。只有父加载器无法加载时,它才会去亲自加载。
  • 优先授权自身加载器方式(self-first):加载器先亲自去加载类(或资源),如失败再去调用父加载器。

由于这两种类加载器的优先授权方式不同,导致类加载器之间的调用顺序和各个加载器去试图加载某个类或资源的顺序并不相同。如果所有的类加载器都采用优先授权父加载器的方式,那么当某个子类加载器加载某个类或资源的时候,就会自底向上逐层授权给父类加载器,而最终会由此层次结构中的根节点——组件类加载器最先去试图进行加载。

即便一个类加载器采用优先授权自身加载器的方式,在它加载一个完整类名是以“java.”或“javax.”开头的类的时候,也一定要采用优先授权父加载器的方式。

当装载一个类时,第一个被调用的类加载器被称作“起始类加载器”。而真正将该类加载的加载器被称作“定义类加载器”。类加载器的顺序规则会决定调用的顺序,也就是说,授权方式会影响类加载器加载类的实际顺序。

1.3.2 共享库(Shared Libraries)

JBI组件间的联系是很松散的,甚至可以只通过MessageExchange和NormalizedMessage接口交互。在一些重要的应用案例中,还需要另外利用NormalizedMessage属性交互,在该属性中有一个共享的类集,用来读写消息属性。例如,EDI绑定组件需要共享一个专门的类对象集,它定义了一个描述了一个专门服务引擎的EDI文档。绑定的组件的安装是独立于引擎的,二者如需共享一个通用库就需要实现共享类和共享类加载器。这需要JBI环境在执行类加载器的层面上为这样的共享类和类加载器提供支持。

为了使组件只能能够以一个可控的、可预见的方式共享类加载器,并且不会对其他系统组件造成不利影响,需要提供一个JBI环境下的特殊的类加载方案。接下来的执行类加载器部分将会对此进行描述。

1.3.3 执行类加载器(Execution Class Loader)

执行类加载器由一系列加载器组成,用来加载组件所需的不同的类和资源。此节将会对此进行描述。另外,执行类加载器还必须遵守随后将要介绍的顺序规则。

JBI实现必须为每个组件提供一个单独的组件类加载器,这个加载器可以访问组建安装描述文件中声明的类路径上的类和资源。当组件加载器会顺序的按照安装描述文件中声明的类路径来搜索类或组件。执行类加载器必须是JBI实现用来创造组件声明的Component类实例的初始化类加载器。

共享类加载器可以访问独立于组件安装的共享库,需强调的是组件可以利用他们来共享基于非标准的类和接口所创建的对象。这使共享类加载器所加载的类创建的对象具有唯一的命名空间。由于属于独立的命名空间,Java run-time会认为由不同的类加载器所加载的类创建的对象是不同类型的,甚至由同一个.class源文件创建的不同对象也是如此。共享类库加载器必须按照安装描述文件中的顺序搜索共享库。

JBI类加载器必须被用来加载JBI类。如果平台类加载器或内置的类加载器没有实现javax.xml.namespace.QName类的话,则JBI类加载器必须将它实现。
一些运行环境提供了可加载共享平台类的平台类加载器。对于一些环境来说这是非常实用的,比如在应用服务器中提供可加载除虚拟机内之类以外的类的加载器。

1.3.3.1 类的加载顺序规则(Class Loading Ordering Rules)

本节介绍类加载器的调用顺序规则。类加载器实际的搜索类的顺序将会被优先授权方式所影响。这里所讨论的顺序是指类加载器之间的调用顺序,而不是类加载器亲自去试图加载指定类(或资源)的顺序。

1. 组件类加载器必须是创建Component类的初始化加载器。应注意如果组件被配置为优先授权父加载器(默认配置方式)的话,那么组件类加载器将会在所有其他加载器之后采取试图加载目标类或资源。

2. 共享库类加载器必须按照组件安装描述文件中声明的顺序搜索类或资源。

3. 组件类加载器必须将所有组件类共享库加载器的集合当作一个单独的父加载器,也就是说,如果授权给它的父加载器,那么这些共享库类加载器会按特定的顺序调用。

4. 每个共享库类加载器都必须将JBI类加载器作为自己的父加载器。

5. JBI类加载器必须用平台共享类加载器或系统类加载器作为自己的父加载器。

6. 平台共享类加载器的父加载器并未在此指定,实际上它是由在平台所指定的。

JBI类加载器必须采用优先授权父加载器的方式。

1.3.3.2 授权方式(Delegation Style)

7.3.1节“类和资源的加载方式和顺序”讨论类加载器的授权方式及行为。如果组件的类加载器的安装描述符指定了

component-class –loader-delegation=”self-first”

则使用Self-first授权方式,否则使用parent-first方式。同样,如果一个共享库的安装描述符指定了

class –loader-delegation=”self-first”

共享库的类加载器使用self-first方式,否则使用parent-first方式。

1.3.3.3 安装和卸载共享库(Installation and Uninstallation of Shared Libraries)

使用管理InstallationServiceMBean安装(和卸载)共享库。详见“管理”一章。

共享库必须在安装使用它的组件之前安装,否则该组件的安装会出错。详见“管理”一章“共享库安装”和“组件安装”两节。

同样,如果至少有一个组件在使用不是“关闭”的共享库,就无法卸载该共享库。

1.3.3.4 组件的观点(Component Viewpoint)

JBI不发布绑定组件和服务引擎可用来检查和操纵JBI类加载模型的API。JBI系统保证组件的Component实现使用组件类加载器来构建。组件可隐式地使用组件类加载器构建其他对象。详见Java虚拟机规范第二版[JVM2]5.3节。

1.3.4 引导类加载器(Bootstrap Class Loader)

引导类加载器由一套类加载器组成,这些类加载器用来加载组件安装过程中所需的类和资源。另外,引导类加载器必须符合以下几小节定义的规则。

JBI实现为每个组件提供一个引导类加载器,用来创建组件引导类(在组件安装描述符中声明)的实例。注意,在组件的扩展生命周期中,JBI实现可以创建不止一个这样的实例,但不能同时使用多个实例。

引导类加载器提供组件引导类路径来访问这些类,类路径在组件安装描述符中的bootstrap-class-path声明中制定。引导类加载器按住奥安装描述符里给定的顺序搜索引导类的路径。

JBI类加载器用来加载JBI类。它提供javax.xml.namespace.QName类的一个实现(在JAX-P1.3中定义),如果平台类加载器(platform class loader)或内置类加载器没有提供这个实现的话。

有的执行系统会提供一个平台类加载器(platform class loader)用来加载共享的平台类。适用于应用服务器等系统,这些系统提供虚拟机内置类加载器以外的类。

1.3.4.1 类加载顺序规则(Class Loading Ordering Rules)

本节指定顺序调用引导类加载器相关的类加载器的规则。实际的查找顺序受类加载器所用授权方式的影响。该顺序指的是每个类加载器被调用的顺序,它不同于加载器定义给定的类(或资源)的顺序。

1. 创建组件引导Bootstrap类的时候首先使用引导类加载器。

2. 引导类加载器的父加载器是JBI类加载器。

3. JBI类加载器的父加载器一般是平台类加载器,否则使用系统类加载器作为父加载器。

4. 平台共享类加载器的父加载器是系统类加载器。

JBI类加载器必须使用parent-first授权方式。

1.3.4.2 授权形式(Delegation Style)

类加载器授权方式和行为在7.3.1节“类和资源加载方式和顺序”讨论。如果组件安装描述符指定了:

Bootstrap-class-loader-delegation=”self-first”

引导类加载器必须使用self-first授权方式,否则使用parent-first授权方式。

1.4 出错提示(Error Indication)

使用Java异常来提示出错信息。所有的JBI异常必须基于javax.jbi.JBIException,从而使组件和管理程序知道错误信息是JBI相关的。JBI异常的类型有:

  • 部署异常(DeploymentException)由DeploymentServiceMBean抛出给管理工具,表示部署失败。详见“管理”一章。
  • 消息异常(messagingExceptino)由NMR抛出,表示当创建、操作、发送或接收消息交换和规格化消息的时候产生的各种错误。详见“规格化消息路由”一章。

注意,有的JBI管理bean(MBean)方法规定它们抛出java.lang.Exception对象。这是为了避免远程JMX客户要求JBI异常类匹配版本的问题。

[8]——javax.jbi

类的说明  
异常

JBIException110

JBIException是由JBI提供的API和组件提供的SPI抛出的顶层异常。

声明

public class JBIException extends java.lang.Exception
java.lang.Object  

      |  

      +--java.lang.Throwable    

                |       

               +--java.lang.Exception           

                          |           

                         +--javax.jbi.JBIException

它们都实现接口:java.io.Serializable

两个子类:javax.jbi.management.DeploymentException141,

javax.jbi.messaging.MessagingException183

描述:JBIException是由JBI提供的API和组件提供的SPI抛出的顶层异常。

成员的说明  
构造器

Constructors

JBIException(java.lang.String message)111
JBIException(java.lang.String message, java.lang.Throwable cause)111
JBIException(java.lang.Throwable cause)111
火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。

资源网站: UML软件工程组织