OSGi Service Platform
OSGi Service Platform[1] (dále OSGi Framework, nebo jen Framework) je specifikace dynamického modulárního systému pro programovací jazyk Java. Standard je definován a udržován mezinárodním konsorciem OSGi Alliance (původně Open Services Gateway initiative, dnes se již nepoužívá). OSGi umožňuje instalaci a odebírání modulů za běhu, definuje životní cyklus modulu a nabízí infrastrukturu pro spolupráci modulů skrze služby. V současné době je OSGi Framework považován za nejvyspělejší modulární systém pro Javu. Zakládají se na něm například nové verze téměř všech velkých aplikačních serverů.
Historie
Historie OSGi sahá až do roku 1998, kdy započaly práce na první verzi specifikace. Původně bylo OSGi cíleno na potřeby systémů pro chytré domácnosti. Díky tomuto původnímu zaměření je OSGi Framework velmi kompaktní. Plná implementace aktuální verze R4 může být jediný jar s velikostí kolem 250KB (Knopflerfish [1]). K nárůstu pozornosti kolem OSGi došlo s úspěchem platformy Eclipse, jež ve verzi 3.0 vyměnila proprietární modulární systém za vlastní implementaci OSGi Equinox. Další oblastí, kde došlo k rozšíření OSGi, na poli aplikačních serverů – je základem nových verzí serverů WebLogic, Websphere, JBoss, GlassFish, JOnAS a dalších [2].
V poslední době se OSGi pomalu začíná využívat na poli enterprise aplikací, které jsou tradičně doménou Java EE. Zatím se jedná spíše o první pokusy, než masovou adopci OSGi Frameworku pro tvorbu enterprise aplikací. Mnoho vývojářů považuje vývojový model OSGi za příliš komplikovaný oproti Java EE a nevidí dostatek přínosů vyvažujících přidanou komplexitu zejména u malých a středních aplikací. Hlavní silou prosazující OSGi na poli enterprise aplikací se vedle dodavatelů jednotlivých implementací stala společnost SpringSource [2] stojící za úspěšným Spring Framework. V případě SpringSource dm Server není OSGi využito pouze vnitřně v infrastruktuře serveru, ale je i základní jednotkou nasazení (deploymentu).
Architektura
Podívejme se podrobněji na architekturu OSGi Frameworku a její jednotlivé vrstvy.
Execution Environment
OSGi Framework je použitelný na široké škále virtuálních strojů včetně široké škály implementací v mobilních zařízeních. Execution Environment je abstrakcí JRE definující dostupné balíčky v daném prostředí. Každý modul přitom může uvést požadovaný minimální Execution Environment (množinu Java balíčků) potřebný k jeho běhu. Modul uvádějící jako požadovaný Execution Environment J2SE-1.5 tak půjde nainstalovat například do OSGi kontejnerů běžících nad Javou 5 či Javou 6, ale ne do kontejneru běžícího nad Javou 1.4, či virtuálním strojem podporujícím pouze Javu ME.
Moduly
Jednotkou modularity v OSGi je takzvaný bundle (dále pouze modul, i když korektnější pojmenování by bylo balík, což by se ovšem pletlo s Java balíčky). Jedná se o běžný jar archiv s několika speciálními hlavičkami v manifestu. Každý modul má za běhu speciálně vytvořený zavaděč tříd (classloader), který vidí pouze Java balíčky a zdroje (obrázky, konfigurační soubory a další) definované v modulu samotném, části standardní knihovny odpovídající specifikovanému Execution Environment a balíčky z ostatních modulů, které explicitně importuje. Níže je MANIFEST.MF soubor jednoduchého modulu.
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.example.bundle
Bundle-Version: 1.0.0
Bundle-Name: Ukázkový bundle
Bundle-Activator: org.example.bundle.Activator
Export-Package: org.example.bundle.package;version="1.0.0";uses:=" org.example.utility.package"
Import-Package: org.example.utility.package;version="[1.4.0, 2.0.0)"
Require-Bundle: org.example.otherbundle;version="[1.2.1, 2.0.0)"
Význam jednotlivých hlaviček je následující:
Bundle-ManifestVersion:
Indikuje verzi OSGi hlaviček v manifestu
Bundle-SymbolicName:
Jedinečný identifikátor modulu. Měl by dodržovat konvenci pro pojmenovávání balíčků v Javě a v ideálním případě být shodný s identifikátorem balíčku nejvyšší úrovně v daném modulu. Jedná se o jedinou povinnou hlavičku.
Bundle-Version:
Verze modulu skládající se ze tří číselných částí a volitelně jedné textové. Pokud není uvedena, uvažuje se verze 0.0.0
. Každý modul by však měl specifikovat verzi.
Bundle-Name:
Člověkem čitelné pojmenování modulu například pro potřeby řídících systémů, kde se může zobrazit namísto či spolu se symbolickým jménem.
Bundle-Activator:
Třída implementující rozhraní BundleActivator
definující metody volané v rámci životního cyklu modulu. Definuje 2 metody (start a stop), ve kterých je jako parametr předáván BundleContext
– objekt umožňující interakci s okolím (vyhledávání a získávání služeb, registrace vlastních služeb, získávání informací o ostatních modulech a jejich stavech, možnost měnit stav ostatních modulů, programová instalace nových modulů a další). Aktivátor je volitelný.
Export-Package:
Jednotkou závislosti mezi moduly je jeden Java balíček. Tato hlavička říká, že balíček org.example.bundle.package
obsažený v našem ukázkovém modulu má být přístupný ostatním modulům. Dále uvádí verzi exportovaného balíčku (ta je v praxi často stejná, jako verze modulu, ale musí být u každého exportovaného balíčku explicitně uvedena). Nakonec je uvedena ještě uses direktiva, která říká, že třídy v balíčku org.example.bundle.package
využívají tříd z importovaného balíčku org.example.utility.package
. Tato direktiva zaručí, že kterýkoli jiný modul importující balíček org.example.bundle.package
a zároveň org.example.utility.package
uvidí stejnou verzi org.example.utility.package
jako tento modul. Vhodný nástroj (například Eclipse PDE) dokáže tyto direktivy automaticky počítat.
Import-Package:
Exportováním balíčku dává modul možnost ostatním modulům využívat třídy definované v daném balíčku. Aby je však ostatní jiný modul mohl využívat, musí je explicitně importovat. Tato hlavička říká, že tento ukázkový modul využívá balíček org.example.utility.package
. Pokud v OSGi kontejneru není přítomný žádný modul exportující daný balíček s potřebnou verzí, nebude možné tento modul použít. Verze je v tomto případě uvedená rozsahem (od 1.4.0
včetně do 2.0.0
vyjma). Verze 1.4.0
je zřejmě verzí, proti které byl ukázkový modul vyvíjen a jeho autor zároveň vyjadřuje důvěru, že až do verze 2.0.0
daného balíčku bude použité API zpětně kompatibilní. V případě více dostupných verzí bude pro tento modul viditelná nejvyšší verze balíčku vyhovující rozsahu a zároveň nezpůsobující konflikty s ostatními importy a omezeními.
Require-Bundle:
Zatímco importování balíčku nevytváří „tvrdou závislost“ na jakémkoliv jiném konkrétním modulu (je jedno, který modul potřebný balíček v dané verzi exportuje), tato hlavička udává přímou závislost na konkrétně uvedeném modulu. Dochází k importu všech exportovaných balíčků uvedeného modulu. Doporučuje se pokud možno používat pouze Import-Package
hlavičky, kterými lze udržet minimální rozsah závislostí a předcházet tak konfliktům.
Životní cyklus
Každý OSGi modul prochází dobře definovaným životním cyklem. Pokud není Frameworkem spravován, nachází se ve stavu uninstalled, po instalaci do Frameworku se nachází ve stavu installed a je učiněn pokus o uspokojení jeho závislostí definovaných hlavičkami Import-Package
a Require-Bundle
. Pokud jsou tyto závislosti uspokojeny, přechází modul do stavu resolved, ve kterém jsou již jím exportované balíčky dostupné ostatním modulům. Pokud závislosti modulu nelze v dané chvíli uspokojit, zůstává ve stavu installed a po instalaci dodatečných modulů může být zopakován pokus o uspokojení jeho závislostí.
Ze stavu resolved může být modul nastartován – je zavolána metoda start jeho aktivátoru, pokud nějaký definuje. Při zpracování této metody se nachází ve stavu starting. Pakliže metoda nevyhodí žádnou výjimku, dostává se do stavu active, ve kterém zůstává až do doby jeho zastavení voláním metody stop aktivátoru. O startování a zastavování modulů se může starat jednak Framework sám, jednak může být vyvoláno (stejně jako ostatní operace životního cyklu) z konzole a mohou je iniciovat ostatní moduly (pokud to umožňuje bezpečnostní nastavení).
Operace update může být použita k aktualizování modulu na vyšší verzi či zopakování procesu uspokojení závislostí, stejně jako operace refresh, která navíc způsobí update všech modulů závislých na daném refreshovaném modulu.
Služby
Importy a exporty balíčků definují statické závislosti mezi moduly. OSGi navíc nabízí vrstvu služeb, které tvoří dynamické závislosti. Služba v OSGi je libovolný POJO objekt. Může být zaregistrován kdykoliv je modul ve stavu starting nebo active. Registrace se provádí pod názvem jednoho či více rozhraní, které daný objekt implementuje, a libovolného počtu atributů klíč-hodnota pro odlišení služeb stejného typu. Po registraci může být služba nalezena ostatními moduly v registru na základě názvu rozhraní a LDAP filtru přes další atributy. Modul může zaregistrovat posluchače ServiceListener
na službu, která zatím není dostupná a čekat na její registraci.
V průběhu času mohou přicházet nové služby s tím, jak se do kontejneru instalují nové moduly. Existující služby mohou být také odregistrovány – děje se tak buď programově, nebo automaticky při zastavení modulu, který je zaregistroval. Toto dynamické chování klade specifické nároky na klientský kód, který služeb využívá. Správné použití služeb přes nativní OSGi API je poměrně složité a obnáší množství opakujícího se kódu. K dispozici je několik nadstaveb usnadňující práci se službami – například knihovna Spring Dynamic Modules [3], která umožňuje definovat pro každý modul springový aplikační kontext, deklarativně exportovat beany jako služby a importovat libovolnou službu jako Spring bean. Vytvořený bean je proxy objektem k požadované službě, umožňujícím například dynamickou záměnu za ekvivalentní službu, pokud je původní služba odregistrována.
Reference
- ↑ Archivovaná kopie. www.osgi.org [online]. [cit. 2009-05-23]. Dostupné v archivu pořízeném dne 2009-05-20.
- ↑ http://www.infoq.com/news/2008/02/osgi_jee
Externí odkazy
- Obrázky, zvuky či videa k tématu OSGi Service Platform na Wikimedia Commons