• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 319, comments - 22, trackbacks - 0, articles - 11
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            OSGi開發(fā)起步

            Posted on 2012-05-15 17:07 RTY 閱讀(3129) 評論(1)  編輯 收藏 引用 所屬分類: 編程常識

            OSGi開發(fā)起步(Getting Started with OSGi)-1第一個OSGi模塊
            原文出自: http://neilbartlett.name/blog/osgi-articles/ - Getting Started with OSGi

            http://hi.baidu.com/tinawhisper/home

            本文系翻譯文章,但并未嚴格按照原文翻譯。

            1. 第一個OSGi模塊
            本文期望展示OSGi開發(fā)環(huán)境的簡潔性,因此這里沒有使用eclipse開發(fā)環(huán)境。本文只使用文本編輯器和基本的命令行工具進行OSGi程序開發(fā)。
            本文的第一個例子相對其他示例有些長,這是因為我們需要建立一個基本的工作環(huán)境。在開始之前,我們需要一個可以運行的OSGi框架。現(xiàn)在我們可以在三種開源框架中進行選擇:Apache Felix,Knopflerfish和Equinox。本文所有編寫的代碼對以上三個框架來說都是可用的,只是在使得這些代碼運行的指令上有所區(qū)別。本文選擇eclipse使用的Equinox。首先需要找到eclipse安裝目錄下的org.eclipse.osgi_3.2.1.R32x_v20060919.jar并復(fù)制此文件到一個空目錄(文件中的版本號會根據(jù)安裝eclipse的不同而不同)。為了使命令行參數(shù)短一些,把剛才復(fù)制過來的jar文件更名為equinox.jar。現(xiàn)在就可以在命令行中定位到剛才那個空目錄并執(zhí)行這個命令:
            > java -jar equinox.jar -console
            幾秒鐘后,命令窗口中會出現(xiàn)osgi>提示。如果是這樣,就說明OSGi已經(jīng)運行起來了(意外總會發(fā)生,比如你沒有安裝java!)。
            osgi>命令行提示允許我們運行Equinox中的命令來進行整個框架的控制。可以在提示符后輸入help獲得所有命令的列表。經(jīng)常使用的一個命令是ss。ss表示簡要狀態(tài)(short status)的意思。ss會列出當前已經(jīng)安裝到框架中所有模塊(bundle)和它們狀態(tài)的列表。在OSGi中bundle表示一個模塊,這與eclipse中的插件(plug-ins)具有等同的概念。
            輸入ss后,Equinox輸出為:
            Framework is launched.
            id State Bundle
            0 ACTIVE system.bundle_3.2.1.R32x_v20060919
            以上輸出告訴我們當前只安裝了一個已經(jīng)啟動的模塊:系統(tǒng)模塊(System bundle)。此模塊是OSGi的一個總是安裝并處于活動狀態(tài)的特殊模塊,它代表了框架本身。
            現(xiàn)在我們開始編寫一個模塊。在相同的目錄下,創(chuàng)建一個名稱為HelloActivator.java的文件并復(fù)制以下代碼到文件中:
            import org.osgi.framework.*;
            public class HelloActivator implements BundleActivator {
            public void start(BundleContext context) { System.out.println("Hello Eclipse!"); }
            public void stop(BundleContext context) { System.out.println("Goodbye Eclipse!"); }
            }
            一個模塊還需要一個元文件(manifest file)對它的各種元信息進行聲明:如模塊的名稱,版本等信息。所以現(xiàn)在還需要創(chuàng)建一個名稱為HelloWorld.mf的文件并復(fù)制下文的文本到此文件中。特別需要注意的是要在此文本文件的最后保留一個空白行,否則打包命令jar在打包時會截短文件(最后一行丟失)。這樣就會使得生產(chǎn)的jar包中的元文件缺失一行,導(dǎo)致jar文件讀取和運行失敗。
            Manifest-Version: 1.0
            Bundle-Name: HelloWorld
            Bundle-Activator: HelloActivator
            Bundle-SymbolicName: HelloWorld
            Bundle-Version: 1.0.0
            Import-Package: org.osgi.framework
            現(xiàn)在另外打開一個命令行窗口使用如下命令構(gòu)建一個jar文件:
            > javac -classpath equinox.jar HelloActivator.java
            > jar -cfm HelloWorld.jar HelloWorld.mf HelloActivator.class
            回到原先的OSGi控制臺,輸入install file:HelloWorld.jar。控制臺會輸出"Bundle id is 1" ,接著輸入ss命令就可以得到如下輸出信息:
            Framework is launched.
            id State Bundle
            0 ACTIVE system.bundle_3.2.1.R32x_v20060919
            1 INSTALLED HelloWorld_1.0.0
            剛才開發(fā)的HelloWorld模塊安裝到了OSGi框架中,但此模塊還沒有運行起來。現(xiàn)在輸入命令start 1。命令中的1是安裝HelloWorld模塊完成后,OSGi賦值給此模塊的ID號。當運行啟動命令后,控制臺顯示消息"Hello Eclipse!"。接著輸入stop 1就會得到消息"Goodbye Eclipse!"。
            這些命令都做了些什么事情?上面的代碼實現(xiàn)了一個BundleActivator接口,這個接口用于允許框架通知我們一些重要的生命期事件。當模塊啟動時,框架調(diào)用start方法,而如果模塊停止時,框架則調(diào)用stop方法。還有一個要說明的是元文件中有一行聲明"Bundle-Activator: HelloActivator"就是用于通知框架在一個模塊中那個類是啟動類(activator)。通常我們使用一個完整的類名稱,但在此示例中為了簡單而只使用的缺省包名。
            術(shù)語表:
            Bundle: 模塊
            Activator:啟動器
            Component:組件
            Service:服務(wù)
            Sevice Register:服務(wù)注冊表
            Declarative Service: 服務(wù)聲明,簡稱DS
            OSGi Framework: OSGi框架或者框架
            manifest file: 元文件

            =========================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-2與OSGi框架進行交互
            原文出自: http://neilbartlett.name/blog/osgi-articles/ - Getting Started with OSGi

            本文系翻譯文章,但并未嚴格按照原文翻譯。

            2. 與OSGi框架進行交互

            上一節(jié)舉了一個簡單的示例模塊HelloWorld:啟動和停止時輸出消息的模塊。該模塊通過實現(xiàn)BundleActivator接口定義的start和stop函數(shù)方法完成了這些消息的輸出。如果回過頭來仔細的研究start和stop方法的定義,我們可以發(fā)現(xiàn)函數(shù)定義了一個BundleContext類型的參數(shù)。在本節(jié)描述參數(shù)BundleContext以及它的用法。
            BundleContext是OSGi框架提供給一個模塊的'通行票據(jù)'。當模塊需要與框架進行交互和通信時,就可以使用BundleContext。實際上,這也是一個模塊可以唯一與OSGi框架進行交互的渠道。OSGi框架在每個模塊啟動時會通過此模塊的BundleActivator派送一個票據(jù):BundleContext。
            如果上一節(jié)的OSGi控制臺還在運行狀態(tài),下面的工作就不需要重啟控制臺。如果控制臺關(guān)閉了就需要使用下面的命令啟動它。
            > java -jar equinox.jar -console
            輸入ss后就會發(fā)現(xiàn)上次安裝過的HelloWorld模塊。即便是OSGi框架關(guān)閉重啟了之后,HelloWorld模塊也會保持上節(jié)的狀態(tài)。這主要是因為OSGi框架進行了模塊的持久化存儲處理。
            在這一節(jié)我們完成一個查找并卸載HelloWorld模塊的模塊。在OSGi控制臺可以使用uninstall命令簡單的完成這項任務(wù)。但是這一節(jié)希望通過OSGiAPI編碼的方式實現(xiàn)此任務(wù)。
            創(chuàng)建一個名稱為HelloWorldKiller.java的文件,輸入下面的代碼:
            import org.osgi.framework.*;
            public class HelloWorldKiller implements BundleActivator {
            public void start(BundleContext context) {
            System.out.println("HelloWorldKiller searching...");
            Bundle[] bundles = context.getBundles();
            for(int i=0; i<bundles.length; i++) {
            if("HelloWorld".equals(bundles[i]
            .getSymbolicName())) {
            try { System.out.println("Hello World found, " + "destroying!"); bundles[i].uninstall(); return; } catch (BundleException e) { System.err.println("Failed: " + e.getMessage()); }
            }
            }
            System.out.println("Hello World bundle not found");
            }
            public void stop(BundleContext context) { System.out.println("HelloWorldKiller shutting down"); }
            }
            接著創(chuàng)建此模塊的元文件,注意文件最后要留一個空白行。在元文件中輸入以下內(nèi)容:
            Manifest-Version: 1.0
            Bundle-Name: HelloWorldKiller
            Bundle-Activator: HelloWorldKiller
            Bundle-SymbolicName: HelloWorldKiller
            Bundle-Version: 1.0.0
            Import-Package: org.osgi.framework
            編譯和構(gòu)建模塊對應(yīng)的jar文件:
            > javac -classpath equinox.jar HelloWorldKiller.java
            >jar -cfm HelloWorldKiller.jar HelloWorldKiller.mf HelloWorldKiller.class
            在控制臺安裝構(gòu)建完成的新模塊:install file:HelloWorldKiller.jar。然后輸入ss。模塊的狀態(tài)類似以下列表:
            id State Bundle
            0 ACTIVE system.bundle_3.2.1.R32x_v20060919
            1 ACTIVE HelloWorld_1.0.0
            2 INSTALLED HelloWorldKiller_1.0.0
            接著啟動我們的新模塊HelloWorldKiller:start 2。其輸出信息為:
            HelloWorldKiller searching...
            Hello World found, destroying!
            Goodbye Eclipse!
            注意最后一行是先前那個HelloWorld模塊的輸出信息。當這個模塊處于運行狀態(tài)并且啟動HelloWorldKiller時,HelloWorld模塊被停止并進行了卸載。這種操作觸發(fā)了HelloWorld模塊啟動器的stop方法。
            輸入命令ss就會發(fā)現(xiàn)HelloWorld模塊消失了。
            id State Bundle
            0 ACTIVE system.bundle_3.2.1.R32x_v20060919
            2 ACTIVE HelloWorldKiller_1.0.0
            這里的問題是:安全性如何保證?好像任何模塊都可以卸載其他模塊。實際上OSGi定義了一個復(fù)雜的安全層次機制。這個安全機制提供了對模塊與框架交互的精確控制。這樣就可以把卸載模塊的權(quán)限限制在某些特定的管理模塊。重要的是OSGi進行安全控制基本上是一個配置過程,所以在這個講解系列里我們關(guān)注點主要放在編碼上。
            在開始下節(jié)講解之前,我們可以了解一下BundleContext接口,看看此接口提供了哪些方法可供使用。比如,我們可以使用installBundle方法安裝一個模塊到OSGi中。或者也可以獲取當前所有安裝的模塊列表并打印這些模塊最近一次修改的日期和時間。具體的方法可以參考OSGi R4的API javadoc .
            術(shù)語表:
            Bundle: 模塊
            Activator:啟動器
            Component:組件
            Service:服務(wù)
            Sevice Register:服務(wù)注冊表
            Declarative Service: 服務(wù)聲明,簡稱DS
            OSGi Framework: OSGi框架或者框架
            manifest file: 元文件

            =========================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-3 模塊間依賴
            3. 模塊間依賴
            上兩節(jié)展示了模塊的啟動和停止以及模塊與系統(tǒng)框架的交互方式。但是問題是模塊真正的用途是什么呢?
            模塊是功能集合。模塊使我們能夠把龐大紛雜的項目劃分成可管理的單元。這些單元可以方便的載入到OSGi運行環(huán)境。問題是,不管我們喜歡還是不喜歡,模塊幾乎總是依賴于其他模塊。對jar文件來說,從來沒有一個可靠的方式來指定不同jar之間的依賴關(guān)系(注意,jar元文件中的類路徑也不是一個可靠的方法)。因此你永遠不能真正的確定: Jar中的代碼會正常工作?還是會在運行時失控拋出一個ClassNotFoundException異常?
            OSGi以簡潔的方式解決了這個問題。僅僅這樣說還不如通過功能展示來的更有說服力些。所以讓我們馬上開始編碼來做到這點。
            不幸的是直至現(xiàn)在,我們一直使用默認包的方式進行類的開發(fā)。這種方式不適應(yīng)更復(fù)雜問題的編碼,所以現(xiàn)在需要使用java包的形式進行編碼開發(fā)和功能劃分。首先以一個非常簡單的JavaBean類開始,復(fù)制以下代碼到文件osgitut /movies/ Movie.java :
            package osgitut.movies;
            public class Movie {
            private final String title;
            private final String director;
            public Movie(String title, String director) { this.title = title; this.director = director; }
            public String getTitle() { return title; }
            public String getDirector() { return director; }
            }
            現(xiàn)在接著在此包內(nèi)創(chuàng)建一個接口。復(fù)制以下代碼到文件osgitut /movies/ MovieFinder.java :
            package osgitut.movies;
            public interface MovieFinder { Movie[] findAll(); }
            然后把這兩個類封裝為一個模塊。當然,這個模塊十分簡單而且用途也不大。但是對于本節(jié)探討的內(nèi)容已經(jīng)足夠了。同前面一樣,模塊還必要有一個對應(yīng)的元文件MoviesInterface.mf:
            Manifest-Version: 1.0
            Bundle-ManifestVersion: 2
            Bundle-Name: Movies Interface
            Bundle-SymbolicName: MoviesInterface
            Bundle-Version: 1.0.0
            Export-Package: osgitut.movies;version="1.0.0"
            文件中多了一行:Export-Package。這個屬性表明包osgitut.movies從這個模塊中導(dǎo)出。而問題是java的jar文件中所有的東西都是導(dǎo)出的。但是我們難道不想只導(dǎo)出包中的部分代碼而保持其他部分只在包內(nèi)部可見嗎?我們當然可以使用private或者protected限制類的訪問范圍,而這樣一來同一個jar中其他包也不能訪問這些類了。所以O(shè)SGi有效地引進了一種新的代碼保護級別:如果模塊中的包名沒有在Export-Package頭中列出,那么此包就只能在這個模塊中才能訪問到。
            此外,我們還在導(dǎo)出包名稱后掛接了軟件版本號。在下一個階段的講解中我們會發(fā)現(xiàn)這十分重要。當然,這也不是絕對有必要提供一個版本。但如果你不指定版本號時,OSGi將自動為導(dǎo)出的包指定版本" 0.0.0 "。始終為導(dǎo)出的包指定明確的版本是一個優(yōu)秀的實踐經(jīng)驗。
            現(xiàn)在構(gòu)建此模塊:
            > javac osgitut/movies/Movie.java osgitut/movies/MovieFinder.java
            > jar -cfm MoviesInterface.jar MoviesInterface.mf osgitut/movies/*.class
            完成此項工作后我們不要急于安裝此模塊。在這之前我們還需要構(gòu)建此模塊的一個依賴模塊。即一個實現(xiàn)了MovieFinder接口的具體實現(xiàn)類:osgitut/movies/impl/BasicMovieFinderImpl.java :
            package osgitut.movies.impl;
            import osgitut.movies.*;
            public class BasicMovieFinderImpl implements MovieFinder {
            private static final Movie[] MOVIES = new Movie[] { new Movie("The Godfather", "Francis Ford Coppola"), new Movie("Spirited Away", "Hayao Miyazaki") };
            public Movie[] findAll() { return MOVIES; }
            }
            還需要一個元文件:BasicMovieFinder.mf。
            Manifest-Version: 1.0
            Bundle-ManifestVersion: 2
            Bundle-Name: Basic Movie Finder
            Bundle-SymbolicName: BasicMovieFinder
            Bundle-Version: 1.0.0
            Import-Package: osgitut.movies;version="[1.0.0,2.0.0)"
            注意,在這里我們導(dǎo)入了一個由其他模塊導(dǎo)出的包osgitut.movies。而且還同時為此導(dǎo)入包設(shè)置了版本范圍。框架在運行時刻使用此版本范圍匹配一個適合的導(dǎo)出包。OSGi表達版本范圍使用的是通用的數(shù)學(xué)式語法:方括號表示包含,圓括號表示排除。在上面這個示例元文件中我們使用這個語法有效的指定了一個1.x的版本。
            對本節(jié)示例來說,在導(dǎo)入部分加入版本限制并不是必需的。但這是一個通用的實踐準則。
            現(xiàn)在按照以下命令構(gòu)建第二個模塊:
            > javac -classpath MoviesInterface.jar osgitut/movies/impl/BasicMovieFinderImpl.java
            > jar -cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class
            最后我們可以準備在OSGi環(huán)境中試驗這兩個模塊。第一步就是安裝BasicMovieFinder模塊并運行ss命令。這時此模塊的狀態(tài)顯示為INSTALLED。
            id State Bundle
            0 ACTIVE org.eclipse.osgi_3.3.0.v20070208
            4 INSTALLED BasicMovieFinder_1.0.0
            (說明,你的模塊列表可能與上面這個示例列表存在一定的差異。實際上,模塊id號會根據(jù)你上次安裝和卸載HelloWorld模塊的次數(shù)而確定。因此下面示例中的id號要根據(jù)你運行環(huán)境中相應(yīng)的id進行替換。)
            INSTALLED表示框架載入了這個模塊,但還沒有完成此模塊依賴關(guān)系的解析。一個讓框架執(zhí)行模塊依賴解析的命令是refresh。這里輸入refresh 4然后是ss命令就可以看到:
            id State Bundle
            0 ACTIVE org.eclipse.osgi_3.3.0.v20070208
            4 INSTALLED BasicMovieFinder_1.0.0
            這個模塊沒有解析成功并仍然處于INSTALLED狀態(tài)。這個問題出在我們并沒有安裝其依賴的那個包含Movie類和MovieFinder接口的模塊。如果想確認是不是這個問題,在框架控制臺輸入diag 4以獲取此模塊的調(diào)試信息:
            file:BasicMovieFinder.jar [4]
            Missing imported package osgitut.movies_[1.0.0,2.0.0).
            看來確實是因為框架運行環(huán)境沒有任何可供導(dǎo)出的osgitut.movies包。那么現(xiàn)在就安裝MoviesInterface.jar模塊并運行ss。大致的輸出結(jié)果為:
            id State Bundle
            0 ACTIVE org.eclipse.osgi_3.3.0.v20070208
            4 INSTALLED BasicMovieFinder_1.0.0
            5 INSTALLED MoviesInterface_1.0.0
            最后一步就是通知框架重新執(zhí)行BasicMovieFinder模塊的依賴解析。依舊使用refresh 4命令。輸出的結(jié)果為:
            id State Bundle
            0 ACTIVE org.eclipse.osgi_3.3.0.v20070208
            4 RESOLVED BasicMovieFinder_1.0.0
            5 RESOLVED MoviesInterface_1.0.0
            BasicMovieFinder模塊現(xiàn)在處于已解析狀態(tài)。這是一個基本步驟。因為只有處于已解析的模塊才能啟動。
            需要注意的是通常情況下并不需要使用本節(jié)展示的手工方式進行模塊依賴解析。一般來說模塊會在它們需要的時候自動進行解析。例如,我們可以注意到MoviesInterface模塊也處理已解析狀態(tài),而我們并沒有針對此模塊顯示的執(zhí)行refresh命令。
            下一節(jié)我們進行OSGi服務(wù)開發(fā)。

            =======================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-4注冊一個服務(wù)
            4 注冊一個服務(wù)
            我們終于可以開始OSGi服務(wù)的講解了。在我看來,服務(wù)層是OSGi中最具特點的部分。在接下來的幾節(jié)你就會充分體會到這一點。
            上節(jié)講解的是MovieFinder接口的例子。這個接口被MovieLister用來查找電影。這個例子是一個Ioc(控制反轉(zhuǎn))的例子。
            MovieLister并不太關(guān)心原始電影數(shù)據(jù)的來源,所以我們使用了MovieFinder接口隱藏了這些細節(jié)。關(guān)鍵在于我們可以使用另外一種實現(xiàn)替代現(xiàn)有的MovieFinder:比如從數(shù)據(jù)庫或者亞馬遜web服務(wù)進行電影查詢。而MovieLister只依賴與接口而不是具體的實現(xiàn)。
            雖然不錯,但是有些時候我們實際上還是要把一個MovieFinder的具體實現(xiàn)明確指定給MovieLister。我們是用一個外部容器把這個具體實現(xiàn)"推"給了MovieLister,而不是讓MovieLister自己調(diào)用一個查找方法發(fā)現(xiàn)這個具體實現(xiàn)對象。
            這就是IoC術(shù)語的由來。現(xiàn)在存在大量的這類外部容器,如PicoContainer,HiveMind,Spring以及EJB3.0。但是直至現(xiàn)在這些容器都有一個基本限制:它們都是靜態(tài)的。一旦一個MovieFinder指定給了MovieLister,兩者在整個JVM虛擬機生命周期內(nèi)都一直關(guān)聯(lián)。
            OSGi支持具有動態(tài)特性的IoC模式。OSGi支持動態(tài)的提供給MovieLister一個MovieFinder的實現(xiàn)然后移除它們。這樣我們就能在一個支持文本電影數(shù)據(jù)查詢的應(yīng)用程序和一個依賴亞馬遜web服務(wù)電影數(shù)據(jù)查詢的應(yīng)用程序之間進行熱切換。
            OSGi服務(wù)層用于支持以上特性。我們要做的只是在服務(wù)注冊表中注冊MovieFinder為其中的一個服務(wù)就能支持熱切換特性。這可是相當簡單啊。同樣的MovieLister的功能也可以由一個MovieLister服務(wù)提供。一個服務(wù)與一個普通的java對象(POJO)沒有什么不同,它只是使用java接口名進行了服務(wù)注冊而已。
            在本節(jié)我們就看看如何在注冊表中注冊服務(wù)。接下來就試試怎樣編碼從注冊表獲取服務(wù)并提供給一個MovieLister。
            在上節(jié)代碼基礎(chǔ)上增加一個BasicMovieFinder。不需要更改任何現(xiàn)有的類,只是在增加一個啟動器類。下面就是這個啟動類osgitut/movies/impl/BasicMovieFinderActivator.java:
            package osgitut.movies.impl;
            import org.osgi.framework.*;
            import osgitut.movies.*;
            import java.util.Properties;
            import java.util.Dictionary;
            public class BasicMovieFinderActivator implements BundleActivator {
            private ServiceRegistration registration;
            public void start(BundleContext context) { MovieFinder finder = new BasicMovieFinderImpl(); Dictionary props = new Properties(); props.put("category", "misc"); registration = context.registerService( MovieFinder.class.getName(), finder, props); }
            public void stop(BundleContext context) { registration.unregister(); }
            }
            接下來就是修改BasicMovieFinder.mf文件:
            Manifest-Version: 1.0
            Bundle-ManifestVersion: 2
            Bundle-Name: Basic Movie Finder
            Bundle-SymbolicName: BasicMovieFinder
            Bundle-Version: 1.0.0
            Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
            Import-Package: org.osgi.framework,
            osgitut.movies;version="[1.0.0,2.0.0)"
            在這個元文件中相對上節(jié)變化了兩行。第一個增加了Bundle-Activator行。此行描述當前模塊使用的啟動模塊(以前我們并沒有使用)。此外還在導(dǎo)入包行中增加了org.osgi.framework。本節(jié)以前版本的模塊并沒有與框架進行交互,因此也就不需要導(dǎo)入OSGiAPI相關(guān)的包。
            現(xiàn)在重新構(gòu)建BasicMovieFinder.jar。
            > javac -classpath equinox.jar:MoviesInterface.jar osgitut/movies/impl/*.java
            > jar cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class
            回到OSGi控制臺,上節(jié)安裝的BasicMovieFinder.jar還在框架中運行。因此要更新這個模塊,運行update N命令,N是這個模塊的ID(使用ss可以得到此ID)。接著使用start N命令啟動此模塊。接著我們看到什么-基本上也沒看到些什么。
            實際上,模塊已經(jīng)在OSGi服務(wù)注冊表中注冊了,但是并沒有其他人在另外一端要使用此服務(wù)。因此這個服務(wù)的注冊過程并沒有產(chǎn)生任何可視效果。如果我們想向自己保證我們的代碼確實運行了,也大可不必掘地三尺。執(zhí)行下面這個命令就可以了:
            services (objectClass=*MovieFinder)
            我們會看到下面的輸出:
            {osgitut.movies.MovieFinder}={category=misc, service.id=22}
            Registered by bundle: file:BasicMovieFinder.jar [4]
            No bundles using service.
            看到了,服務(wù)的確注冊了。在下節(jié)我們就講解如何查找這個服務(wù)并在另外模塊中使用此服務(wù)。

            =============================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-5使用一個服務(wù)
            5. 使用一個服務(wù):Consuming a Service
            上節(jié)講解了如何注冊一個服務(wù)。現(xiàn)在就是在一個模塊中查找和使用這些服務(wù)的時候了。
            與上節(jié)一樣,我們還是使用電影搜索的例子。我們已經(jīng)構(gòu)建了一個MovieFinder服務(wù)并在服務(wù)注冊表中進行了服務(wù)注冊。現(xiàn)狀我們需要構(gòu)建一個MovieLister來使用這個MovieFinder服務(wù)搜索某個導(dǎo)演執(zhí)導(dǎo)的影片。這里假設(shè)MovieLister也是一個服務(wù),也可以被其他諸如GUI程序使用。問題是,OSGi服務(wù)是動態(tài)的:它們可能有效也可能處理不可使用狀態(tài)。這也就是說有時存在需要使用MovieFinder服務(wù)而它卻正好不能使用的情況。
            那么當MovieFinder不可用的時候MovieLister怎么辦?顯然,調(diào)用MovieFinder是MovieLister能有效運作的核心部分。實際上我們也只有以下幾種選擇:

            • 產(chǎn)生錯誤。如返回null或者拋出一個異常。
            • 等待。
            • 不讓這種情況出現(xiàn)。
              在本文我們選擇比較簡單的前兩種選擇。The third option may not even make any sense to you yet, but hopefully it will after we look at some of the implications of the first two.
              首先我們需要定義MovieLister服務(wù)的接口。復(fù)制以下內(nèi)容到文件osgitut/movies/MovieLister.java:
              package osgitut.movies;
              import java.util.List;
              public interface MovieLister { List listByDirector(String name); }
              然后是文件osgitut/movies/impl/MovieListerImpl.java :
              package osgitut.movies.impl;
              import java.util.*;
              import osgitut.movies.*;
              import org.osgi.framework.*;
              import org.osgi.util.tracker.ServiceTracker;
              public class MovieListerImpl implements MovieLister {
              private final ServiceTracker finderTrack;
              public MovieListerImpl(ServiceTracker finderTrack) { this.finderTrack = finderTrack; }
              public List listByDirector(String name)
              Unknown macro: { MovieFinder finder = (MovieFinder) finderTrack.getService(); if(finder == null) { return null; } else { return doSearch(name, finder); } }

              private List doSearch(String name, MovieFinder finder) {
              Movie[] movies = finder.findAll();
              List result = new LinkedList();
              for (int i = 0; i < movies.length; i++)
              Unknown macro: { if(movies[i].getDirector().indexOf(name) > -1) { result.add(movies[i]); } }

              return result;
              }
              }
              上面的代碼可能是至今我們最長的一段示例代碼了。這里面有些什么呢?第一,實際的影片搜索邏輯分離到了doSearch(string, MovieFinder)方法中。此方法幫助我們隔離了搜索代碼與OSGi代碼。雖然,這里制定的搜索辦法并不是很高效,但對于一個以示例為目的的程序已經(jīng)足夠了:實際上只是實現(xiàn)了一個只有兩部影片的數(shù)據(jù)庫。
              第二部分是listByDirector(String name)方法。這個方法使用了一個ServiceTracker對象從服務(wù)注冊表獲取一個MovieFinder。ServiceTracker是一個十分有用的類。它封裝和抽象了一些難于使用的OSGi底層API。無論如何我們還是要檢查需要的服務(wù)是否實際可用。此服務(wù)假定在MovieLister構(gòu)造函數(shù)中把ServiceTracker傳遞過啦。
              注意,你可能在其他部分看到一些沒有使用ServiceTracker從服務(wù)注冊表查詢服務(wù)的代碼。例如,使用BundleContext的getServiceReference和GetSevice方法。但這樣一來就需要編寫一些更復(fù)雜的代碼并需要仔細進行一些清理工作。在我看來,使用底層API并不能得到太多的好處,反倒是會引起許多問題。最好的選擇還是使用ServiceTracker。
              在一個模塊的啟動器中是創(chuàng)建ServiceTracker的最佳地點。復(fù)制以下代碼到文件osgitut/movies/impl/MovieListerActivator.java:
              package osgitut.movies.impl;
              import java.util.*;
              import org.osgi.framework.*;
              import org.osgi.util.tracker.ServiceTracker;
              import osgitut.movies.*;
              public class MovieListerActivator implements BundleActivator {
              private ServiceTracker finderTracker;
              private ServiceRegistration listerReg;
              public void start(BundleContext context) throws Exception { // Create and open the MovieFinder ServiceTracker finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null); finderTracker.open(); // Create the MovieLister and register as a service MovieLister lister = new MovieListerImpl(finderTracker); listerReg = context.registerService(MovieLister.class.getName(), lister, null); // Execute the sample search doSampleSearch(lister); }
              public void stop(BundleContext context) throws Exception { // Unregister the MovieLister service listerReg.unregister(); // Close the MovieFinder ServiceTracker finderTracker.close(); }
              private void doSampleSearch(MovieLister lister) {
              List movies = lister.listByDirector("Miyazaki");
              if(movies == null) { System.err.println("Could not retrieve movie list"); } else
              Unknown macro: { for (Iterator it = movies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); System.out.println("Title: " + movie.getTitle()); } }

              }
              }
              現(xiàn)在這個啟動器可以啟動服務(wù)查找感興趣的影片了。首先,在start方法中創(chuàng)建了一個ServiceTracker對象。這個對象由之前寫的MovieLister使用。接著就打開ServiceTracker并設(shè)置此對象跟蹤注冊表中的MovieFinder服務(wù)實例。然后創(chuàng)建MovieListerImpl對象并使用MovieLister為名稱在注冊表中注冊一個服務(wù)。最后,為了證明服務(wù)確實運行,在啟動模塊時啟動器使用MovieLister執(zhí)行了一個簡單的搜索并輸出了搜索結(jié)果。
              到此,我們可以使用前幾節(jié)的方法構(gòu)建和安裝我們設(shè)計的這些模塊。要記得創(chuàng)建元文件,并在元文件中正確的設(shè)置Bundle-Activator為osgitut.movies.impl.MovieListerActivator。還有就是在導(dǎo)入包中包含以下三個包:org.osgi.framework , org.osgi.util.tracker and osgitut.movies。
              一旦把MovieLister.jar安裝到了OSGi框架運行環(huán)境,就可以啟動這個模塊。根據(jù)BasicMovieFinder模塊是否處于運行狀態(tài),此時OSGi控制臺會輸出不同的消息。
              如果沒有運行,會輸出:
              osgi> start 2
              Could not retrieve movie list
              如果處于運行狀態(tài)則會輸出:
              osgi> start 2
              Title: Spirited Away
              在停止和啟動模塊的時候,以上消息都會在控制臺出現(xiàn)。還記得我們可以在服務(wù)處于不可用狀態(tài)是選擇等待嗎?使用上面的代碼,只需要做簡單的修改:在MovieListerImpl的第16行中使用ServiceTracker的waitForService(5000)代替getService(),同時增加一個處理InterruptedException的try/catch塊。
              這會導(dǎo)致listByDirector()方法阻塞5000毫秒等待MovieFinder服務(wù)可用。如果這時剛好MovieFinder服務(wù)安裝并且可以使用,這個方法就可以立即獲取到這個服務(wù)。
              通常并不建議以這樣的方式掛起一個線程。在實踐中,這樣做是相對危險的。因為listByDirector方法實際是由模塊啟動器中的start方法調(diào)用的。而模塊啟動器start方法又是由一個框架線程調(diào)用。啟動器必須快速返回,因為當前一個模塊啟動是還需要執(zhí)行許多其他的工作。實際上在最糟糕的情況下這種等待的做法可能導(dǎo)致一個死鎖。因為我們進入了一個框架對象的同步區(qū),而這個同步區(qū)可能已經(jīng)被其他現(xiàn)場鎖定。通常的建議是不要在模塊啟動器的start方法或者其他任何框架直接調(diào)用的代碼中執(zhí)行任何長時間或者阻斷性的操作。

            =================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-6(1)動態(tài)服務(wù)跟蹤
            6 動態(tài)服務(wù)跟蹤:Dynamic Service Tracking
            上節(jié)講解了如何使用一個服務(wù):MovieLister使用MovieFinder查找由某個導(dǎo)演指導(dǎo)的影片。同時還涉及了處理OSGi服務(wù)動態(tài)特性的各種策略,特別介紹了MovieLister找不到一個有效的MovieFinder服務(wù)時的處理辦法。
            還有一種上節(jié)沒有涉及的情況是:如何處理同時有多個MovieFinder服務(wù)可用的情況?因為任何模塊都可以使用MovieFinder接口注冊一個服務(wù),并且對于服務(wù)注冊機制來說任何模塊都是同等對待的。
            我們可以簡單的忽略這個問題,這也是上節(jié)代碼的實際處理邏輯。通過調(diào)用ServiceTracker上的getService()方法,我們選擇由服務(wù)注冊表任意提供一個MovieFinder服務(wù)。有一些參數(shù)可以調(diào)整注冊表的選擇策略(比如在服務(wù)注冊時設(shè)置的SERVICE_RANKKING屬性),但是作為一個服務(wù)的使用者我們并沒有參與這個服務(wù)選擇過程。并且實際上盡量少的控制并不是一件壞事,因為我們應(yīng)該可以使用任何一個MovieFinder服務(wù)。這也是使用接口的原因。
            從另外一方面來說,在一些情況下注冊和使用多個服務(wù)也大有用途,例如,如果有多個有效的MovieFinder服務(wù),這就意味這存在多個影片數(shù)據(jù)來源可供MovieLister使用。如果可以通過使用所有的這些服務(wù)進行影片查找,我們就可以獲得更大的搜索網(wǎng)絡(luò)空間并提供給用戶更好的搜索結(jié)果。
            另一個問題是上節(jié)遺留下來的:在沒有任何一個MovieFinder服務(wù)可用時,MovieLister應(yīng)該如何正確處理?上節(jié)的處理邏輯是在服務(wù)無效時簡單的在listByDirector調(diào)用時返回null。But what if we made it impossible for methods on MovieLister to be called when the MovieFinder isn't present?
            MovieLister與MovieFinder一樣是一個服務(wù)。如果MovieFinder服務(wù)消失了,MovieLister服務(wù)是不是也應(yīng)該消失?也就是說,我們希望MovieLister與MovieFinder之間有一個一對多的依賴關(guān)系。本文的最后一節(jié),我們介紹零對一依賴關(guān)系。
            首先使用下面的代碼修改MovieListerImpl類:
            package osgitut.movies.impl;
            import java.util.*;
            import osgitut.movies.*;
            public class MovieListerImpl implements MovieLister {

            private Collection finders =
            Collections.synchronizedCollection(new ArrayList());

            protected void bindFinder(MovieFinder finder) { finders.add(finder); System.out.println("MovieLister: added a finder"); }

            protected void unbindFinder(MovieFinder finder) { finders.remove(finder); System.out.println("MovieLister: removed a finder"); }

            public List listByDirector(String director) {
            MovieFinder[] finderArray = (MovieFinder[])
            finders.toArray(new MovieFinder[finders.size()]);
            List result = new LinkedList();
            for(int j=0; j<finderArray.length; j++) {
            Movie[] all = finderArray[j].findAll(); 
            for(int i=0; i<all.length; i++) {
            if(director.equals(all[i].getDirector())) { result.add(all[i]); }
            }
            }
            return result;
            }

            }
            上面的代碼移除了MovieListerImpl上所有的OSGi依賴。MovieListerImpl現(xiàn)在是一個純POJO了。當然,它還需要其他對象協(xié)助跟蹤MovieFinder服務(wù)并通過bindFinder方法提供此服務(wù)。因此我們還需要創(chuàng)建一個新的java文件:osgitut/movies/impl/MovieFinderTracker.java。
            package osgitut.movies.impl;
            import org.osgi.framework.*;
            import org.osgi.util.tracker.*;
            import osgitut.movies.*;
            public class MovieFinderTracker extends ServiceTracker {

            private final MovieListerImpl lister = new MovieListerImpl();
            private int finderCount = 0;
            private ServiceRegistration registration = null;

            public MovieFinderTracker(BundleContext context) { super(context, MovieFinder.class.getName(), null); }

            private boolean registering = false;
            public Object addingService(ServiceReference reference) {
            MovieFinder finder = (MovieFinder) context.getService(reference);
            lister.bindFinder(finder);
            synchronized(this) { finderCount ++; if (registering) return finder; registering = (finderCount == 1); if (!registering) return finder; }
            ServiceRegistration reg = context.registerService(
            MovieLister.class.getName(), lister, null);
            synchronized(this) { registering = false; registration = reg; }
            return finder;
            }
            public void removedService(ServiceReference reference, Object service) {
            MovieFinder finder = (MovieFinder) service;
            lister.unbindFinder(finder);
            context.ungetService(reference);
            ServiceRegistration needsUnregistration = null;
            synchronized(this) {
            finderCount --;
            if (finderCount == 0) { needsUnregistration = registration; registration = null; }
            }
            if(needsUnregistration != null) { needsUnregistration.unregister(); }
            }
            }

            ==========================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-6(2)動態(tài)服務(wù)跟蹤
            上面這個類覆蓋了上節(jié)討論的ServiceTracker類,并且定制了服務(wù)有效和失效時ServiceTracker的行為。具體來說就是,當一個增加MovieFinder服務(wù)時調(diào)用addingSevice方法,當刪除一個MovieFinder服務(wù)時調(diào)用removedService方法。此外還有一個modifiedService方法,只不過在本節(jié)處理邏輯中不需要而沒有覆蓋。
            有必要仔細看看這兩個方法中的處理邏輯。首先,在addingSevice方法中傳入的參數(shù)是ServiceReference而不是一個實際服務(wù)實現(xiàn)對象。ServiceReference是一個輕量級的句柄對象。它可以作為一個參數(shù)任意的進行傳輸,并可以用來獲取實際服務(wù)的屬性。比如,服務(wù)在進行服務(wù)注冊時傳入的屬性集。實際上,使用一個ServiceReference對象并不會導(dǎo)致OSGi框架增加實際服務(wù)的引用計數(shù)。你可以認為ServiceReference承擔了類似java反射API中WeakReference類的角色。
            在addingSevice方法中首先是通過ServiceReference獲取一個實際的MovieFinder服務(wù)對象。這又要使用BundleContext:任何與OSGi框架的交互都是通過BundleContext接口進行的。幸運的是,ServiceTracker定義了一個我們可以直接使用的類型為BundleContext的保護成員context。
            接下來我們使用bindFinder方法把獲取到的MovieFinder服務(wù)綁定到MovieListerImpl。然后就把MovieListerImpl也注冊為一個使用MovieLister接口的服務(wù)。注意,我們只在MovieListerImpl沒有注冊為服務(wù)的情況下才進行服務(wù)注冊。這是因為,我們現(xiàn)在希望一個MovieLister使用多個MovieFinder服務(wù)。
            最后,addingSevice方法返回一個Object。問題是,我們到底要返回一個什么對象呢?實際上,ServiceTracker并不關(guān)心addingSevice返回什么對象。addingSevice可以返回任何我們需要的對象。重點是,addingSevice返回的對象會在調(diào)用modifiedService或者removedService的時候重新傳回。這也就是在removedService方法第一行見到的直接對object進行的MovieFinder類型轉(zhuǎn)換。接著就使用此對象進行它與MovieLister的解綁,同時我們根據(jù)可以使用的MovieFinder服務(wù)是否為零進行MovieLister服務(wù)的反注冊。
            一般說來,任何在addingSevice中的行為都應(yīng)該在removedService中進行狀態(tài)清理。因此,我們應(yīng)該在addingSevice中返回任何有助于我們需要在removedService中進行清理工作的對象。返回的可以是一個HashMap中的鍵值,也可以是一個ServiceRegistration對象,或者是一個實際的服務(wù)對象。
            在removedService的最后一步,我們解綁了在addingSevice中綁定的服務(wù)。這一點十分重要,這個操作使得服務(wù)注冊表減少服務(wù)使用計數(shù)值,從而可以使計數(shù)值為零進行服務(wù)釋放。
            現(xiàn)狀我們還需要一個模塊啟動器osgitut/movies/impl/TrackingMovieListerActivator.java:
            package osgitut.movies.impl;
            import org.osgi.framework.*;
            public class TrackingMovieListerActivator implements BundleActivator {
            private MovieFinderTracker tracker;
            public void start(BundleContext context) { tracker = new MovieFinderTracker(context); tracker.open(); }
            public void stop(BundleContext context) { tracker.close(); }
            }
            不要忘記TrackingMovieLister.mf:
            Manifest-Version: 1.0
            Bundle-ManifestVersion: 2
            Bundle-Name: Tracking Movie Lister
            Bundle-SymbolicName: TrackingMovieLister
            Bundle-Version: 1.0.0
            Bundle-Activator: osgitut.movies.impl.TrackingMovieListerActivator
            Import-Package: org.osgi.framework,
            org.osgi.util.tracker,
            osgitut.movies;version="[1.0.0,2.0.0)"
            按照前幾節(jié)的步驟進行模塊的構(gòu)建和部署。完成后,執(zhí)行以下的步驟驗證運行邏輯是否正確:

            • 啟動 BasicMovieFinder和TrackingMovieLister。檢查是否出現(xiàn)"MovieLister:added a finder"消息。
            • 停止 BasicMovieFinder。檢查是否出現(xiàn)"MovieLister: removed a finder"消息。
            • 執(zhí)行service命令,檢查是否MovieLister服務(wù)已經(jīng)反注冊。
              本節(jié)內(nèi)容展示的是一個十分強大的技術(shù)。我們把一個服務(wù)的生命周期與另一個服務(wù)(實際上是多個)的生命周期綁定。進一步,我們可以綁定MovieLister和第三個服務(wù)并可以依此類推。這樣我們就構(gòu)建了一個服務(wù)依賴圖,這種依賴圖與其他一些靜態(tài)IOC容器構(gòu)建的beans圖不同。OSGi的服務(wù)依賴圖更健壯,self-healing并具有自動調(diào)整能力。
              另一方面,這一節(jié)的代碼還存在一些問題。MovieFinderTracker和TrackingMovieListerActivator類會形成整個系統(tǒng)負載的瓶頸。當我們打算進行這個系統(tǒng)的擴展時,我們還不得不重復(fù)進行這些基本雷同的編碼工作。因此在下一節(jié)我們講解如何使用幾行XML語句完成這些重復(fù)和繁瑣的工作。
              在下一節(jié)不再在一個目錄下使用命令行構(gòu)建我們的系統(tǒng)。本文的目標是展示OSGi是一個簡單而強大的框架,但并不妨礙我們使用更高效的方式比如eclipse的IDE完成這些工作。

            ==================================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-7服務(wù)聲明介紹
            7 服務(wù)聲明介紹:Introducing Declarative Services
            服務(wù)聲明(簡稱DS)規(guī)范是OSGi最新的內(nèi)容之一。增加此服務(wù)規(guī)范是為了在多個模塊之間有效協(xié)調(diào)服務(wù)。這種服務(wù)協(xié)調(diào)工作本身并不復(fù)雜(如上節(jié)我們展示的那樣),但是這項工作總是面臨那些惱人的代碼。而且具體實現(xiàn)時還必須注意處理各種線程問題。總的來說,模塊服務(wù)協(xié)作這件工作是一件極易出錯的工作。
            最早試圖解決服務(wù)協(xié)作問題的是一個稱為Service Binder的工具。這個工具由Humberto Cervantes 和 Richard Hall開發(fā)。Service Binder設(shè)計了自動服務(wù)依賴管理特性。這個特性允許開發(fā)者專注于服務(wù)。服務(wù)之間的協(xié)作和依賴通過聲明實現(xiàn),而所有的聲明都使用XML完成。
            服務(wù)聲明規(guī)范是由Service Binder發(fā)展而來,并成為了OSGi4.0標準的一部分。
            前面幾節(jié)的示例都是使用的命令行完成所有的編碼、配置、構(gòu)建和運行工作。從這節(jié)開始則使用EclipseSDK。需要明確的是,本文中介紹的任何技術(shù)和方法并不依賴于Eclipse。盡管Eclipse可以很好的幫助我們完成很多工作,但同樣的我們也可以使用NetBeans,Intellij或者vi來完成這些工作。
            首先我們需要下載Equinox的服務(wù)聲明實現(xiàn)包。可以在Equinox的下載頁面下載最新的org.eclipse.equniox.ds_x.x.x_xxxx.jar。下載完成后,把下載的jar復(fù)制到Eclipse的plugins目錄,并重新啟動Eclipse。
            現(xiàn)在重新創(chuàng)建一個模塊。在Eclipse中使用插件工程向?qū)В?br />l 在主菜單中選擇File->New->Project。
            l 選擇Plug-in Project并點擊Next。
            l 輸入工程的名稱:SampleExporter。
            l 在最下面的"This plug-in is targeted to run with"文字下選擇"OSGi framework"以及下拉框中的"standard"。這個是個基本步驟:這一步可以防止我們使用OSGi框架實現(xiàn)之外的特性。
            l 選擇"Next"。在向?qū)э@示的下一個對話框中選擇"Generate an activator..."。如果選擇,點擊"Finish"完成創(chuàng)建工作。
            我們現(xiàn)在生成了一個空的模塊工程。接下來的工作就是在這個工程中增加代碼。為了使得示例盡量簡單,我們使用在每個java運行環(huán)境中都存在的java.lang.Runnable接口提供服務(wù)。現(xiàn)在我們可以使用Eclipse的復(fù)制代碼特性創(chuàng)建包和源文件。復(fù)制下面的代碼,在Eclipse中選擇SampleExporter工程的src目錄,并執(zhí)行Edit->Paste。
            package org.example.ds;
            public class SampleRunnable implements Runnable {
            public void run() { System.out.println("Hello from SampleRunnable"); }
            }
            接下來我們需要創(chuàng)建一個XML文件。使用這個文件聲明SampleRunnable是一個服務(wù)。在工程的頂級目錄下創(chuàng)建一個名稱為OSGI-INF的目錄,新建一個名稱為samplerrunnable.xml的文件然后復(fù)制下面的文本到文件中。
            <?xml version="1.0"?>
            <component name="samplerunnable">
            <implementation class="org.example.ds.SampleRunnable"/>
            <service>
            <provide interface="java.lang.Runnable"/>
            </service>
            </component>
            這是一個最簡單的DS聲明。這個聲明表示有一個名稱為"samplerunnable"的組件,這個組件使用接口java.lang.Runnable向服務(wù)注冊器提供一個服務(wù)。同時還說明了這個模塊是由org.example.da.SampleRunnable類實現(xiàn)的。
            最后就是要告知服務(wù)聲明runtime這個XML文件的位置。通知是通過在組件的MANIFEST.MF文件中增加一項說明實現(xiàn)的。在Eclipse中打開元文件頁面,定位到"MANIFEST.MF"文件窗格。在這個文件內(nèi)容的最后增加一行:
            Sevice-Component: OSGI-INF/samplerunnable.xml
            然后保存文件。在進行下一步之前,我們可以運行Equinox檢查一下我們的模塊是否可以正常運行。在Run菜單中選擇"Run...",當運行對話框打開后選擇左邊樹中的"Equniox OSGi Framework"并點擊New按鈕。如果你是一個熟練的Eclipse用戶但又沒有做過OSGi開發(fā),這個對話框相對來說有些復(fù)雜(或者奇怪)。第一個標識為Bundles的窗格允許我們選擇哪些Bundles是運行環(huán)境需要包含的,是否需要啟動等。為了使用一個簡化的運行環(huán)境,取消當前所有選中的Bundles,并重新選擇以下的Bundles:
            SampleExporter (underneath Workspace)
            org.eclipse.equinox.ds (underneath Target Platform)
            除此之外我們還有一些依賴的Bundles,這可以使用Add Required Bundles功能把這些依賴的Bundles添加進來。最好把"Validate bundles automatically prior to launching"也選中。最后,點擊Run運行這個工程。一段時間后osgi>提示符就出現(xiàn)在Eclipse控制臺。OSGi框架運行起來了,我們可以使用前幾節(jié)的命令與框架進行交互。
            我們開發(fā)的服務(wù)注冊了沒有?使用services命令檢查一下。在命令的輸出結(jié)果中,我們會發(fā)現(xiàn)類似的信息:
            {java.lang.Runnable}={component.name=samplerunnable, component.id=1, service.id=22}
            Registered by bundle: initial@reference:file:..../SampleExporter/ [5]
            No bundles using service.
            看來服務(wù)的確注冊了。需要指出的是:服務(wù)是由我們的模塊注冊的,而不是由聲明服務(wù)模塊注冊的,實際上,In fact DS has registered it on behalf of our bundle。還需要注意的是,服務(wù)的使用者并不需要為了使用這個服務(wù)而做什么特別的處理,它甚至不必知道我們使用了服務(wù)聲明。這些服務(wù)使用者也可以使用DS或者直接使用OSGi代碼來訪問服務(wù)。
            還有一點就是在上面的模塊代碼中并沒有出現(xiàn)與OSGi標準相關(guān)的java代碼。也就是說,本節(jié)我們編寫的是一個POJO類。這也是DS的一個主要特點。

            =================================================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-8(1)服務(wù)聲明和依賴

            8服務(wù)聲明和依賴:Declarative Services and Dependencies
            上節(jié)對DS做了初步的介紹。本節(jié)介紹如何使用DS聲明的服務(wù)。上節(jié)我們使用java.lang.Runnable接口使用DS注冊了一個服務(wù),本節(jié)介紹如何創(chuàng)建一個依賴于這個服務(wù)的組件。
            正如介紹的那樣,DS規(guī)范使得開發(fā)者只需關(guān)注應(yīng)用自身的邏輯,避免如前幾節(jié)需要編寫的那些OSGi服務(wù)"粘合"代碼。使用DS,我們只要簡單的編寫代碼就可以了。但在之間編碼之前,我們還得創(chuàng)建一個Eclipse工程。使用上節(jié)的工程創(chuàng)建步驟創(chuàng)建一個名稱為SampleImporter的工程。
            復(fù)制下面的代碼并粘貼到新建工程的src目錄:
            package org.example.ds;
            import org.eclipse.osgi.framework.console.CommandInterpreter;
            import org.eclipse.osgi.framework.console.CommandProvider;
            public class SampleCommandProvider1 implements CommandProvider {

            private Runnable runnable;

            public synchronized void setRunnable(Runnable r) { runnable = r; }
            public synchronized void unsetRunnable(Runnable r) { runnable = null; }
            public synchronized void _run(CommandInterpreter ci) {
            if(runnable != null) { runnable.run(); } else { ci.println("Error, no Runnable available"); }
            }
            public String getHelp() { return "\trun - execute a Runnable service"; }
            }
            這個類實現(xiàn)了CommandProvider接口。啟動Equinox后在osgi>提示符可以執(zhí)行一系列的命令。CommandProvider接口用于擴充這些命令。編寫CommandProvider的原因是為了提供一種方便的交互式測試代碼方式。在IBM developerWorks上Chirs Aniszczyk的一篇文章詳細討論了CommandProvider。
            在上面的代碼中我們注意到?jīng)]有任何OSGi方法調(diào)用,實際上我們并不需要從org.osgi.*導(dǎo)入任何類。我們依賴的那個服務(wù)(在本示例中是一個java.lang.Runnable實例)是通過setRunnable方法提供的,并且通過unsetRunnable移除。我們可以認為這是某種形式的依賴注射。
            另外兩個方法getHelp和_run是CommandProvider接口定義的實現(xiàn)方法。_run方法名的下劃線有些滑稽,但這也只是Equinox控制臺API的一項odd特性,與OSGi的DS無關(guān)。在Equinox控制臺中規(guī)定具有下劃線的方法名稱是一個控制臺命令。因此定義一個_run方法就相當于在Equinox控制臺中增加了一個run命令。另外一個需要注意的情況是,在上面這個類的實現(xiàn)代碼中仔細的處理了runnable屬性的線程安全特性。由于OSGi本質(zhì)上是多線程的,因此在OSGi中線程安全是十分重要的。Frankly我們應(yīng)該總是編寫線程安全的代碼。
            和以前一樣,我們還是需要一個包含DS聲明的XML文件。復(fù)制下面的文本到這個插件工程的OSGI-INF/commandprovider1.xml文件中:
            <?xml version="1.0"?>
            <component name="commandprovider1">
            <implementation class="org.example.ds.SampleCommandProvider1"/>
            <service>
            <provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
            </service>
            <reference name="RUNNABLE"
            interface="java.lang.Runnable"
            bind="setRunnable"
            unbind="unsetRunnable"
            cardinality="0..1"
            policy="dynamic"/>
            </component>
            編寫完成了XML文件后還要把下面這行加入到bundle元文件的最后:
            Service-Component: OSGI-INF/commandprovider1.xml

            ===========================================================================================================
            OSGi開發(fā)起步(Getting Started with OSGi)-8(2)服務(wù)聲明和依賴

            在XML文件中有兩個我們已經(jīng)介紹過的元素項:implementation和service。Implementation提供了組件的實現(xiàn)類,而service則告知DS把這個組件注冊為一個服務(wù)。這個示例中的服務(wù)使用CommandProvider接口注冊。這個接口使得Equinox控制臺知曉這個CommandProvider的存在。
            文件中還有一個以前沒有介紹的reference元素項。reference用于向DS聲明組件與一個服務(wù)之間的依賴關(guān)系。其中的name屬性為任意字符串。名稱屬性用于對依賴進行命名(我們并不需要關(guān)心它的用途-實際上只是一個可讀性的標識)。本示例中使用了組件依賴的接口名稱來命名。bind屬性指定一個實現(xiàn)類的方法名。當一個服務(wù)可用時,也就是一個Runnable的服務(wù)在服務(wù)注冊表中注冊時,DS就會調(diào)用此方法。DS使用這個方法傳入這個新服務(wù)對象的引用提供給我們的組件使用。同樣的,unbind屬性是當一個服務(wù)不可用時,DS回調(diào)的方法。
            Cardinality是一個能顯示DS真正能力的屬性。這個屬性控制服務(wù)的依賴是可選的還是強制的、單依賴還是多依賴。這個屬性可能的值為:
            0..1: optional and singular, "zero or one"
            1..1: mandatory and singular, "exactly one"
            0..n: optional and multiple, "zero to many"
            1..n: mandatory and multiple, "one to many" or "at least one"
            在這個示例中我們使用的是0.1。也就是能cope那些不可用的服務(wù)。回頭看以下_run方法中的代碼,我們就會發(fā)現(xiàn)為了處理這種情況而進行的null檢查代碼。
            現(xiàn)在還是來看看這個模塊運行的情況。如果上節(jié)安裝的SampleExporter還有效,在osgi控制臺輸入run命令會得到如下輸出:
            Hello from SampleRunnable
            這證實了我們已經(jīng)成功將上節(jié)編寫的Runnable服務(wù)成功導(dǎo)入。接著運行stop命令停止SampleExporter模塊。再運行run命令得到的輸出是:
            Error,no Runnable available
            這個輸出表明DS發(fā)現(xiàn)了Runnable服務(wù)已經(jīng)停止并調(diào)用了unsetRunnable方法。
            再看一下cardinality屬性,如果把它的值改為1.1(改可選為強制)會怎樣?更改這個屬性后重新運行Equinox。在SampleExporter啟動后運行run命令會得到與前面相同的結(jié)果。但是當停止SampleExporter后運行run,OSGi輸出的是一個與前面不同的錯誤消息。實際上,Equinox控制臺輸出的是一個幫助消息。這個消息是對一個不可識別命令的標準錯誤輸出消息。這也就是說我們這個命令執(zhí)行服務(wù)已經(jīng)被DS反注冊了。當一個組件的強制依賴不能滿足是,DS強制停止這個組件并反注冊組件提供的任何服務(wù)。這就是Equinox不能識別run命令的原因。
            如此方便的服務(wù)裝配是選擇DS的最佳原因。還記得使用ServiceTracker時,我們處理這種情況時不得不編寫的那些代碼嗎?
            還有一個值得關(guān)注的屬性是ploicy。這個屬性的值定義為static和dynamic之一。這兩個狀態(tài)值用于表明組件是否支持服務(wù)的動態(tài)切換。如果不支持,DS就沒有必要每次在目標服務(wù)變化后停止原組件并創(chuàng)建一個新組件實例。停止后新建這個過程可是一個重負載性的過程,所以我們的建議最好還是盡可能的編碼一個支持動態(tài)切換的組件。不過DS的這個屬性缺省是靜態(tài)的,因此在組件開發(fā)時還得手動的調(diào)整此屬性為動態(tài)。
            上面我們試了0.1和1.1,但是沒有講解多對多(0.n)的依賴。在服務(wù)注冊表中不會僅僅只有一個Runnable服務(wù)注冊。如果我們只是需要綁定其中的一個服務(wù),那么我們所綁定的將是其中的任意一個服務(wù)。看來我們還需要一個runall命令用來運行服務(wù)注冊表中已經(jīng)注冊的所有Runnable服務(wù)。
            但就對上面那個組件來說,我們把cardinality屬性更改為"0.n"時會出現(xiàn)什么情況?其實,更改后這個服務(wù)還是可以運行:只不過setRunnable不會只調(diào)用一次,DS會在每個Runnable服務(wù)注冊是調(diào)用此方法一次。問題是多次調(diào)用會導(dǎo)致上面組件處理邏輯的混亂。因此為應(yīng)對更通常的0.n的情況,就不能只是使用單個的Runnable成員,而是需要一個Runnable集合成員。下面是根據(jù)以上分析修改的類,把這個類的代碼復(fù)制到工程的src目錄下:
            package org.example.ds;
            import java.util.*;
            import org.eclipse.osgi.framework.console.CommandInterpreter;
            import org.eclipse.osgi.framework.console.CommandProvider;
            public class SampleCommandProvider2 implements CommandProvider {
            private List<Runnable> runnables =
            Collections.synchronizedList(new ArrayList<Runnable>());

            public void addRunnable(Runnable r) { runnables.add(r); }
            public void removeRunnable(Runnable r) { runnables.remove(r); }
            public void _runall(CommandInterpreter ci) {
            synchronized(runnables) {
            for(Runnable r : runnables) { r.run(); }
            }
            }
            public String getHelp() { return "\trunall - Run all registered Runnables"; }
            }
            再創(chuàng)建OSGI-INF/commandprovider2.xml:
            <?xml version="1.0"?>
            <component name="commandprovider2">
            <implementation class="org.example.ds.SampleCommandProvider2"/>
            <service>
            <provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
            </service>
            <reference name="RUNNABLE"
            interface="java.lang.Runnable"
            bind="addRunnable"
            unbind="removeRunnable"
            cardinality="0..n"
            policy="dynamic"/>
            </component>
            最后是把這個文件按照如下方式加到元文件中:
            Service-Component: OSGI-INF/commandprovider1.xml,
            OSGI-INF/commandprovider2.xml
            上面的XML文件中的DS聲明與以前的基本一樣,只是修改了bind和unbind方法的名稱,以及cardinality的值(0.n)。現(xiàn)在就可以試著運行這個新的runall命令看看結(jié)果如何啦。欣賞完輸出結(jié)果后,還可以試著更改cardinality為1.n看看有什么不一樣的事情發(fā)生!
            本節(jié)是這個講解的最后一節(jié)。通過這幾節(jié)的講解我們發(fā)現(xiàn)OSGi并不神秘并且相當易用。接下來的事情就是不斷的開發(fā)和使用它。不過,我還是強烈的建議在開始和進行過程中一定認真的學(xué)習(xí)一下OSGi R4 core和service規(guī)范。

            =======================================================================================

            Feedback

            # re: OSGi開發(fā)起步  回復(fù)  更多評論   

            2016-01-16 14:31 by M.I
            OSGi最成功的開源開發(fā)平臺JXADF,詳細訪問OSGi Alliacne , http://osgia.com
            国产精品久久亚洲不卡动漫| 日本加勒比久久精品| 99久久夜色精品国产网站| 日本精品久久久久久久久免费| 伊人色综合久久天天人手人婷| 久久精品亚洲精品国产色婷 | 亚洲午夜久久久久久噜噜噜| 精品久久久久久综合日本| 国产精品久久久久免费a∨| 99久久人妻无码精品系列| 武侠古典久久婷婷狼人伊人| 精品熟女少妇a∨免费久久| 久久人妻少妇嫩草AV蜜桃| 久久久久无码精品国产| 亚洲精品乱码久久久久久不卡| 久久国产高清字幕中文| 99精品久久精品一区二区| 久久婷婷五月综合97色直播| 久久精品无码一区二区三区| 久久亚洲私人国产精品| 亚洲精品午夜国产va久久| 精品国产青草久久久久福利| 99久久99久久久精品齐齐| 久久久亚洲欧洲日产国码二区| 伊色综合久久之综合久久| 久久国产福利免费| 国产激情久久久久影院| 99精品久久久久中文字幕| 久久婷婷五月综合97色一本一本| 合区精品久久久中文字幕一区| 久久AⅤ人妻少妇嫩草影院| 欧美精品一区二区精品久久| 久久成人国产精品| 久久夜色精品国产噜噜亚洲AV | 色欲久久久天天天综合网 | 色偷偷888欧美精品久久久| 99久久综合狠狠综合久久止| 久久ZYZ资源站无码中文动漫| 久久99精品国产自在现线小黄鸭 | 久久精品人人做人人爽电影蜜月| 狠狠色婷婷久久一区二区 |