OSGi的EventAdmin

大家都知道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機制的原因一樣,減少模塊間的依賴。使用模塊化開發的最重要原因就是低耦合,所以這一點很重要。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章