OSGi R4服务平台核心规范 :第五章 服务层(2)

 由  满江红开放技术研究组织 发布

版权说明

本文档版权归原作译者所有。 在免费、且无任何附加条件的前提下,可在网络媒体中自由传播。

如需部分或者全文引用,请事先征求作译者意见。

如果本文对您有些许帮助,表达谢意的最好方式,是将您发现的问题和文档改进意见及时反馈给作者。当然,倘若有时间和能力,能为技术群体无偿贡献自己的所学为最好的回馈。

本文档可从http://www.redsaga.com获取最新更新信息

5.5. 过滤器

框架提供了一个Filter接口,并且在方法getServiceReferences中使用过滤器语法(在过滤器语法一节中定义)。通过调用BundleContext的方法createFilter(String)或者是FrameworkUtil的方法createFilter(String)来创建一个Filter对象,方法中的参数为一个选定的过滤规则字符串。过滤字符串支持以下匹配方法:

  • match(ServiceReference) – 通过对Service Reference的属性进行匹配,匹配不区分大小写。

  • match(Dictionary) – 通过给定的Dictionary对象来查找,也是不区分大小写的。

  • matchCase(Dictionary) –通过给定的Dictionary对象来查找,不同之处在于是区分大小写的。

可以对一个Filter对象进行多次匹配来确定是否匹配。如果找到了匹配的ServiceReference对象或者Dictionary对象,那么就通过它们来创建Filter对象。

匹配的过程需要将过滤器中的字符串和目标对象包括服务属性或者是Dictionary进行比较。如果目标对象的类实现了带有一个字符串参数的构造方法,并且实现了Comparable接口,那么就可以通过Comparable接口来进行比较。也就是说,如果目标对象是一个Target类,那么这个Target类必须实现:

  • 构造方法Target(String)

  • 实现了接口java.lang.Comparable

如果目标类没有实现接口Comparable,那么当对象相等(使用equals方法),对对象使用操作符=、~=、<=、>=将只能返回true。Target类不一定要是public类,下面的例子说明了类可以通过过滤器对一个枚举分类进行校验:

public class B implements Comparable { String keys[] = {"bugs", "daffy", "elmer", "pepe"}; int index; public B(String s) { for ( index=0; index

public int compareTo( Object other ) { B vother = (B) other; return index - vother.index; } }

可以对这个类使用如下过滤器:

(!(enum>=elmer)) -> matches bugs and daffy

方法Filter.toString必须返回一个不带空格的过滤字符串。

5.6. 服务工厂

通过服务工厂,可以在bundle调用方法BundleContext.getService(ServiceReference)时返回一个定制的服务对象。

通常,对一个bundle注册的服务对象的调用是直接返回这个服务对象。但是,如果这个注册的服务对象实现了ServiceFactory接口,那么框架必须调用这个服务对象的方法来为每一个使用服务的bundle创建一个惟一的服务对象。当bundle不再使用服务对象——比如停止了bundle之后——那么框架必须要将事件通知ServiceFactory对象。

可以用ServiceFactory来管理bundle间的依赖关系,而框架并没有很明确的管理这种依赖关系。通过绑定一个请求bundle的返回服务对象,那么当bundle停止使用服务之后,比如停止bundle,服务对象就可以接收到事件通知,然后就可以释放提供给那个bundle的服务资源。

在ServiceFactory接口中定义了以下方法:

  • getService(Bundle,ServiceRegistration) –如果是方法BundleContext.getService来调用,并且下述情况为真,则由框架调用这个方法:

  • BundleContext.getService中的参数ServiceReference关联的服务对象实现了ServiceFactory接口。

  • bundle对那个服务对象的使用计数是0,也就说bundle当前并没有对这个服务对象的任何依赖。

对方法BundleContext.getService的调用请求必须要是由框架发出到getService方法,将bundle对象发送给调用者。框架必须要缓存请求的bundle-to-service映射,并且如果以后bundle对这个服务对象的使用计数要大于0,则对于BundleContext.getService调用,返回给bundle缓存的服务对象。

框架必须检查方法返回的服务对象。如果它不是服务工厂注册时的类的一个实例,那么对getService方法的调用返回null值。这种检查必须根据注册服务一节中描述的规范来进行。

  • ungetService(Bundle,ServiceRegistration,Object) – 如果是BundleContext.ungetService方法调用,并且下述条件为真,则由框架调用这个方法:

  • BundleContext.getService中的参数ServiceReference关联的服务对象实现了ServiceFactory接口。

  • 调用返回之后,bundle对服务对象的使用计数降为零,也就说bundle即将释放对这个服务对象的依赖。

对方法BundleContext.getService的调用请求必须要是由框架发出到getService方法,这样ServiceFactory对象就可以将事先创建的服务对象释放。

另外,服务对象事先创建的缓存拷贝必须是和框架没有引用关联的,这样就可以通过gc进行回收。

5.7. 服务释放

如果bundle需要释放一个服务对象,那么bundle必须移除它和注册服务的bundle之间的动态依赖。在接口BundleContext中定义了一个方法来释放服务对象:ungetService(ServiceReference),在这个方法中使用了一个ServiceReference对象作为参数。

方法返回一个布尔值:

  • false:如果调用方法时,bundle对服务对象的使用计数为0,或者是已经取消注册了服务对象。

  • true:如果调用方法时,bundle对服务对象的使用计数大于0。

5.8. 取消注册服务

在接口ServiceRegistration中定义了方法unregister()来取消注册一个服务对象。这个方法必须要从框架的服务注册中心移除指定服务对象。ServiceRegistration对象的ServiceReference对象不能再对服务对象进行访问。

ServiceRegistration对象中的这个方法的实际作用在于确保了只有拥有这个对象的bundle才可以取消注册相关的服务对象。那么取消服务注册的bundle与注册服务的bundle可能不是同一个bundle。例如,注册服务的bundle将ServiceRegistration对象发送给另一个bundle,这样,就将取消注册服务的职责赋予给另一个bundle。对ServiceRegistration对象的传递应该特别小心。

方法ServiceRegistration.unregister成功返回之后,服务对象必须达到如下要求:

  • 从框架的服务注册中心完全的移除了。因此,服务对象的ServiceReference对象也就不能再用于进行访问服务对象了。对方法BundleContext.getService的调用也将返回null值。

  • 处于未注册状态,即使存在其他bundle的依赖。所有的bundle都应该接收到取消注册的事件通知,事件类型是ServiceEvent.UNREGISTERING。这个事件是同步发送的,这样bundle就可以释放服务对象。 bundle接收到ServiceEvent.UNREGISTERING事件之后,bundle应该释放服务对象,释放对这个对象的任何引用,这样Java VM的gc就可以回收服务对象的资源。

  • 所有使用服务的bundle释放资源。对于调用事件监听器之后,如果bundle对服务对象的使用计数还是大于0,那么就由框架来将使用计数清零,并释放服务对象。

5.9. 多版本导出

允许多个bundle导出同名的包给框架实现和bundle开发带来了复杂度的提高:不能用类名来惟一标志导出的类。这影响到了服务注册中心和权限的检查。

5.9.1. 服务注册中心

bundle不能暴露存在冲突的类加载器的服务。bundle获取的服务应该是可以安全的声明为服务注册时登记的接口或者类的类型,并且应该是可以访问获取的服务。而不能因为这些接口来自于不同的类加载器就抛出类声明异常(ClassCastExceptions)。而服务注册中心应该确保bundle所访问的服务都是没有冲突的。bundle获取的服务是没有冲突的是指:和注册服务的bundle相比较,这个bundle没有和接口包的另一个类加载器进行连接。也就是说,它要么连接到同一个类加载器,要么就和那个包没有进行连接。

要求bundle中不能有存在冲突的服务是非常重要的,因此,可以通过以下方法来过滤存在冲突的ServiceReferenc对象。通过BundleContext对象来标记bundle:

  • getServiceReference(String) – 返回和调用bundle的指定接口不冲突的一个ServiceReference对象。

  • getServiceReferences(String,String) – 返回和调用bundle的指定接口不冲突的多个ServiceReference对象。

方法getAllServiceReferences(String,String)提供了对服务注册中心的所有没有任何兼容约束的服务的访问,通过这个方法获取的服务可能会导致抛出类声明异常。

ServiceReference类中定义的方法isAssignableTo(Bundle,String)也可以用来测试这个ServiceReference对象关联的服务的注册bundle和指定的bundle是否关联到了指定接口的同样的源。

5.9.2. 服务事件

服务事件只发送到和ServiceReference不矛盾的事件监听器。

而有一些bundle需要监听所有的服务事件而不考虑是否存在冲突的问题。这样增加了一种新的服务监听器:

AllServiceListener。这是一个标记接口,继承了ServiceListener。使用了这个标记接口的监听器向框架标明了它们需要可以访问到所有的服务,包括存在冲突的服务。

5.10. 安全

5.10.1. 服务权限控制

ServicePermission中包括以下变量:

  • Interface Name – 接口名称中可能包括了一个通配符来匹配多个接口名称(在java.security.BasicPermission中描述了通配符)。

  • Action – 支持的Action包括:

  • REGISTER – 权限的持有者可以注册这个服务对象。

  • GET – 权限持有者可以获取服务。

当使用BundleContext.registerService来注册一个服务对象时,注册服务的bundle必须要有的ServicePermission权限。参阅服务注册一节。

当通过BundleContext.getServiceReference或者BundleContext.getServiceReferences来获取一个ServiceReference对象时,调用方法的bundle必须要ServicePermission[, GET]权限来获取服务对象。参阅服务引用一节。 当通过BundleContext.getService(ServiceReference)方法来获取一个服务对象,调用方法的代码必须至少有关于注册的一个类的权限:ServicePermission[, GET]。 ServicePermission必须用于对服务事件监听器接收的事件的过滤器,同时也是列举服务的方法的过滤器,方法包括了Bundle.getRegisteredServices和Bundle.getServicesInUse。框架必须要确保bundle不能够检测到它没有权限访问的服务的状态。

查看评论