背景 OSGi給出了一套Java模組化規範,這套規範給出了OSGi框架的定義,而具體的OSGi平台,如Felix和Equinox則分別是Apache和Eclipse開源社區給出的標準規範的實現。
OSGi服務平台提供在多種網路設備上無需重啟的動態改變構造的功能。為了最小化耦合度和促使這些
耦合度 可管理,OSGi技術提供一種面向服務的架構,它能使這些組件動態地發現對方。OSGi聯盟已經開發了例如像
HTTP 伺服器、配置、日誌、安全、用戶管理、
XML 等很多公共功能標準組件接口。這些組件的兼容性
外掛程式 實現可以從進行了不同最佳化和使用代價的不同計算機服務提供商得到。然而,服務接口能夠基於專有權基礎上開發。
OSGI 因為OSGi技術為集成提供了預建立和預測試的
組件 子系統,所以OSGi技術使你從改善產品上市時間和降低開發成本上獲益。因為這些組件能夠動態發布到設備上,所以OSGi技術也能降低維護成本和擁有獨一無二的新的配件市場機會。
安全協定 安全機制是建立在Java和
Java2 安全模型基礎之上。
Java語言 的設計對很多結構進行了限制。例如病毒中經常遇到的
快取 溢出是不可能發生的。Java語言中的訪問控制符限制了代碼可見性。
OSGI平台通過使用私有類(在Java中不能用標準方式使用的機制)擴展了該模型。Java2安全模型提供了一個完整模組檢查代碼對於資源的可訪問性。OSGI增加了完全動態的許可權管理,簡化了操作者和
系統管理員 的工作。
OSGI聯盟已經定義了很多協定服務,這些服務將外部協定映射為OSGI服務。
HTTP 服務(HttpService)該HTTP服務是servlet運行器。bundles提供servlets,這些服務端小程式基於
HTTP協定 成為可用的。OSGi服務平台的動態更新功能使HTTP服務成為一個非常具有吸引力的Web伺服器,它能伴隨著新的servlet被更新,如果需要可以遠程更新而無需重啟。
UPnP 服務(UPnPService)通用即插即用(UPnP)是一個正在形成中的消費電子標準。OSGi中的UPnP服務在一個UPnP網路上將設備映射到服務註冊中。同樣,它也可以將OSGi服務映射到UPnP網路。這是發布版本3中的推薦規範。
DMT管理(DMTAdmin)開放移動聯盟(OMA)基於設備管理樹為移動設備管理提供了一個完整規定。DMT管理服務定義該樹如何被訪問和/或者在OSGi服務平台中被擴充。
框架結構 OSGI規範的核心組件是OSGI框架。這個框架為應用程式(被叫做組件(bundle))提供了一個標準環境。整個框架可以劃分為一些層次:
L0:運行環境
OSGI L2:生命周期管理
L3:服務註冊
L0層執行環境是Java環境的規範。Java2配置和子規範,像
J2SE ,CDC,CLDC,
MIDP 等等,都是有效的執行環境。OSGi平台已經標準化了一個執行環境,它是基於基礎輪廓和在一個執行環境上確定了最小需求的一個小一些的變種,該執行環境對OSGi組件是有用的。
L1模組層定義類的裝載策略。OSGi框架是一個強大的具有嚴格定義的類裝載模型。它基於Java之上,但是增加了模組化。在Java中,正常情況下有一個包含所有類和資源的類路徑。OSGi模組層為一個模組增加了私有類同時有可控模組間連結。模組層同安全架構完全集成,可以選擇部署到部署
封閉系統 ,防禦系統,或者由廠商決定的完全由用戶管理的系統。
L2生命周期層增加了能夠被動態安裝、開啟、關閉、更新和
卸載 的bundles。這些bundles依賴于于具有類裝載功能的模組層,但是增加了在運行時管理這些模組的
API 。生命周期層引入了正常情況下不屬於一個應用程式的動態性。擴展依賴機制用於確保環境的操作正確。生命周期操作在安全架構保護之下,使其不受到病毒的攻擊。
L3層增加了服務註冊。服務註冊提供了一個面向bundles的考慮到動態性的協作模型。bundles能通過傳統的類共享進行協作,但是類共享同動態安裝和卸載代碼不兼容。服務註冊提供了一個在bundles間分享對象的完整模型。定義了大量的事件來處理服務的註冊和刪除。這些服務僅僅是能代表任何事物的Java對象。很多服務類似伺服器對象,例如HTTP伺服器,而另一些服務表示的是一個真實世界的對象,例如附近的一個藍牙手機。這個服務模組提供了完整安全保障。該服務安全模組使用了一個很聰明的方式來保障bundles之間通信安全。
標準服務 在該框架之上,OSGi聯盟定義了很多服務。這些服務通過一個Java接口指定。bundles能夠實現這個接口,並在註冊服務層註冊該服務。服務的客戶端在註冊庫中找到它,或者當它出現或者消失時做出回響。這個同
SOA 架構使用Web服務進行發布的方式相似。
OSGI 兩者主要不同是Web服務總是需要傳輸層,這個使它比採用直接方法調用的OSGi服務慢幾千倍。同時,OSGi組件能夠對這些服務的出現和消失做出回響。更多的信息可以從OSGi服務平台發行版本4手冊或者
PDF 下載中找到。需要注意的是每一種服務都是抽象定義的,與不同計算機服務商的實現相獨立。
框架服務 OSGi框架提供一個許可權管理服務,一個包管理服務和一個開始級別服務。這些服務是一個可選部分,指示框架的操作。框架服務如下:
OSGI 許可權管理(PermissionAdmin)目前或者將來的bundles的
許可權 通過這種服務進行維護。一旦設定了它們,許可權服務立即激活。
包管理(PackageAdmin)bundles同類和資源分享包。bundles的更新可能需要系統重新計算這些依賴。這個包管理服務提供關於系統的實際包分享狀態和能夠刷新已經共享的包。也就是,取消依賴和重新計算依賴。
啟動級別(StartLevel)啟動級別是一個bundles集合,它們應該同時運行或者應該在其它已經啟動以前被初始化。啟動級別服務設定當前的啟動級別,為每個bundle排一個啟動級別和審核當前的設定。
URL處理者(URLHandler)Java環境為
URL 處理者支持一個提供者模型。然而,這是一個單件,不可能在一個象OSGi可能有很多提供者的協作環境上使用它。此服務規範使任何組件提供額外的URL處理者。
系統服務 系統服務提供水平功能,它在每個系統是必須的。日誌服務,配置管理服務,設備訪問服務,用戶管理服務,
IO 連線器服務和參數服務都是系統服務的一個方面。
OSGI 日誌服務(LogService)日誌信息,警告,調試或者錯誤信息通過日誌服務來處理的。它接受日誌實體並分派這些實體到訂閱了這個信息的其他bundles。
配置管理服務(ConfigurationAdminService)該服務提供一個設定和獲取配置信息的靈活、動態模型。
設備訪問服務(DeviceAccessService)設備訪問是OSGi為一個新的設備匹配一個驅動,並自動下載一個實現該驅動的bundles的機制。這個可用作即插即用方案。
用戶管理服務(UserAdminService)該服務使用一個用於授權和驗證目的的用戶信息資料庫。
IO連線器服務(IOConnectorService)該IO連線器服務實現了CDC/CLDCjavax包,並作為一個服務。該服務允許bundles提供新的可交換協定模式。
參數服務(PreferencesService)該服務提供了參數層級資料庫的可訪問性,同Windows註冊表或者Java參數類相似。
組件運行時服務(ComponentRuntime)服務的動態特性--它們能夠在任何時間來去自由--使編寫軟體變得更難。組建運行時規範通過提供一個基於依賴聲明的XML檔案來簡化處理這些動態方面。
部署管理服務(DeploymentAdmin)OSGi的主要部署格式是bundle,它是一個JAR/ZIP檔案。部署管理提供第二種可選格式:部署包。部署包能夠將bundles和相應資源聯接成可被安裝和卸載的單個交付。完整的資源處理器模型允許用戶代碼擴充資源類型。
事件管理服務(EventAdmin)很多OSGi事件有特定的類型化的
接口 ,使其很難接收和過濾事件。事件服務提供一個泛化的基於主題的事件機制。這個規範包括為所有已存框架和服務事件的映射。
應用程式管理服務(ApplicationAdmin)OSGibundle模型不同於依賴於啟動和關閉形式的典型的桌面或者行動電話應用程式模型。該應用程式管理服務提供了傳統應用程式模型和它所要求的管理設施。
HelloWorld 0.前言
這篇文檔介紹如何使用OSGi框架的一個實現——Equinox來教你如何配置一個簡單的OSGi開發環境,並且在這個環境上開發一個HelloWorld程式,這其中會涵蓋前面的入門篇講到的三個層次的內容,讓你在實踐的同時鞏固之前了解的內容。話不多說,我們開始吧!
開發環境建立
1.1 Equinox是什麼
從代碼角度來看,Equinox其實就是OSGi核心標準的完整實現,並且還在這個基礎上增加了一些額外的功能(比如為框架增加了命令行和程式執行的入口)。我們在之前入門篇講解的東西其實都是OSGi核心標準的一小部分。其實它的核心就是一個jar包,這個jar包既能執行(作為標準Java包的特性),也是一個bundle(Manifest裡面含有OSGi bundle特有的元數據)。
現在你需要知道的就是,我們能夠利用Equinox項目的代碼來運行一個實實在在的OSGi框架,框架啟動後,你就可以將你開發好bundle放到裡面運行。
1.2下載Equinox
Equinox在Eclipse官方網站上有下載,裡面列出了各個版本供我們選擇:
在這裡,我們使用3.7版本的Equinox,下載好以後放在一個單獨的資料夾下(這裡我的路徑是E:\OSGi framework\equinox)
1.3 從命令行啟動框架
如果啟動這個框架的話,有了上面的jar包就足夠了,我們進入命令行輸入如下命令:java –jar org.eclipse.osgi_3.7.0.v20110613.jar -console ,然後就會進入Equinox的控制台:
如果出現osgi>的提示符,就說明啟動成功了。
Equinox的控制台的部分基本命令如下(區分大小寫):
install [URL]
將URL表示的bundle安裝到框架中
uninstall [bundleID]
將id=bundleID的bundle卸載
start [bundleID]
啟動一個bundle
stop [bundleID]
停止一個bundle
refresh [bundleID]
刷新bundle
update [bundleID]
更新bundle 的內容
ss
簡單顯示所有bundle的狀態
status
展示安裝的bundle和註冊的服務
headers [bundleID]
展示bundle 的manifest中的元數據
1.4 Eclipse建立環境
在上一節中大家看到啟動和控制框架的方法,是相當簡單的一個過程。不過單單只是運行環境還不夠,我們還需要開發環境。
1.4.1 設定
首先我們不需要安裝必須的外掛程式,只要你有較新版本的Eclipse就行了。然後進入Eclipse的window->preferences界面,選中Plug-in Development下的Target Platform
現在右邊只有一個Runing Platform的,我們任務是點擊“Add…”按鈕來增加一個我們自己的的platform,進入如下界面:
選擇默認的第一個就好,點擊next。
再點擊這裡的“Add…”:
選擇“Directory”:
選擇你的Equinox的jar包所在的路徑,然後點擊finish,回到剛才的界面:
這時候你就會發現裡面多出來了你剛剛設定的路徑,路徑後面描述的“1 plug-ins available”則就是說的我們放置的Equinox的jar包。
繼續點擊finish,回到最開始的界面
這時候多出來了一個新的target platform,勾選上,然後確定。
1.4.2 啟動
打開選單項Run->Run configurations…,在OSGi Framework項中,新建一個Runconfiguration:
這裡面現在已經自動包含了Equinox的jar包了,點擊Run,看看運行的效果:
Eclipse的控制台中也出現來了osgi的提示符,說明你已經成功啟動了。
你可以試試剛才講的那些命令,看看能輸出些什麼(比如上圖中我輸入了ss)。
1.4.3 新建一個project
打開新建project的界面,選擇Plug-in Project:
然後輸入project的名字,TargetPlatform處選擇an OSGi framework->Equinox或者standard都行,點擊下一步:
這裡實際上是對bundle的Manifest檔案的設定,其中的ID就是Bundle-SymbolicName,Version就是bundle的版本號,下面還能決定是否定義BundleActivator,點擊finish就創建了一個project
至此,開發環境已經建立完畢(這個project只是為了演示怎么建立,不會在接下來的內容中用到,可刪之)。
2 HelloWorld代碼
現在可謂是萬事具備,只欠Helloworld了。為了將OSGi框架的三個層次都涵蓋到,這個Helloworld可能會比其他你見到的OSGi Helloworld程式要複雜一點點。如果對代碼中的一些API感到生疏,記得回到之前的入門篇中找到對應的內容,這樣對你理解代碼會有幫助。裡面的關鍵代碼已經用黃色高亮顯示。(出於篇幅考慮,代碼中的import語句都省略)
2.1 HelloWorld的定義與實現
首先我們創建一個工程org.serc.helloworld,在這個工程裡面,我們創建一個包含sayHello方法的接口,準備作為服務接口:
package org.serc.helloworld;public interface Hello { void sayHello();} 然後,對這個接口進行實現:
package org.serc.helloworld.impl;public class HelloImpl implements Hello{ final String helloString; public HelloImpl(String helloString){ this.helloString= helloString; } public void sayHello(){ System.out.println(this.helloString); }} 這個類實現的sayHello所做的工作就是輸出一個在對象構造的時候得到的helloString 字元串。
為了將這個接口暴露出來,我們需要在MANIFEST檔案中加入如下條目:
Export-Package: org.serc.helloworld;version="1.0" 接下來,為了把這個服務註冊到框架中,我們定義了一個Activator:
package org.serc.helloworld.activator;public class Activator implements BundleActivator { private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>(); public void start(BundleContext ctx) { registrations.add(ctx.registerService(Hello.class.getName(),new HelloImpl("Hello, OSGi"), null)); } public void stop(BundleContext ctx) { for(ServiceRegistration registration : registrations) { System.out.println("unregistering:"+ registration); registration.unregister(); }} 我們為這個HelloImpl傳入了"Hello, OSGi"的字元串
為了讓這個Activator能夠工作,需要在MANIFEST檔案中做如下定義:
Bundle-Activator:org.serc.helloworld.activator.Activator 這個bundle 最終的MANIFEST內容如下:
Bundle-ManifestVersion:2Bundle-SymbolicName:org.serc.helloworldBundle-Version:1.0Bundle-Activator:org.serc.helloworld.activator.ActivatorImport-Package:org.osgi.frameworkExport-Package: org.serc.helloworld;version="1.0" 2.2 獲得並執行SayHello服務
創建一個工程org.serc.helloworld.client,創建一個叫HelloUser的BundleActivator,其中的start方法會獲得接口為Hello的服務對象,並且通過這個對象來調用sayHello方法:
package org.serc.helloworld.client;public class HelloUser implements BundleActivator { public void start(BundleContext ctx) { ServiceReference ref = ctx.getServiceReference(Hello.class.getName()); if(ref != null) { Hello hello = null; try{ hello= (Hello) ctx.getService(ref); if(hello != null) hello.sayHello(); else System.out.println("Service:Hello---objectnull"); }catch (RuntimeException e) { e.printStackTrace(); }finally { ctx.ungetService(ref); hello= null; } }else { System.out.println("Service:Hello---notexists"); } } public void stop(BundleContext ctx) throws Exception { }} 為了獲得Hello這個接口的定義,我們還需要在MANIFEST檔案中import Hello所在的package:
Bundle-ManifestVersion:2Bundle-SymbolicName:org.serc.helloworld.clientBundle-Version:1.0Bundle-Activator:org.serc.helloworld.client.HelloUserImport-Package:org.serc.helloworld;version="[1.0,2.0)",org.osgi.framework 2.3 HelloWorld程式的流程
可能光看代碼會比較不容易看清楚程式的執行流程,下圖表示了這幾個類的各個功能的相互依賴關係,整個關係從Hello接口的定義開始;然後到Hello接口被實現,得到HelloImpl;再到Activator將HelloImpl註冊為框架中的一個服務,再到HelloUser通過與框架互動得到剛才註冊的服務,並且使用這個服務從而輸出字元串;最後一個可選流程是當我們stop org.serc.helloworld這個bundle的時候,程式會將之前註冊的服務註銷掉。
2.4 程式的執行
通過上面的工作,我們得到了兩個自己定義的bundle:
org.serc.helloworld
org.serc.helloworld.client
現在打開Run configurations界面,我們會看見Bundles標籤裡面多出來了這兩個bundle:
也就是說,OSGi框架在啟動的時候,會自動install和start這2個bundle。
2.5 利用命令行查看程式執行過程中框架狀態的變化
2.4其實只給出了一個結果,如果你還不太清楚這個結果具體是怎么出來的,那么這一節的內容應該能夠幫助你更好的理解輸出結果的過程。下面我們通過Equinox的一些命令行來一步一步安裝和執行bundle,並且查看過程中框架的狀態變化,來讓你們搞清楚這個結果是怎么來的。
首先在Run configuration中取消兩個helloworld bundle的自動啟動:
然後點擊Run,這時候就不會立即輸出Hello, OSGi字元串了,現在我們先用“ss”命令查看bundle 的狀態:
可見兩個bundle並不是出於ACTIVE狀態,說明並沒有啟動,現在我們執行“start 8”來啟動org.serc.helloworld這個bundle
在用services命令查看當前已經註冊的服務,我們會看到一大堆的系統服務中多出來如下一項服務
這顯然是我們在start以後註冊上去的,但是現在還沒有任何一個bundle在使用這個服務。
接下來我們start 9號bundle,也就是我們用來調用服務的bundle
這時就輸出了“Hello, OSGi”的字元串。
那么如果我們先啟動9號bundle而不啟動8號bundle會怎么樣呢?大家可以試一試,因為我們在代碼中已經對沒有服務的異常情況做了處理,屆時會有相應的輸出。
我們先停止8號bundle
大家可以看見剛才註冊的服務已經被註銷了,現在我們執行refresh 11(也就是剛才的9號bundle)來重新執行其中BundleActivator的start方法
可見Hello服務已經不復存在了。從這裡我們可以看出來,其實Bundle的啟動順序也是一個需要注意的環節,有時候你所定義的bundle是具有順序敏感性的,必須要某些前置bundle啟動了以後,後面的bundle才能正確啟動。
3 小結
這篇文檔是入門篇的最後一章了,希望讀者在花時間看完這4篇文檔並且動手實踐後能夠有所收穫,並且對OSGi框架的工作原理及其優勢能有一個比較清晰的了解和認識。
相關書籍 1.《深入理解OSGi:Equinox原理、套用與最佳實踐》
深入理解OSGi 作者:周志明 & 謝小明,出版時間:2013-2-1.
內容簡介:本書共14章,分4個部分。第一部分(第1章):走近OSGi,主要介紹了什麼是OSGi以及為什麼要使用OSGi。第二部分(第2~4章):OSGi規範與原理,對最新的OSGi R5.0中的核心規範進行了全面的解讀,首先講解了OSGi模組的建立、描述、依賴關係的處理,然後講解了Bundle的啟動原理和調度管理,最後講解了與本地及遠程服務相關的內容。第三部分:OSGi服務與Equinox套用實踐(第5~11章),不僅詳細講解了OSGi服務綱要規範和企業級規範中最常用的幾個子規範和服務的技術細節,還通過一個基於Equinox的BBS案例演示了Equinox的使用方法,最重要的是還通過源碼分析了Equinox關鍵功能的實現機制和原理。第四部分:最佳實踐(第12~14章),總結了大量關於OSGi的最佳實踐,包括從Bundle如何命名、模組劃分、依賴關係處理到保持OSGi動態性、管理程式啟動順序、使用API基線管理模組版本等各方面的實踐技巧,此外還介紹了Spring DM的原理以及如何在OSGi環節中進行程式測試。
2.《Eclipse RCP與Spring OSGi:技術詳解與最佳實踐》
Eclipse RCP與Spring OSGi 作者:陸陽,出版時間:2013-1-1.
內容簡介:全書共分3個部分:基礎篇(第1~5章)詳細介紹了與Eclipse RCP相關的一系列核心概念、Eclipse RCP開發環境的搭建,以及SWT、JFace、Forms、Nebula和WindowBuilder等Eclipse RCP開發所常用的界面編程技術;高級篇(第6~12章)系統講解了Eclipse RCP套用開發的基礎知識、Eclipse RCP軟體產品各個組成部分的構建方法,以及Eclipse RCP擴展的使用和擴展點的開發,掌握這些技術知識的讀者將能構建一個結構完整的Eclipse RCP軟體,並解決軟體開發過程中遇到的故障;實戰篇(第13~15章)詳細講解了Eclipse RCP與Spring OSGi框架、Hibernate ORM框架、JPA規範、Maven工具的整合,以及它與Java的模組化設計。
3.osgi中文社區
簡介:由北京大學軟體工程國家工程研究中心“軟體協同研發支撐技術”組維護的一個模組化動態化開發技術的公益社區。初學者、進階者和大牛都可以在這裡分享技術和經驗,自由的交流和學習,在貢獻和收穫中充實自己。為更多的學生、研究者和從業者提供更廣闊的交流平台。