OSGi的EventAdmin

来源:互联网 发布:合金装备5连接网络 编辑:程序博客网 时间:2024/06/04 17:47

大家都知道Eventing或者Publish / Subscribe机制对于低耦合系统的重要性。很多时候写一个listener接口,一个list用来记录所有的listener,当有event发生的时候,就遍历list来通知每个listener,这种方法最简单明了,但在模块化开发(比如OSGi)中,如果在模块之间实现Publish Subscribe 模式就没有这么简单了。就好像logging这么简单的东西,到了模块化开发中也颇费周折一样。原因很简单,想要从模块化中获利(“利”指的是热启动,热关闭,模块之间耦合度降低,灵活性大大加强,可扩展性加强,健壮性加强,少了几个模块,其他不强制依赖这几个模块的其他模块照样运行),就必然在一些"简单"功能的实现上要多费思量。


好在OSGi准备了EventAdmin service,来解决这个模块间eventing的问题。如果你熟悉JMS中的messaging机制,或者GWT的EventBus的话,EventAdmin基本上是同一回事。你所需要的就是org.eclipse.osgi.services这个插件。


下面这个例子是基于Eclipse这个IDE(Eclipse本身是基于OSGi的,所以对于任何基于OSGi的项目,笔者认为Eclipse是不二选择)

首先新建一个Plugin Project,在MANIFEST.MF中加两个依赖



新建一个类,先称之为EventBus,它将注册OSGi的Declarative Service,获取一个EventAdmin的实例

import org.osgi.service.event.EventAdmin;/** * <ul> * <li>Title: EventBus</li> * <li>Description: This class instantiates the <code>Event Admin</code> service. Bundles wishing to publish events must obtain the Event Admin service and call one of the event delivery methods.</li> * <li>Created: Oct 15, 2012 by: JQin</li> * </ul> */public class EventBus {    /**     * The eventBus.     */    private static EventAdmin eventBus;        /**     * @return     */    public static EventAdmin getEventBus() {        return eventBus;    }        /**     * Method will be used by DS to set the <code>org.osgi.service.event.EventAdmin</code> service.     * @param eventBus     */    public synchronized void setEventBus(EventAdmin eventBus) {        EventBus.eventBus = eventBus;        System.out.println("EventAdmin service is set!"); //$NON-NLS-1$    }        /**     * Method will be used by DS to unset the <code>org.osgi.service.event.EventAdmin</code> service.     * @param eventBus     */    public synchronized void unsetEventBus(EventAdmin eventBus) {        if (EventBus.eventBus == eventBus) {            EventBus.eventBus = null;        }        System.out.println("EventAdmin service is unset!"); //$NON-NLS-1$    }}

在OSGi中注册Declarative Service的步骤很简洁:在Project根目录下新建一个文件夹OSGI-INF,然后在这个文件夹中新建一个Component Definition文件

<?xml version="1.0" encoding="UTF-8"?><scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.jqin.common.eventbus">   <implementation class="com.jqin.common.eventbus.EventBus"/>   <reference bind="setEventBus" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unsetEventBus"/></scr:component>

在Eclipse里面运行的时候,记得要保证这么几个插件的存在:

org.eclipse.equinox.ds

org.eclipse.equinox.event

并且要设置为自动开始(auto-start 为 true)



当点击Run之后,如果Console立刻打印了

EventAdmin service is set!

就表示这个EventAdmin Declarative Service设置成功了!


使用起来非常方便,首先在这个插件的MANIFEST.MF中开放EventBus所在的包给其他插件。

和REST中定位资源的方式类似,EventAdmin通过定义成为topic的URI来区分不同的Event,比如下面这个event的topic就是

org/eclipse/equinox/events/MemoryEvent/CRITICAL

任何subscribe了这个topic的listener都可以接收到event信息

Event event =newEvent("org/eclipse/equinox/events/MemoryEvent/CRITICAL", null);eventAdmin.postEvent(event);

EventAdmin发送Event的方法有两种,一种是同步发送,即sendEvent,另一个是异步发送,即postEvent。二者的区别是sendEvent会block caller,确保所有的subscriber都接收到了event,而postEvent则是把event排到EventAdmin的一个queue里面,然后caller就不管了,也不会被block。


在新建Event的时候,除了需要topic来定义这个Event的URI以外,Event还支持一个Map变量用来存储属性,这个属性Map通常就被用来保存需要在Event中传输的数据。


下面介绍一个如何注册成为某个或者某些event的subscriber,同样通过Declarative Service。

先新建一个subscriber类

import org.osgi.service.event.Event;import org.osgi.service.event.EventHandler;public class Subscriber implements EventHandler {    /*      * (non-Javadoc)     * @see org.osgi.service.event.EventHandler#handleEvent(org.osgi.service.event.Event)     */    @Override    public void handleEvent(Event event) {        // TODO Auto-generated method stub    }}

在OSGI-INF中新建一个Component Definition文件,注册一个Provided Service


同样在这个文件中,注册一个名为event.topics的属性






完成后的component.xml文件类似于这样

<?xml version="1.0" encoding="UTF-8"?><scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="subscriber">   <implementation class="Subscriber"/>   <property name="event.topics" type="String" value="org/eclipse/equinox/events/MemoryEvent/*"/>   <service>      <provide interface="org.osgi.service.event.EventHandler"/>   </service></scr:component>


这就表示这个Subscriber注册了org/eclipse/equinox/events/MemoryEvent/* 这个topic,大家都知道 * 标记指的是任何,所以前面发送的Event topic是org/eclipse/equinox/events/MemoryEvent/CRITICAL自然也其中。

推荐基于OSGi模块化开发的程序员使用EventAdmin的原因和推荐使用Publish Subscribe pattern,以及eventing/messaging机制的原因一样,减少模块间的依赖。使用模块化开发的最重要原因就是低耦合,所以这一点很重要。