CONAN ZONE

          你越掙扎我就越興奮

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks

          一年一度的Ig Nobel prize典禮都都會帶來一些非常新穎的觀點、發(fā)現(xiàn),這些內(nèi)容甚至超過了Ig Nobel prizes本身。每位獲獎?wù)咴谧鐾昶邆€字的總結(jié)后,還有機會利用24秒的時間對自己的新觀點、新發(fā)現(xiàn)進行闡述。

           

          這是一個極其絕妙的主意,這對每位獲獎?wù)叨际且粋€需要完成的挑戰(zhàn)。

           

          OSGi 是近來業(yè)界經(jīng)常提到的事物,隨著Equinox成為Eclipse的頂級項目,Felix被用于SlingGlassfish V3的容器,以及Spring-Modules的發(fā)布。但是,很多人都不熟悉OSGi…而且一直不去了解它,也不在意他人正在循序漸進的了解OSGi

           

          至于我,我不是OSGi的鼓吹者,僅僅是喜歡花些時間試圖向那些還未了解OSGi的朋友進行講解。顯然,做點少量的范例代碼能幫助大家盡快上手。再次強調(diào),我不是OSGi的鼓吹者(也不會站在任何OSGi的立場上。)我僅僅是個普通人,看著OSGi的確不錯,并且它的文檔也不多才寫作本文。當然,如果你很熟悉OSGi,那么不需要閱讀本文。(譯注:這人太羅嗦了,和唐僧有一拼!)

           

          首先,我給大家做七字總結(jié),然后是24秒的闡述,接著我將用范例來解釋這一切。

           

          OSGi 的七字總結(jié)和24秒闡述

          OSGi 是一個為Java而設(shè)計的組件框架。

           

          24 秒的闡述:OSGi是一個Java框架,該框架能裝載以bundle為單位的資源。Bundle能提供服務(wù)或響應(yīng)處理請求,而他們之間的依賴都是被管理起來的,正如一個bundle能從容器中獲得它所需要的管理。每個bundle都可以有它自己的內(nèi)部類路徑,所以它可以作為獨立的服務(wù)單元。所有的這些符合OSGi規(guī)范的bundle理論上都可以安裝在任何符合OSGi規(guī)范的容器中。

           

          閑聊一下,不管我念得如何得快都要花掉24秒,貌似我就只能念這么快了。還缺少什么?喔,這則闡述缺少對為何需要這樣一個模塊化系統(tǒng)的解釋。

           

          為什么需要模塊化系統(tǒng)?

          模塊化系統(tǒng)為分布式bundle提供了翻譯支持(這里的“bundle”超過了“OSGi bundle”的范疇—我習(xí)慣使用這一術(shù)語來做比喻。)當然,模塊話系統(tǒng)的依賴關(guān)系是個話題,生命周期也是個話題… …有趣。

           

          所有這些都很重要;所謂翻譯并不是使用web serviceEJB翻譯依然被強迫通過JNDI方式,jar之間的依賴由并行jar部署來管理(除了JCAWAR,當然他們也有不同的方式進行依賴管理)。

           

          Java EE 是一套解決方案,盡管這些都不是必要的:WARJCA可以包含jar文件,EJB jar通過配置他們的manifest能參考其他jar文件,盡管應(yīng)用服務(wù)器能提供高級的類資源庫;一旦你在相同web應(yīng)用程序或web service中使用不同的版本,JNDI將提供版本檢測機制。生命周期是為web應(yīng)用程序(加載并啟動 servlets、上下文監(jiān)聽器)和JCA而存在的,但是EJB3.1可能會有自己的生命周期機制—還不確定。

           

          現(xiàn)在我們知道了,Java EE就是個棒槌可以搞定一切事情。

           

          OSGi JSR-277試圖把Java的模塊化部署標準化起來,而不是強行往Java EE概念上靠攏,這樣也能避免Java EE關(guān)于依賴和版本檢測以及生命周期方面的弱點。既然本文以OSGi而非模型為主題,那就集中在OSGi上吧

           

          開始OSGi

          簡單的講,運行OSGi是非常簡單的,基本上沒什么樂趣:尋找一個OSGi容器的實現(xiàn)方案(EquinoxFelixKnopflerfishProSyst),并運行這些容器的啟動命令,有點像在運行Java EE的服務(wù)器。類似Java EE,每個容器都有不同的啟動環(huán)境和細小的性能差異;請檢查你選擇容器的具體信息和選項。為了更加清晰點,我將在本文中采用Equinox

           

          Equinox 是一個OSGi容器,你可以從http://download.eclipse.org/eclipse/equinox/下載它。下載的文件是ZIP格式,解壓縮到“eclipse”目錄,不必驚訝:EquinoxEclipse內(nèi)置的OSGi容器。(我將把包含所有Equinox發(fā)布的頂級目錄“/eclipse”作為$EQUINOX變量。)文檔在Equinox快速入門中可以找到,訪問$EQUINOX/plugins將顯示出很多jar文件:

          /opt/tools/eclipse/plugins> ls
          javax.servlet.jsp_2
          .0.0 .v200706191603.jar                    org.eclipse.equinox.jsp.jasper_1 .0.1 .R33x_v20070816.jar
          javax.servlet_2
          .4.0 .v200706111738.jar                        org.eclipse.equinox.launcher_1 .0.1 .R33x_v20070828.jar
          org.apache.commons.el_1
          .0.0 .v200706111724.jar                org.eclipse.equinox.launcher_1 .0.1 .R33x_v20080118.jar
          org.apache.commons.logging_1
          .0.4 .v200706111724.jar           org.eclipse.equinox.log_1 .0.100 .v20070226.jar
          org.apache.jasper_5
          .5.17 .v200706111724.jar                   org.eclipse.equinox.metatype_1 .0.0 .v20070226.jar
          org.eclipse.equinox.app_1
          .0.1 .R33x_v20070828.jar             org.eclipse.equinox.preferences_3 .2.100 .v20070522.jar
          org.eclipse.equinox.common_3
          .3.0 .v20070426.jar               org.eclipse.equinox.preferences_3 .2.101 .R33x_v20080117.jar
          org.eclipse.equinox.device_1
          .0.0 .v20070226.jar               org.eclipse.equinox.registry_3 .3.1 .R33x_v20070802.jar
          org.eclipse.equinox.event_1
          .0.100 .v20070516.jar              org.eclipse.equinox.servletbridge_1 .0.1 .R33x_v20070816.jar
          org.eclipse.equinox.http.jetty_1
          .0.1 .R33x_v20070816.jar      org.eclipse.equinox.source_3 .3.1 .R33x_r20070918-7n7LECgEKVsLIM1aGBO4b00
          org.eclipse.equinox.http.registry_1
          .0.0 .v20070608.jar        org.eclipse.equinox.source_3 .3.1 .R33x_r20070918-7n7LEClEIdwb-bbP_z--EYAO
          org.eclipse.equinox.http.registry_1
          .0.1 .R33x_v20071231.jar   org.eclipse.equinox.useradmin_1 .0.0 .v20070226.jar
          org.eclipse.equinox.http.servlet_1
          .0.1 .R33x_v20070816.jar    org.eclipse.osgi.services_3 .1.200 .v20070605.jar
          org.eclipse.equinox.http.servletbridge_1
          .0.0 .v20070523.jar   org.eclipse.osgi.util_3 .1.200 .v20070605.jar
          org.eclipse.equinox.http_1
          .0.100 .v20070423.jar               org.eclipse.osgi_3 .3.1 .R33x_v20070828.jar
          org.eclipse.equinox.http_1
          .0.101 .R33x_v20071016.jar          org.eclipse.osgi_3 .3.2 .R33x_v20080105.jar
          org.eclipse.equinox.jsp.jasper.registry_1
          .0.0 .v20070607.jar  org.mortbay.jetty_5 .1.11 .v200706111724.jar


          啟動Equinox很簡單:在這個目錄中,執(zhí)行java -jar org.eclipse.osgi_3.3.2.R33x_v20080105.jar –console(在控制臺窗口),然后你將看到一個命令提示符:

          osgi>


          這就是EquinoxOSGi控制臺。從這里開始,你可以安裝新的bundle并啟動、停止、卸載他們,檢查他們的依賴、注冊服務(wù),或者其他東西。你嘗試到的第一個命令將是“ss”,是“short status”的縮寫。如果當前是全新安裝,這樣的交互看起來是這樣的:

          osgi> ss

          Framework is launched.

          id      State       Bundle
          0        ACTIVE      org.eclipse.osgi_3 .3.2 .R33x_v20080105

          osgi>

          這樣的顯示證明容器是運行著的,并安裝了一個bundle。這個bundleid為“0”,這個id有很大的作用,因為你可以使用它來控制bundle的生命周期。

           

          那么,bundle有什么優(yōu)點呢?bundle提供了生命周期和服務(wù)暴露,就像我們在24秒的總結(jié)中提到一樣。

           

          將一個簡單的資料庫放入Bundle

          讓我們創(chuàng)建一個bundle。我想利用資料庫存儲信息。我的最初接口看起來是這樣的:

          package  repository;

          public   interface  RepositoryService {
                  Node put(String path, Node content);
                  Node get(String path);

          }

          不是太多,這是一個開始:我可以利用它來存放或提取信息。我的節(jié)點類是一個簡單的POJO,看起來是這樣的:

          package  repository;

          import  java.util.ArrayList;
          import  java.util.Date;
          import  java.util.List;

          public   class  Node {
                  Date created 
          =   new  Date();
                  String source 
          =   " unknown " ;
                  String author 
          =   " unknown " ;
                  List contents 
          =   new  ArrayList < String > ();
                  String path;

                  @Override
                  
          public  String toString() {
                          String s 
          =   " node: path= "   +  getPath()  +   " , author= "   +  getAuthor()
                                          
          +   " , created= "   +  getCreated()  +   " , source= "   +  getSource()
                                          
          +   " , data=[ " ;
                          String separator 
          =   "" ;
                          
          for  (String d : getContents()) {
                                  s 
          +=  separator  +  d;
                                  separator 
          =   " , " ;
                          }
                          s 
          +=   " ] " ;
                          
          return  s;
                  }

                  
          public  Node() {

                  }

                  
          public  Node(String content) {
                          getContents().add(content);
                  }

                  
          public  Node(String content, String context) {
                          
          this (content);
                          setSource(context);
                  }

                  
          //  .. accessors and mutators go here.
          }

          這是一個服務(wù)實現(xiàn),它可以獨立于OSGi進行實現(xiàn)和測試;到此為止我們還沒有涉及到OSGi。要向OSGi邁進就應(yīng)該有個服務(wù)的實現(xiàn)。我最初的代碼是以Map為基礎(chǔ):

          import  java.util.HashMap;
          import  java.util.Map;

          import  repository.Node;
          import  repository.RepositoryService;

          public   class  MapRepositoryService  implements  RepositoryService {
                  Map
          < String, Node >  data = new  HashMap < String, Node > ();

                  
          public  Node put(String path, Node content) {
                          
          return  data.put(path, content);
                  }

                  
          public  Node get(String path) {
                          
          return  data.get(path);
                  }

          }

          然而,一個Map不能很好的實現(xiàn)查詢功能,而且很有可能遇到有層次結(jié)構(gòu)的信息。我想用DOM來代替它。所以我現(xiàn)在將引入XOM,并重新實現(xiàn)資源庫服務(wù):

          package  repository.impl;

          import  java.util.Date;

          import  nu.xom.Attribute;
          import  nu.xom.Document;
          import  nu.xom.Element;
          import  nu.xom.Elements;
          import  nu.xom.Nodes;
          import  nu.xom.ParentNode;
          import  repository.Node;
          import  repository.RepositoryService;

          public   class  XMLRepositoryService  implements  RepositoryService {
                  Document document;
                  Element data;

                  
          public  XMLRepositoryService() {
                          data 
          =   new  Element( " data " );
                          document 
          =   new  Document(data);
                  }

                  Node toNode(nu.xom.Element node) {
                          
          if  (node.getAttribute( " source " ==   null ) {
                                  
          return   null ;
                          }
                          Node n 
          =   new  Node();
                          n.setAuthor(node.getAttributeValue(
          " author " ));
                          n.setCreated(
          new  Date(node.getAttributeValue( " created " )));
                          n.setSource(node.getAttributeValue(
          " source " ));
                          Elements e 
          =  node.getChildElements( " contents " );
                          n.setPath(node.getAttributeValue(
          " path " ));
                          
          for  ( int  i  =   0 ; i  <  e.size(); i ++ ) {
                                  Element elt 
          =  e.get(i);
                                  
          for  ( int  i1  =   0 ; i1  <  elt.getChildCount(); i1 ++ ) {
                                          n.getContents().add(elt.getChild(i1).getValue());
                                  }
                          }
                          
          return  n;
                  }

                  nu.xom.Element getElement(String path) {
                          
          while  ( ! path.startsWith( " // " )) {
                                  path 
          =   " / "   +  path;
                          }
                          
          while  (path.endsWith( " / " )) {
                                  path 
          =  path.substring( 0 , path.length()  -   1 );
                          }
                          Nodes nodes 
          =  document.query(path);
                          
          if  (nodes.size()  >   0 ) {
                                  
          return  (Element) nodes.get( 0 );
                          }
                          
          return   null ;
                  }

                  
          public  Node get(String path) {
                          Element e 
          =  getElement(path);
                          
          if  (e  !=   null ) {
                                  
          return  toNode(e);
                          }
                          
          return   null ;
                  }

                  
          public  Node put(String path, Node content) {
                          Element oldElt 
          =  getElement(path);
                          
          if  (oldElt  !=   null ) {
                                  
          //  need to remove this node!
                                  ParentNode p  =  oldElt.getParent();
                                  p.removeChild(oldElt);
                          }
                          StringBuilder pathBuilder
          = new  StringBuilder( " / " );
                          String[] tree 
          =  path.split( " / " );
                          Element node 
          =  data;
                          
          for  (String t : tree) {
                                  
          if  (t.length()  !=   0 ) {
                                          Element child 
          =  node.getFirstChildElement(t);
                                          
          if  (child  ==   null ) {
                                                  
          // System.err.println("creating new "+t);
                                                  child  =   new  Element(t);
                                                  node.appendChild(child);
                                          }
                                          pathBuilder.append(
          ' / ' );
                                          pathBuilder.append(t);
                                          node 
          =  child;
                                  }
                          }
                          node.addAttribute(
          new  Attribute( " created " , content.getCreated()
                                          .toString()));
                          node.addAttribute(
          new  Attribute( " source " , content.getSource()));
                          node.addAttribute(
          new  Attribute( " author " , content.getAuthor()));
                          content.setPath(pathBuilder.toString());
                          node.addAttribute(
          new  Attribute( " path " , content.getPath()));
                          Element contents 
          =   new  Element( " contents " );
                          node.appendChild(contents);
                          
          for  (String c : content.getContents()) {
                                  Element e 
          =   new  Element( " content " );
                                  e.appendChild(c);
                                  contents.appendChild(e);
                          }
                          
          // System.out.println(data.toXML());
                           return   null ;
                  }

                  
          public   static   void  main(String[] args) {
                          XMLRepositoryService s 
          =   new  XMLRepositoryService();
                          s.put(
          " /foo/bar/baz " new  Node( " stuff " ));
                          Node n 
          =   new  Node( " bletch " );
                          n.setAuthor(
          " jottinger " );
                          s.put(
          " /foo/bar/bletch " , n);
                          System.out.println(s.get(
          " foo/bar/baz/ " ));

                          System.out.println(s.get(
          " //foo/bar/baz " ));
                          System.out.println(s.get(
          " //foo/bar/bletch " ));
                          System.out.println(s.get(
          " foo/bar/ " ));
                          System.out.println(s.get(
          " //*[@author='jottinger'] " ));
                  }
          }

          這離完美還有很長的距離,不過總算是個好的開始。然而,我們還是沒涉及到任何關(guān)于OSGi方面的東西—我們只有一個無關(guān)緊要的資源庫類。然后我們來看看OSGi模塊是怎樣被構(gòu)建的。

           

          轉(zhuǎn)移目標:根據(jù)依賴關(guān)系構(gòu)造OSGi bundle

          一個OSGi模塊是一個.jar文件,在這里,它應(yīng)該遵循標準的.jar文件規(guī)范,除此之外還有一部分信息應(yīng)該放進manifest文件中。

           

          讓我們創(chuàng)建一個簡單的模塊,首先,最簡單的它應(yīng)該能在啟動和結(jié)束的時候顯示消息,作為內(nèi)部依賴將引入一個.jar文件。這個jar文件對其他bundle是不可見的-它只是作為我們的范例bundle的資源,在鄙文中這是一個很簡單和普通的需求。(是的,不怎么樣的例子-我在尋找更簡單的,但沒找到。)依賴將放在jar中,baselib.jar,這里面只有一個類:baselib.BaseService

          package  baselib;

          import  java.util.logging.Logger;

          public   class  BaseService {
            Logger log
          = Logger.getLogger( this .getClass().getName());
            
          public   void  sayHello() {
              log.info(
          " Hello, world! " );
            }
          }

          接下來要做的是把編譯好的文件放進.jar中,這就是baselib.jar

           

          現(xiàn)在,一個OSGi bundle需要一個“activator”,是一個管理bundle生命周期的類。一個activator要實現(xiàn)org.osgi.framework.BundleActivator接口,該接口有兩個方法:startBundleContext)以及stopBundleContext)。這些生命周期方法能讓bundle注冊服務(wù)或啟動服務(wù),但是在這里它還很簡單:

          package  tutorial;

          import  baselib.BaseService;

          import  org.osgi.framework.BundleActivator;
          import  org.osgi.framework.BundleContext;

          import  java.util.logging.Logger;

          public   class  TutorialActivator  implements  BundleActivator {
            Logger log
          = Logger.getLogger( this .getClass().getName());
            
          public   void  start(BundleContext bc) {
              log.info(
          " started " );
              
          new  BaseService().sayHello();
            }

            
          public   void  stop(BundleContext bc) {
              log.info(
          " stopped. " );
            }
          }

          我們不只是把這些東西放進一個.jar文件,然后就能工作了,真不巧(Spring-OSGi可以派上用場,但是這超出了本文的范圍。)我們還需要以一種特定的文件集和特定的結(jié)構(gòu)來創(chuàng)建tutorialbundle.jar。首先,baselib.jar應(yīng)該放在我們新的jar文件的根目錄。我們還需要一個MANIFEST.MF文件,它包含了一些OSGi配置和啟動信息:

          Manifest-Version:  1.0
          Bundle-ManifestVersion: 
          2
          Bundle-SymbolicName: com.theserverside.tutorial.osgi.TutorialBundle
          Bundle-Version: 
          1
          Bundle-Activator: tutorial.TutorialActivator
          Import-Package: org.osgi.framework
          ; version="1.3.0"
          Bundle-ClassPath: . , baselib.jar

          這個manifest文件假設(shè)相應(yīng)的jar像這樣:

          $ jar tvf tutorialbundle.jar
               
          0  Thu Apr  17   11 : 57 : 14  EDT  2008  META-INF/
             
          391  Thu Apr  17   11 : 57 : 12  EDT  2008  META-INF/MANIFEST.MF
               
          0  Thu Apr  17   11 : 29 : 56  EDT  2008  tutorial/
             
          714  Thu Apr  17   11 : 51 : 02  EDT  2008  tutorial/TutorialActivator.class
             
          902  Thu Apr  17   11 : 15 : 28  EDT  2008  baselib.jar


          這里的要點是我們的manifest文件引入了包(只是一些OSGi框架需要的包)、activator類文件名以及bundle類路徑-以逗號隔開的在jar中的資源集。如果我們能再次重復(fù)實現(xiàn)這個結(jié)構(gòu),說明我們已經(jīng)準備好在Equinox中安全和運行bundle了。

          $ java -jar org.eclipse.osgi_3 .3.2 .R33x_v20080105.jar -console
          osgi> ss

          Framework is launched.

          id      State       Bundle
          0        ACTIVE      org.eclipse.osgi_3 .3.2 .R33x_v20080105

          osgi> install file:///workspaces/osgi/tutorial/tutorialbundle.jar
          Bundle id is 
          4

          osgi> start 
          4
          Apr 
          17 ,   2008   11 : 57 : 29  AM tutorial.TutorialActivator start
          INFO: started
          Apr 
          17 ,   2008   11 : 57 : 29  AM baselib.BaseService sayHello
          INFO: Hello
          ,  world!

          osgi> stop 
          4
          Apr 
          17 ,   2008   1 : 29 : 25  PM tutorial.TutorialActivator stop
          INFO: stopped.

          osgi>


          現(xiàn)在我們知道如何構(gòu)建bundle了,并知道如何進行相關(guān)依賴jar的部署(PS

          后面的資源庫類將依賴XOM。)

           

          構(gòu)建我們的資源庫Bundle

          現(xiàn)在我們可以搞定自己的資源庫bundle了,需要在Activator中添加些功能:把資源庫注冊成服務(wù)并發(fā)布它,所以其他bundle也可以使用我們的資源庫進行查詢。


          package  repository;

          import  java.util.Hashtable;

          import  org.osgi.framework.BundleActivator;
          import  org.osgi.framework.BundleContext;
          import  org.osgi.util.tracker.ServiceTracker;

          import  repository.impl.XMLRepositoryService;

          public   class  Activator  implements  BundleActivator {

                  
          /**
                   * 
          @see  org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
                   
          */
                  
          public   void  start(BundleContext context)  throws  Exception {
                          
          //  register the service
                          context.registerService(
                                          RepositoryService.
          class .getName(),
                                          
          new  XMLRepositoryService(),
                                          
          new  Hashtable < Object,Object > ());

                          
          //  create a tracker and track the log service
                          ServiceTracker repositoryServiceTracker  =
                                  
          new  ServiceTracker(context, RepositoryService. class .getName(),  null );
                          repositoryServiceTracker.open();

                          
          //  grab the service
                          RepositoryService repositoryService  =  (RepositoryService) repositoryServiceTracker.getService();
                          System.err.println(
          " RepositoryService Activated " );
                  }

                  
          /*
                   * (non-Javadoc)
                   * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
                   
          */
                  
          public   void  stop(BundleContext context)  throws  Exception {

                          
          //  close the service tracker
                          System.err.println( " RepositoryService Deactivated " );
                  }
          }

          注意,上面的代碼把XMLRepositoryService硬編碼進去了,有點不爽。我們可以用Spring、或者Service Provider Interface、或者環(huán)境變量、或者—甚至是OSGi青睞的方式,不過這些都超出了本文的范圍。讓我們開始服務(wù)部署,然后我們將揭示如何在其他bundle中調(diào)用它。

           

          根據(jù)我們的第一個范例bundle,我們的資源庫bundle的相關(guān)信息將組成manifest文件和形成目錄結(jié)構(gòu)。下面是目錄結(jié)構(gòu):

          $ jar tvf repositorybundle.jar
               
          0  Thu Apr  17   14 : 08 : 46  EDT  2008  META-INF/
             
          553  Thu Apr  17   14 : 08 : 44  EDT  2008  META-INF/MANIFEST.MF
               
          0  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/
               
          0  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/impl/
            
          1383  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/Activator.class
            
          2095  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/Node.class
             
          205  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/RepositoryService.class
             
          694  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/impl/MapRepositoryService.class
            
          3823  Thu Apr  17   13 : 57 : 12  EDT  2008  repository/impl/XMLRepositoryService.class
          895924  Thu Apr  17   14 : 03 : 40  EDT  2008  xerces- 2.4.0 .jar
          109318  Thu Apr  17   14 : 05 : 08  EDT  2008  xml-apis- 1.0 .b2.jar
          431568  Thu Apr  17   13 : 54 : 06  EDT  2008  xom- 1.1 .jar
          And the manifest file:

          Manifest-Version: 
          1.0
          Bundle-ManifestVersion: 
          2
          Bundle-Name: Repository Plug-in
          Bundle-SymbolicName: repository
          Bundle-Version: 
          1.0.0
          Bundle-Activator: repository.Activator
          Bundle-Vendor: theserverside.com
          Import-Package: org.osgi.framework
          ; version="1.3.0",
           org.osgi.util.tracker ; version="1.3.1"
          Export-Package: repository ; uses:="org.osgi.framework"
          Bundle-ClassPath: . , xom- 1.1 .jar , xerces- 2.4.0 .jar , xml-apis- 1.0 .b2.jar


          我們在干什么呢?我們在創(chuàng)建一個jar,使用我們之前寫好的Activator實現(xiàn)類,以及數(shù)個依賴包:Xerces的實現(xiàn)包XOM,以及ServiceTracker API

           

          我們也干了一件有趣的事情:我們把資源庫bundle暴露出來。這意味著在OSGi容器中的其他的bundle能引入那些bundle,也可以通過詳細的名稱來查詢服務(wù)(這樣的話,資源庫被注冊為“RepositoryService“接口,或者“repository.RepositoryService“。)接下來安裝bundle并啟動它:


          osgi> install file:///workspaces/osgi/tutorial/repositorybundle.jar
          Bundle id is 
          11

          osgi> start 
          11
          RepositoryService Activated

          osgi>

          目前為止一點都不令人興奮,但是我們已經(jīng)在利用OSGi基礎(chǔ)部件工作了。需要注意的是,我們把接口和實現(xiàn)都放進去了。理想情況下,RepositoryService可以放在自己的jar中,所以我們能分離接口和實現(xiàn)。這并不困難,甚至從bundle的觀點看;在activator 中調(diào)用bundle接口,你不需要做任何事情,而在bundle實現(xiàn)中,你應(yīng)該從其他地方導(dǎo)入接口bundle。我們在這里并沒有這樣干,因為這樣做會走很多彎路,相應(yīng)的也會減緩開發(fā)速度。

           

          在其他Bundle中調(diào)用我們的OSGi Bundle

          最后步驟是構(gòu)建例外一個bundle—但它將會找到資源庫服務(wù)并使用之。

           

          讓我們先來看看Bundle Activator。的確很簡單,基本上沒什么功能:當bundle啟動時,它先查找RepositoryService,并在往這個服務(wù)中存入數(shù)據(jù)。它使用stop()機制來實際查找資源庫里面的數(shù)據(jù)并顯示在控制臺上。這不是一個嚴謹?shù)臏y試,但這足以證明流程的行為:

          package  testrepouser;

          import  org.osgi.framework.BundleActivator;
          import  org.osgi.framework.BundleContext;
          import  org.osgi.framework.ServiceReference;

          import  java.util.logging.Logger;

          import  repository.Node;
          import  repository.RepositoryService;

          public   class  SampleActivator  implements  BundleActivator {
                  Logger log
          = Logger.getLogger( this .getClass().getName());

                  
          /*
                   * (non-Javadoc)
                   * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
                   
          */
                  
          public   void  start(BundleContext context)  throws  Exception {
                          ServiceReference ref 
          =  context.getServiceReference(
                          RepositoryService.
          class .getName());
                          RepositoryService lookup 
          =  (RepositoryService) context.getService(ref);
                          Node testNode
          = new  Node( " this is some content " );
                          lookup.put(
          " /foo/bar/baz " , testNode);
                          log.info(
          " /foo/bar/baz stored. " );
                  }

                  
          /*
                   * (non-Javadoc)
                   * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
                   
          */
                  
          public   void  stop(BundleContext context)  throws  Exception {
                          ServiceReference ref 
          =  context.getServiceReference(
                          RepositoryService.
          class .getName());
                          RepositoryService lookup 
          =  (RepositoryService) context.getService(ref);
                          log.info(lookup.get(
          " //*/baz " ));
                  }
          }
          The MANIFEST.MF file looks like 
          this :

          Manifest
          - Version:  1.0
          Bundle
          - ManifestVersion:  2
          Bundle
          - Name: Repository Sample Plug - in
          Bundle
          - SymbolicName: samplerepouser
          Bundle
          - Version:  1.0 . 0
          Bundle
          - Activator: sample.SampleActivator
          Bundle
          - Vendor: theserverside.com
          Import
          - Package: org.osgi.framework;version = " 1.3.0 "
          Require
          - Bundle: repository

          在上面最后一行中。如果你檢查RepositoryServicemanifest,字符名字是“repository.”,在這里我們說bundle應(yīng)該能訪問任何被參考的bundle暴露的類,換句話說,一旦我們的資源庫bundle暴露了“repository”包,我們的SampleActivator就能夠直接導(dǎo)入這個資源庫類而不是自己再用包來組織。

           

          我們構(gòu)建了sample.jar bundle,結(jié)構(gòu)是這樣的:

          $ jar tvf ../samplebundle.jar
               
          0  Thu Apr  17   14 : 47 : 48  EDT  2008  META-INF/
             
          421  Thu Apr  17   14 : 47 : 46  EDT  2008  META-INF/MANIFEST.MF
               
          0  Thu Apr  17   14 : 47 : 48  EDT  2008  sample/
            
          1270  Thu Apr  17   14 : 47 : 48  EDT  2008  sample/SampleActivator.class


          注意一個簡單的地方:這個bunlde除了activator沒什么其他內(nèi)容了。沒有服務(wù)實現(xiàn),甚至接口。

           

          我們安裝并開啟這個bundle,然后再停止,是否和預(yù)期的效果一樣:


          osgi> install file:///workspaces/osgi/tutorial/samplebundle.jar
          Bundle id is 
          17

          osgi> start 
          17
          Apr 
          17 ,   2008   2 : 49 : 07  PM sample.SampleActivator start
          INFO: /foo/bar/baz stored.

          osgi> stop 
          17
          Apr 
          17 ,   2008   2 : 49 : 09  PM sample.SampleActivator stop
          INFO: node: path
          = //foo/bar/baz ,  author = unknown ,  created = Thu Apr  17   14 : 49 : 07  EDT  2008 ,  source = unknown ,  data = [ this is some content ]

          osgi>

          在另一個OSGi容器中運行我們的Bundle

          OSGi 的強大力量之一是容器的“平臺無關(guān)”,就像Java EE模塊能部署到任何兼容的容器中一樣。現(xiàn)在我們花點時間來展示我們之前寫好的bundle部署到Felix上—ApacheOSGi容器,再看看在其他容器上bundle看起來是什么樣子的。Felix首先需要你對當前配置命名,如果你用一樣的名字的話它可以重新載入,在這里的范例,我們將把它叫做“tutorial01

          $ java -jar bin/felix.jar

          Welcome to Felix.
          =================

          Enter profile name: tutorial01

          DEBUG: WIRE: 
          1.0  -> org.ungoverned.osgi.service.shell ->  1.0
          DEBUG: WIRE: 
          1.0  -> org.osgi.service.startlevel ->  0
          DEBUG: WIRE: 
          1.0  -> org.apache.felix.shell ->  1.0
          DEBUG: WIRE: 
          1.0  -> org.osgi.framework ->  0
          DEBUG: WIRE: 
          1.0  -> org.osgi.service.packageadmin ->  0
          DEBUG: WIRE: 
          2.0  -> org.apache.felix.shell ->  1.0
          DEBUG: WIRE: 
          2.0  -> org.osgi.framework ->  0
          DEBUG: WIRE: 
          3.0  -> org.osgi.framework ->  0
          DEBUG: WIRE: 
          3.0  -> org.osgi.service.obr ->  3.0
          DEBUG: WIRE: 
          3.0  -> org.apache.felix.shell ->  1.0
          -> install file:///workspaces/osgi/tutorial/tutorialbundle.jar
          Bundle ID: 
          7
          -> start 
          7
          DEBUG: WIRE: 
          7.0  -> org.osgi.framework ->  0
          Apr 
          18 ,   2008   10 : 46 : 37  AM tutorial.TutorialActivator start
          INFO: started
          Apr 
          18 ,   2008   10 : 46 : 37  AM baselib.BaseService sayHello
          INFO: Hello
          ,  world!
          -> install file:///workspaces/osgi/tutorial/repositorybundle.jar
          Bundle ID: 
          8
          -> start 
          8
          DEBUG: WIRE: 
          8.0  -> org.osgi.util.tracker ->  0
          DEBUG: WIRE: 
          8.0  -> org.osgi.framework ->  0
          RepositoryService Activated
          -> install file:///workspaces/osgi/tutorial/samplebundle.jar
          Bundle ID: 
          9
          -> start 
          9
          DEBUG: WIRE: 
          9.0  -> org.osgi.framework ->  0
          DEBUG: WIRE: 
          9.0  -> module ; bundle-symbolic-name="repository";bundle-version="1.0.0" -> 8.0
          Apr  18 ,   2008   10 : 47 : 08  AM sample.SampleActivator start
          INFO: /foo/bar/baz stored.
          -> stop 
          9
          Apr 
          18 ,   2008   10 : 47 : 09  AM sample.SampleActivator stop
          INFO: node: path
          = //foo/bar/baz ,  author = unknown ,  created = Fri Apr  18   10 : 47 : 07  EDT  2008 ,  source = unknown ,  data = [ this is some content ]
          -> shutdown
          -> RepositoryService Deactivated
          Apr 
          18 ,   2008   10 : 47 : 12  AM tutorial.TutorialActivator stop
          INFO: stopped.


          一個實際的應(yīng)用程序,類似IRC Bot

          非常清晰的看到資源庫范例是如何運行的—但是測試是沒什么樂趣的。讓我們再把這個測試更進一步,引入一個IRC bot。我們的IRC bot將使用PircBot,因為學(xué)習(xí)它的API沒什么難度,IRC bot將加入某個IRC網(wǎng)絡(luò)(irc.freenode.net"#pircbot"頻道)的頻道,將響應(yīng)兩個外部命令:~set~get~set將獲取一個路徑和一些文字,并把文字加入到路徑中;而~get將從路徑中獲取信息。同時,這個例子極其簡單并且也不能達到infobot的水平;那就把這個例子留下來給讀者練習(xí),并賦予它更多功能吧。

           

          首先要做的事情是建立一個查詢服務(wù)的通用方式。的確還沒有最好的辦法實現(xiàn)!有多種不同模式可選;現(xiàn)在有個簡單而不是最好的方式來快速展開。我們先創(chuàng)建ServiceLookup接口,接著為OSGi實現(xiàn)ServiceLookup接口。


          package  service;

          public   interface  ServiceLookup {
              Object getService(String name);
          }

          package  service.osgi;

          import  org.osgi.framework.BundleContext;
          import  org.osgi.framework.ServiceReference;
          import  service.ServiceLookup;

          public   class  OSGIServiceLookupImpl  implements  ServiceLookup {
              BundleContext ctx;

              
          public  OSGIServiceLookupImpl(BundleContext ctx) {
                  
          this .ctx  =  ctx;
              }

              
          public  Object getService(String name) {
                  ServiceReference ref 
          =  ctx.getServiceReference(name);
                  
          return  ctx.getService(ref);
              }
          }

          創(chuàng)建OSGIServiceLookupImpl是很簡單的,只是它的構(gòu)造函數(shù)傳入了Activator BundleContext

          package  ircbot;

          import  org.jibble.pircbot.IrcException;
          import  org.jibble.pircbot.NickAlreadyInUseException;
          import  org.osgi.framework.BundleActivator;
          import  org.osgi.framework.BundleContext;
          import  service.osgi.OSGIServiceLookupImpl;

          import  java.io.IOException;

          public   class  BotActivator  implements  BundleActivator {
              IRCBot bot 
          =   null ;

              
          public   void  start( final  BundleContext context)  throws  Exception {
                  
          try  {
                      bot 
          =   new  IRCBot( new  OSGIServiceLookupImpl(context));
                      bot.setVerbose(
          true );
                      bot.connect(
          " irc.freenode.net " );
                      bot.joinChannel(
          " #pircbot " );
                  } 
          catch  (NickAlreadyInUseException e) {
                      e.printStackTrace();
                  } 
          catch  (IOException e) {
                      e.printStackTrace();
                  } 
          catch  (IrcException e) {
                      e.printStackTrace();
                  }
              }


              
          public   void  stop(BundleContext context)  throws  Exception {
                  bot.disconnect();
                  bot.dispose();
              }
          }


          下面的代碼全是在創(chuàng)建IRCBot實例,要用到ServiceLookup實現(xiàn):

          package  ircbot;

          import  org.jibble.pircbot.PircBot;
          import  service.ServiceLookup;
          import  repository.RepositoryService;
          import  repository.Node;

          public   class  IRCBot  extends  PircBot {
              ServiceLookup service;

              
          public  IRCBot(ServiceLookup service) {
                  
          super ();
                  
          this .service = service;
                  setName(
          " OSGIBot " );
              }

              @Override
              
          protected   void  onMessage(String channel, String sender, String login, String hostname, String message) {
                  String[] command
          = message.split( "   " );
                  
          if (command.length > 1   &&  ( " ~set " .equals(command[ 0 ])  ||   " ~get " .equals(command[ 0 ]))) {
                      String path
          = command[ 1 ];

                      
          //  we should use a tracker for this!
                      RepositoryService repository =  (RepositoryService) service.getService(RepositoryService. class .getName());
                      
          if ( " ~set " .equals(command[ 0 ])) {
                          StringBuilder content
          = new  StringBuilder();
                          
          for ( int  i = 2 ;i < command.length;i ++ ) {
                              content.append(
          "   " );
                              content.append(command[i]);
                          }
                          Node node
          = repository.get(path);
                          
          if (node == null ) {
                              node
          = new  Node();
                              node.setAuthor(sender);
                              node.setSource(
          " irc " );
                          }
                          node.getContents().add(content.toString().trim());
                          repository.put(path, node);
                      }
                      
          if ( " ~get " .equals(command[ 0 ])) {
                          Node node
          = repository.get(path);
                          
          if (node != null ) {
                              
          int  count = 0 //  will only do two at most, to be polite
                               for (String content:node.getContents()) {
                                  
          if (count ++> 2 ) {
                                      
          break ;
                                  }
                                  sendMessage(channel, sender 
          +   " " + content);
                              }
                          }
                      }
                  }
              }
          }


          最后,我們的MANIFEST.MF文件指明了類路徑(包含在pircbot.jar中),以及activator名字(“ircbot.BotActivator”),以及對“repository”的外部依賴:

          Manifest-Version:  1.0
          Bundle-ManifestVersion: 
          2
          Bundle-Name: IRCBot Plug-in
          Bundle-SymbolicName: ircbot
          Bundle-Version: 
          1.0.0
          Bundle-Activator: ircbot.BotActivator
          Bundle-Vendor: theserverside.com
          Import-Package: org.osgi.framework
          ; version="1.3.0"
          Require-Bundle: repository
          Bundle-ClassPath: pircbot.jar
          , .


          安裝并開啟這個bundle(廢話多),它將連接到Freenode并加入#pircbot。注意這里沒有對昵稱沖突進行處理;你可以自己寫點代碼搞定,或者修改默認的昵稱。這不是一些好代碼,人人都可以放任何東西進去,經(jīng)不住測試

           

          這里并不是說只有IRCBot才能使用資源庫。理論上,一個jabber客戶端也可以用相同的資源庫(可以用相似的代碼來處理。)事實上,這就是OSGi的亮點: IRCBot中處理命令的代碼能在一個bundle內(nèi)部獨立運行,并且如果需要的話,IRCBot也能很容易的調(diào)用適當?shù)?span lang="EN-US">bundle管理命令,一旦要求訪問資源庫,它們能立刻查找資源庫服務(wù)。

           

          結(jié)論

          希望,本文能啟發(fā)讀者去探求OSGi的潛能,并能讓讀者知道如何開始使用它。

          posted on 2008-06-25 19:45 CONAN 閱讀(1116) 評論(0)  編輯  收藏 所屬分類: J2EE
          主站蜘蛛池模板: 千阳县| 文水县| 荥阳市| 桑日县| 贵州省| 宿松县| 堆龙德庆县| 徐闻县| 大港区| 绥棱县| 乐安县| 津市市| 宣恩县| 三门峡市| 金昌市| 洛隆县| 贵德县| 柳林县| 阿合奇县| 二手房| 康定县| 通江县| 莱阳市| 怀远县| 西吉县| 色达县| 金华市| 昌宁县| 平和县| 洛扎县| 康平县| 靖西县| 新源县| 五莲县| 中宁县| 景宁| 鄂伦春自治旗| 邻水| 达孜县| 天峻县| 桃园市|