2011年5月3日 星期二

OSGI - WhiteBoard Pattern

Spring DM 的核心思維就是採用 OSGi 的 Extender Model。
(運用 OSGi 有二個重要的概念,Extender Model 與 Whiteboard Pattern), 這邊主要針對Whiteboard Pattern做例子。
實現OSGi Extender Model非常的簡單,編寫一個Bundle Listener,監聽Bundle的安裝、卸載、更新。
Extender Model 的運作模式就是,您預先準備好一個 Bundle。這個 Bundle 會在您要啟用的子系統前先啟動完畢。它會註冊 Bundle Listener,發現有 Bundle 啟動時,就去它的 metadata 或找到需要作用的特徵。並對它自動地,做某些事情。
可以參考
OSGi 4.2: Extender Pattern and BundleTracker
(Spring DM 就實作了類似的 Extender Bundle,當您的 Bundle 有採用 Spring Framework 時(通常是判斷 /META-INF/spring/ 資料夾),它會將 Spring Configuration 取出,做 Bean 初始化的動作。)

WhiteBoard actors


OSGi的白板模式(White board Pattern) , 是什麼?Whiteboard Pattern比原始監聽模式的要更進一步,利用OSGI本身的ServiceTracker服務跟踪監聽class作為Listener的觸發機制,這樣,基於OSGI開發事件模式,就不用把Listener註冊到Source,從而發生緊耦合,只要雙方直接註冊到OSGI中就可以了。

在OSGi WhiteBoard Pattern 的pdf中提到一些information ,
可在這找到
Listeners Considered Harmful:The “Whiteboard” Pattern

文中主要是針對Listener Pattern 跟 Whiteboard Pattern的比較,這邊我只實作whiteboard pattern ,建立一個Interface ,提供getUpdateMessage()的method , 有2個Consumer實作Interface ,
一個Producer包含了ServiceTracker,可取得目前要監看的Interface service
Bundle class list show as below:

WhiteBoardPattern.Interface Bundle Detail
WhiteBoardPattern.Interface 裡面只包含一個IUpdateCommand Interface , Export-package 出去

WhiteBoardPattern.Interface.IUpdateCommand
package WhiteBoardPattern.Interface;

public interface IUpdateCommand {
 public String getUpdateMessage();
}

Manifest.mf 檔
Manifest-Version: 1.0
Export-Package: WhiteBoardPattern.Interface;version="1.0.0"
Unversioned-Imports: *
Build-Jdk: 1.6.0_24
Built-By: momo
Bundle-Version: 1.0.0
Tool: Bnd-1.15.0
Bnd-LastModified: 1304393278593
Bundle-Name: Spring OSGi Bundle
Bundle-ManifestVersion: 2
Created-By: Apache Maven Bundle Plugin
Bundle-SymbolicName: WhiteBoardPattern.Interface


WhiteBoardPattern.Consumer Bundle Detail

WhiteBoardPattern.Consumer.PluginConsumer
package WhiteBoardPattern.Consumer;

import WhiteBoardPattern.Interface.IUpdateCommand;

/**
 * @author momo
 *
 */
public class PluginConsumer implements IUpdateCommand {

 public String getUpdateMessage() {
  return "Update Class is "   this.getClass().getName();
 }

}

Bundle-Context.xml 設定
<bean id="pluginConsumer1" class="WhiteBoardPattern.Consumer.PluginConsumer"></bean>


Osgi-Context.xml 設定
<osgi:service ref="pluginConsumer1" interface="WhiteBoardPattern.Interface.IUpdateCommand" />


Manifest.mf 檔
Manifest-Version: 1.0
Export-Package: WhiteBoardPattern.Consumer;version=&quot;1.0.0&quot;;uses:=&quot;Whit
 eBoardPattern.Interface&quot;
Unversioned-Imports: *
Built-By: momo
Tool: Bnd-1.15.0
Bundle-Name: Spring OSGi Bundle
Created-By: Apache Maven Bundle Plugin
Bundle-Version: 1.0.0
Build-Jdk: 1.6.0_24
Bnd-LastModified: 1304392985843
Bundle-ManifestVersion: 2
Import-Package: WhiteBoardPattern.Interface
Bundle-SymbolicName: WhiteBoardPattern.Consumer

WhiteBoardPattern.Consumer2 設定跟WhiteBoardPattern.Consumer1 是一樣的
2個都Implements IUpdateCommand,不同的只有Bundle-SymbolicName


WhiteBoardPattern.Producer Bundle Detail
Bundle project as below:

WhiteBoardPattern.Producer.WhiteBoardProducer
WhiteBoardProducer 本身包含了一個ServiceTracker, implements BundleContextAware, Spring 做IOC的動作
package WhiteBoardPattern.Producer;

import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.util.tracker.ServiceTracker;
import org.springframework.osgi.context.BundleContextAware;
import WhiteBoardPattern.Interface.IUpdateCommand;

public class WhiteBoardProducer implements BundleContextAware{
 
 private volatile boolean stop = false;
 private ServiceTracker tracker;
 private BundleContext bundleCtx;
 
 
 public void setBundleContext(BundleContext bundleContext) {
  this.bundleCtx = bundleContext;  
  
 }
 
 public void start() throws Exception {
  
  tracker = new ServiceTracker(bundleCtx,IUpdateCommand.class.getName(),null);
  tracker.open();
  
  Thread t = new Thread(){
   public void run(){
    try {
     Thread.sleep(1000);
     System.out.println("tick");
     // Get a list of the IWhiteboardExample services and notify them.
     Object[] services = tracker.getServices();
     System.out.println("services = "  services);
     if (services != null) {
      for(Object s : services) {
       IUpdateCommand iupdate = (IUpdateCommand)s;
       System.out.println("getUpdateMessaget = "  iupdate.getUpdateMessage());       
      }
     }
    } catch (InterruptedException e) {
     // Ignore interruptions.
    }
   }
   
  };
  t.start();
 }
 
 public void stop() throws Exception{
  stop = true;
  tracker.close();
 }
}


bundle-context.xml 設定,Producer 本身不會註冊到Osgi service 裡面,
<bean id="activator" class="WhiteBoardPattern.Producer.WhiteBoardProducer"
       init-method="start" destroy-method="stop"></bean>

Manifest.mf 檔
Manifest-Version: 1.0
Export-Package: WhiteBoardPattern.Producer;version="1.0.0";uses:="org.
 osgi.framework,org.osgi.util.tracker,org.springframework.osgi.context
 "
Unversioned-Imports: *
Built-By: momo
Tool: Bnd-1.15.0
Bundle-Name: Spring OSGi Bundle
Created-By: Apache Maven Bundle Plugin
Bundle-Version: 1.0.0
Build-Jdk: 1.6.0_24
Bnd-LastModified: 1304392978718
Bundle-ManifestVersion: 2
Import-Package: WhiteBoardPattern.Interface,org.osgi.framework,org.osg
 i.util.tracker,org.springframework.osgi.context
Bundle-SymbolicName: WhiteBoardPattern.Producer

先把WhiteBoardPattern.Interface , WhiteBoardPattern.Consumer1, WhiteBoardPattern.Consumer2
都安裝到OSGi container 裡, 之後再把WhiteBoardPattern.Producer 啟動
簡單的Resout 如下



當WhiteBoardPattern.Producer 啟動時, 會去從ServiceTracker 監聽IUpdateCommand.class實作出來的Service , 然後調用該Interface 的method 。
這樣可以做什麼? 比如說我想要知道新的Bundle Install 進來時要把一些MANIFEST裡的header or BundleContext 下的Resource Information回傳過來,集中對ServiceTracker裡面的service list做處理,
這樣做每個Bundle 只需要implements interface 所置定的method,並註冊到OSGi裡面。
每次WhiteBoardPattern.Producer啟動時就會找出全部IUpdateCommand Service 的information。

*如果需要Listener 的話可以實作BundleListener,ServiceListener or FrameworkListener,

Reference :
The Whiteboard Pattern for OSGi

The Whiteboard Pattern

沒有留言:

張貼留言