<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    CONAN ZONE

    你越掙扎我就越興奮

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks

    一年一度的Ig Nobel prize典禮都都會帶來一些非常新穎的觀點、發現,這些內容甚至超過了Ig Nobel prizes本身。每位獲獎者在做完七個字的總結后,還有機會利用24秒的時間對自己的新觀點、新發現進行闡述。

     

    這是一個極其絕妙的主意,這對每位獲獎者都是一個需要完成的挑戰。

     

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

     

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

     

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

     

    OSGi 的七字總結和24秒闡述

    OSGi 是一個為Java而設計的組件框架。

     

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

     

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

     

    為什么需要模塊化系統?

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

     

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

     

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

     

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

     

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

     

    開始OSGi

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

     

    Equinox 是一個OSGi容器,你可以從http://download.eclipse.org/eclipse/equinox/下載它。下載的文件是ZIP格式,解壓縮到“eclipse”目錄,不必驚訝:EquinoxEclipse內置的OSGi容器。(我將把包含所有Equinox發布的頂級目錄“/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很簡單:在這個目錄中,執行java -jar org.eclipse.osgi_3.3.2.R33x_v20080105.jar –console(在控制臺窗口),然后你將看到一個命令提示符:

    osgi>


    這就是EquinoxOSGi控制臺。從這里開始,你可以安裝新的bundle并啟動、停止、卸載他們,檢查他們的依賴、注冊服務,或者其他東西。你嘗試到的第一個命令將是“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有什么優點呢?bundle提供了生命周期和服務暴露,就像我們在24秒的總結中提到一樣。

     

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

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

    package  repository;

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

    }

    不是太多,這是一個開始:我可以利用它來存放或提取信息。我的節點類是一個簡單的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.
    }

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

    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不能很好的實現查詢功能,而且很有可能遇到有層次結構的信息。我想用DOM來代替它。所以我現在將引入XOM,并重新實現資源庫服務:

    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'] " ));
            }
    }

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

     

    轉移目標:根據依賴關系構造OSGi bundle

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

     

    讓我們創建一個簡單的模塊,首先,最簡單的它應該能在啟動和結束的時候顯示消息,作為內部依賴將引入一個.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

     

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

    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可以派上用場,但是這超出了本文的范圍。)我們還需要以一種特定的文件集和特定的結構來創建tutorialbundle.jar。首先,baselib.jar應該放在我們新的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文件假設相應的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中的資源集。如果我們能再次重復實現這個結構,說明我們已經準備好在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>


    現在我們知道如何構建bundle了,并知道如何進行相關依賴jar的部署(PS

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

     

    構建我們的資源庫Bundle

    現在我們可以搞定自己的資源庫bundle了,需要在Activator中添加些功能:把資源庫注冊成服務并發布它,所以其他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、或者環境變量、或者—甚至是OSGi青睞的方式,不過這些都超出了本文的范圍。讓我們開始服務部署,然后我們將揭示如何在其他bundle中調用它。

     

    根據我們的第一個范例bundle,我們的資源庫bundle的相關信息將組成manifest文件和形成目錄結構。下面是目錄結構:

    $ 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


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

     

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


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

    osgi> start 
    11
    RepositoryService Activated

    osgi>

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

     

    在其他Bundle中調用我們的OSGi Bundle

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

     

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

    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應該能訪問任何被參考的bundle暴露的類,換句話說,一旦我們的資源庫bundle暴露了“repository”包,我們的SampleActivator就能夠直接導入這個資源庫類而不是自己再用包來組織。

     

    我們構建了sample.jar bundle,結構是這樣的:

    $ 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沒什么其他內容了。沒有服務實現,甚至接口。

     

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


    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 的強大力量之一是容器的“平臺無關”,就像Java EE模塊能部署到任何兼容的容器中一樣。現在我們花點時間來展示我們之前寫好的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.


    一個實際的應用程序,類似IRC Bot

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

     

    首先要做的事情是建立一個查詢服務的通用方式。的確還沒有最好的辦法實現!有多種不同模式可選;現在有個簡單而不是最好的方式來快速展開。我們先創建ServiceLookup接口,接著為OSGi實現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);
        }
    }

    創建OSGIServiceLookupImpl是很簡單的,只是它的構造函數傳入了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();
        }
    }


    下面的代碼全是在創建IRCBot實例,要用到ServiceLookup實現:

    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。注意這里沒有對昵稱沖突進行處理;你可以自己寫點代碼搞定,或者修改默認的昵稱。這不是一些好代碼,人人都可以放任何東西進去,經不住測試

     

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

     

    結論

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

    posted on 2008-06-25 19:45 CONAN 閱讀(1114) 評論(0)  編輯  收藏 所屬分類: J2EE
    主站蜘蛛池模板: 亚洲AV成人无码久久WWW| 亚洲精品无码日韩国产不卡av| 亚洲精品国偷自产在线| 国产精成人品日日拍夜夜免费| 亚洲网红精品大秀在线观看| 成年人免费视频观看| 久久精品国产亚洲av瑜伽| 亚洲成a人片在线观看无码专区| 久草视频免费在线| 一级毛片免费在线| 亚洲一区二区三区久久| 亚洲一区二区三区在线视频| 18女人水真多免费高清毛片| 亚洲成在人天堂在线| 日韩免费福利视频| 日本卡1卡2卡三卡免费| WWW亚洲色大成网络.COM | 亚洲欧美成人av在线观看| 色噜噜AV亚洲色一区二区| 免费看成人AA片无码视频羞羞网| 国产亚洲精品美女2020久久| 亚洲色av性色在线观无码| 亚洲成年人啊啊aa在线观看| 亚洲国产成人AV网站| 亚洲一区中文字幕久久| 波多野结衣免费视频观看| 久久www免费人成看片| jizz免费在线观看| 国产成人无码综合亚洲日韩| 日韩一品在线播放视频一品免费| 一区二区三区四区免费视频| 美女被爆羞羞网站在免费观看| 亚洲图片校园春色| 曰皮全部过程视频免费国产30分钟 | 亚洲午夜国产精品| 亚洲一区二区三区香蕉| 又粗又硬又大又爽免费视频播放| 亚洲黄色免费在线观看| 国产精品免费大片| 91成人免费福利网站在线| 日韩精品无码免费视频|