1. 簡介
本章規範描述了在OSGi服務平臺下,如何實現管理代理對啓動和停止bundle的順序進行控制。啓動級別服務給每一個bundle分配一個啓動級別(start level)。管理代理可以修改bundle的啓動級別,並通過設置框架激活啓動級別(active start level)來啓動和停止相關的bundle。只有啓動級別小於或者等於激活啓動級別的bundle纔可以激活。
啓動級別服務的目的在於允許管理代理對啓動和停止bundle時進行控制。
1.1. 要點
l 排序(Ordering)– 管理代理可以對啓動和關閉bundle的順序進行排序。
l 級別(Levels) – 管理代理應該支持虛擬的無限制的級別。
l 向後兼容(Backward compatible) – 啓動級別應該和OSGi的R2規範兼容。
1.2. 名詞
l 啓動級別服務(Start Level Service) – 管理代理使用的一種服務,用於對啓動和停止bundle的順序進行排序。
l 管理代理(Management Agent) – 參閱管理代理。
l 框架事件(Framework Event)– 參閱框架事件。
l 框架監聽器(Framework Listener) – 參閱框架監聽器。
1.2. 啓動級別服務
啓動級別服務提供了以下功能:
l 對OSGi框架的開始啓動級別進行控制。
l 用於對框架的激活啓動級別進行修改。
l 可以用於給bundle分配指定的啓動級別。
l 初始化最新安裝的bundle的啓動級別。
對於bundle的啓動和停止順序的定義用於以下情況:
l 安全模式(Safe mode) – 管理代理可以實現安全模式,在這種模式下,這能啓動完全信任的bundle,如果bundle在啓動時失敗並導致了正常操作的破壞而且阻止了對問題的修正,那麼在這種情況下,是有必要使用安全模式的。
l 啓動快照(Splash screen) – 如果整個啓動時間很長,那麼就最好能在安裝過程中顯示啓動快照。這樣有助於用戶對設備安裝時間的把握。啓動排序要確保首先啓動正確的bundle。
l 處理不穩定bundle(Handling erratic bundle) – 由於bundle在激活的時候需要服務是可用的(這是一個編程錯誤),這樣會產生一些問題。通過對啓動順序的控制,管理代理就可以預防這些問題。
l 高優先級bundle(High priority bundle) – 有些任務如測量等是需要儘快啓動的,而不能有長時間的等待,可以首先啓動這些bundle。
2.1. 啓動級別的概念
啓動級別是一個非負的整數。啓動級別爲0表示框架還沒有運行或者框架已經關閉(根據環境來確定這兩種狀態)。在這種爲0狀態下,沒有bundle正在運行。增長的更大的整數代表了更高的啓動級別。例如,啓動級別2要大於1。框架必須支持int類型的所有整數的啓動級別(最大值爲Integer.MAX_VALUE)。
框架有激活啓動級別(active start level),用於確定可以啓動哪些bundle。所有的bundle都有一個bundle啓動級別(bundle start level)。這是指啓動bundle的最小啓動級別。可以通過方法setBundletartLevel(Bundle,int)來設置bundle的啓動級別。當安裝完bundle之後,最初分配的啓動級別是調用方法getInitialBundletartLevel()的返回值。這個對bundle安裝時進行啓動級別初始化的值可以通過方法setInitialBundletartLevel(int)來設置。
另外,可以通過Bundle的start和stop方法來持久標記bundle爲started或者stopped。除非標記bundle爲started,否則bundle就不會運行,而不考慮bundle的啓動級別。
2.2. 修改激活啓動級別
管理代理可以通過方法setStartLevel(int)來設置激活啓動級別。框架通過加減1來設置激活啓動級別的值直到達到了設定值。通過方法setStartLevel(int)來進行啓動或者停止bundle的過程必須是異步進行的。
這也就是說必須要將激活啓動級別(特定時候激活)修改爲一個新的啓動級別,稱之爲請求啓動級別(requested start level)。在框架啓動或者停止某些bundle的一個特定期間,激活和請求的級別是不同的。從激活啓動級別轉變爲請求啓動級別時是通過遞增1完成的。
如果請求啓動級別要大於激活啓動級別,那麼框架就將啓動級別加1,並且啓動所有滿足以下條件的bundle:
l bundle持久標記爲started,並且
l bundle的啓動級別等於新的激活啓動級別。
框架繼續增加激活啓動級別的值,並且啓動符合條件的bundle,直到啓動了所有啓動級別和請求啓動級別的值相同的bundle。
直到所有的啓動bundle都從BundleActivator.start方法中返回纔可以繼續增加激活啓動級別到下一個值,返回可以是正常返回或者是拋出了異常。如果拋出了異常,則框架必須廣播一個FrameworkEvent.ERROR事件。
如果請求啓動級別要比激活啓動級別小,那麼框架必須要停止所有的啓動級別等於激活值的bundle。然後框架必須要將激活值減1。如果激活值還是小於請求值,那麼繼續停止符合條件的bundle並繼續遞減激活值直到激活值等於請求值。如果在調用BundleActivator.stop方法來停止bundle的過程中拋出了異常,那麼框架必須要廣播一個FrameworkEvent.ERROR事件。
如果請求值等於激活值,那麼框架不會停止或者啓動任何bundle。
當達到了請求值之後,而且所有符合條件(bundle啓動級別<=激活啓動級別)的bundle都已經啓動,然後框架將FrameworkEvent.STARTLEVEL_CHANGED事件發送給所有註冊了框架監聽器(FrameworkListener)的對象。如果請求值和激活值相等,那麼,這個事件到達時間也許要比方法setStartLevel返回的時間更早。
因此,以下情況必須爲真:
l 如果bundle啓動級別小於或者等於激活值,那麼這個bundle是啓動完成了,或者即將啓動的。
l 如果bundle啓動級別要大於激活值,那麼這個bundle是已經停止了,或者即將停止的。
下圖描述了這樣的一個過程:
如果框架還沒有完成上次對激活值的修改,那麼在將它設置爲新的激活值之前,它必須要完成上次的設置。例如,激活值是5,框架的請求值爲3。在達到3之前,另外一個請求要把激活值修改爲7。在這種情況下,OSGi框架必須要先完成將激活值修改爲3,然後再將其修改爲7。
1.2.3. 啓動順序
在啓動時,框架的激活啓動級別必須是0。然後再將激活值轉換到初始啓動級別(beginning start level)。初始值可以來自於啓動框架時輸入的參數,或者通過其他未定義途徑。如果沒有給定初始值,框架默認初始值爲1。
框架運行後將請求值設置爲初始值。然後根據前文中修改激活啓動級別一節所描述那樣,使得激活啓動級別的值等於初始啓動級別的值,如果在轉換過程中拋出異常,則發出事件FrameworkEvent.START_LEVEL_CHANGED。在運行過程中,當到達了初始的啓動級別後,框架必須廣播FrameworkEvent.STARTED事件。
1.2.4. 關閉順序
當關閉框架時,請求啓動級別必須要設置爲0。根據上文中修改激活啓動級別一節所描述的那樣將激活啓動級別設置爲0。
1.2.5. 修改bundle的啓動級別
當bundle安裝完畢之後,分配給bundle一個初始的啓動級別。默認的初始值爲1,可以通過setInitialBundletartLevel(int)方法對這個值進行修改。當方法setInitialBundletartLevel(int)修改了默認的初始值之後就不能再進行修改了。
安裝之後,bundle的啓動級別可以通過setBundletartLevel(Bundle,int)方法來修改。如果修改了bundle的啓動級別,而且bundle持久標記爲started,那麼OSGi框架必須要將新的bundle啓動級別和框架的激活值相比較。例如,假設激活值爲5,啓動級別爲5的bundle標記爲started,如果將bundle的啓動級別修改爲6,那麼框架必須要將bundle停止,而且bundle的持久標記依然爲started。
1.2.6. 啓動bundle
如果通過Bundle.start方法來啓動bundle,那麼OSGi必須要持久標記bundle爲started。如果框架的激活值小於bundle的啓動級別,OSGi框架並不會實際啓動bundle,在這種情況下,bundle的狀態不會改變。
1.2.7. BundleActivator中的異常
如果BundleActivator中的start或者stop方法拋出了異常,那麼對異常的處理根據不同的方法調用者而不同。
如果bundle的啓動或者停止是由於框架的激活啓動級別值的修改或者是bundle的啓動級別改變而引起的,那麼必須要將異常封裝成一個BundleException並作爲FrameworkEvent的一個錯誤事件:FrameworkEvent.ERROR廣播。
否則,創建一個包含了這個異常的新的BundleException,並將這個BundleException拋給調用者。
1.2.8. 系統bundle
System Bundle的啓動級別爲0。System Bundle的啓動級別是不能修改的。如果試圖修改System Bundle的啓動級別,那麼會拋出一個IllegalArgumentException異常。
1.3. 兼容模式
兼容模式需要完成所有bundle的啓動級別的一致性。所有bundle分配的啓動級別爲1。在兼容模式下,OSGi框架可以在啓動時輸入參數來指定初始的啓動級別爲1。然後框架啓動所有持久標記爲started的bundle。那麼當到達啓動級別1,框架也就啓動了所有的bundle,然後框架發出事件FrameworkEvent.STARTED。這樣由於啓動了所有的bundle而沒有進行任何啓動控制,就和OSGi框架原來的標準兼容了。OSGi框架的實現必須要支持兼容模式。