现在的位置: 首页 > 综合 > 正文

OSGi的EventAdmin

2014年12月13日 ⁄ 综合 ⁄ 共 3904字 ⁄ 字号 评论关闭

大家都知道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机制的原因一样,减少模块间的依赖。使用模块化开发的最重要原因就是低耦合,所以这一点很重要。

抱歉!评论已关闭.