Open Services Gateway Initiative (OSGi)

Paller Gábor

1. Motiváció

A hagyományos operációs rendszerek két konstrukcióval biztosítják a kóddarabok igényeknek megfelelő elválasztását ill. mások által való hozzáférését.
Több erőfeszítés volt, hogy a Jáva tetején (egy logikai virtuális gépben) az operációs rendszerek szolgáltatásaihoz hasonló funkcionalitást hozzanak létre. Ezek közül a leginkább elfogadott megoldás az Open Service Gateway Initiative (OSGi). A legfrissebb OSGi specifikáció az R3 (Revision 3), az OSGi Alliance oldaláról (http://www.osgi.org) letölthető.

Az OSGi-t eredetileg u.n. home gateway-ek számára fejlesztették ki. A home gateway egy doboz, ami kapcsolatban áll egy szolgáltatóval és a felhasználó által előfizetett szolgáltatásokhoz ad elérést. Pl. lehetséges, hogy a home gateway a kábelszolgáltatóhoz tartozik és filmkölcsönzési (pay per view) szolgáltatásokhoz ad hozzáférést. Ekkor a gateway-en futó program lehetővé teszi filmek kiválasztását, majd a kiválasztott filmet letölti és számláz. A gateway-t tehát a szolgáltató menedzseli, ő tölti fel szoftverrel és tartalommal, a felhasználó csupán használja az általa nyújtott szolgáltatásokat. Ezen környezet követelményei miatt az OSGi elsőrangú menedzselhetőségi lehetőségeket nyújt, jobbakat, mint bármely más Jáva alkalmazásmodell. Az OSGi-t mostanában bővítik ki végfelhasználói rendszerekben (pl. Eclipse) való használatra.

2. Batyu (bundle)

Az OSGi alapegysége a batyu (bundle). A batyu különös keveréke a csomagolási formátumnak és a végrehajtási egységnek.
A batyu egy JAR fájl. A batyuban alapvetően Jáva osztályok, a Jáva osztályokból elért statikus erőforrások (pl. képek) és a Jáva osztályokból elért natív kóddarabok lehetnek. A batyuban levő dolgok értelmezéséhez az immár megszokott módon leíró kell, ez a batyu esetén a szabványos manifest fájlban (META-INF/MANIFEST.MF) van. Íme egy példa egy OSGi manifest fájlra:

Bundle-Name: Helloworld
Bundle-Version: 1.1
Bundle-Description: Hello, world
Bundle-Vendor: FreeWare
Bundle-Copyright: 'Copyleft (c) 1999-2002.'
Bundle-Activator: example.osg.HelloWorld.HelloWorld_Activator
Import-Package: org.osgi.service.cm
Export-Package: example.osg.exported_package

A Bundle-Name-től a Bundle-Copyright-ig terjedő fejlécek a batyuról tárolnak információkat. A Bundle-Activator annak az osztálynak a neve, amelyiknek metódusai meghívódnak a batyu elindítása és leállítása esetén. Ez opcionális, mint látni fogjuk, egy batyu lehet úgy is igen hasznos, hogy nem elindítható. A batyu aktivizáló osztályára lássunk egy példát!

public class HelloWorld_Activator implements org.osgi.framework.BundleActivator {
   public void start(BundleContext bc) {
...
   }

   public void stop( BundleContext bc ) {
...
   }
}


A batyu elindításakor a batyu aktivizáló osztályát példányosítja az OSGi keretrendszer, majd meghívja a start() metódust. A metódus megkap egy BundleContext egyedre mutató referenciát. A BundleContext metódusain keresztül

3. Export és Import

Az OSGi környezet egész sajátos tulajdonsága az export-import mechanizmus. Ennek segítségével a batyu kiajánlhatja a benne levő osztályokat a többi batyunak. A kiajánlás egysége a Jáva package. Tehát a batyu pl. kiajánlhatja a com.mycomp.superfeature.* osztályokat más batyuknak. A batyu nem kiajánlott osztályai direktben nem lesznek elérhetők a z importáló batyu részére, de persze a kiajánlott csomag elérhetővé teheti őket, pl. a kiajánlott csomag egy osztályának egy metódusa visszaadhat referenciát nem kiajánlott osztály egyedére.

A kiajánlást a manifest fájlban kell deklaráljuk:

Export-Package: com.mycomp.superfeature

Opcionálisan deklarálhatja a csomag verzióját is:

Export-Package: com.mycomp.superfeature; specification-version="2.1"

Az importáló batyunak is deklarálnia kell, hogy bizonyos osztályokat nem maga tartalmaz, hanem importál:

Import-Package: com.mycomp.superfeature

Ugyancsak van lehetőség a csomag verzió megadására.

Import-Package: com.mycomp.superfeature; specification-version="1.5"

Ez azt jelenti, hogy a csomagot csak olyan kiajánló batyutól szabad importálni, aki a megadott verziójú, vagy magasabb verziójú csomagot ajánlott ki. A példánk tehát elfogadja majd az előző (2.1-es) kiajánlott csomagot, mert az magasabb az általunk igényelt (1.5-ös) verziónál.

Amikor az OSGi keretrendszer a batyut installálja, végrehajtja a kiajánlások és importok feloldását. A feloldás során a Jáva osztálybetöltő mechanizmusát használja kreatívan. Minden batyuhoz saját osztálybetöltőt készít, így a batyuk egymás osztályait alaphelyzetben nem látják. Ha azonban egy batyu importál egy csomagot, akkor az importáló batyu osztálybetöltője ezen csomagba tartozó osztályok betöltését az exportáló batyu osztálybetöltőjének delegálja. Ezt úgy teszi, hogy referenciát tart a kiajánló batyu csomagbetöltőjére. Az export-import mechanizmus miatt a batyuk között függőségek jönnek létre, amiket az OSGi keretrendszer nyilvántart. Az a batyu, amelynek függőségei rendben vannak, feloldott állapotban van. Ez azt jelenti, hogy minden importált csomagját ki lehet elégíteni a rendszeren installált batyuk kiajánlott
csomagjaiból. Ha valamelyik importhoz tartozó csomag hiányzik (nem exportálja semmilyen más batyu), akkor az importáló batyu nem feloldott állapotban van. Nem feloldott batyut nem lehet használni, pl. nem lehet elindítani, nem lehet importálni tőle, stb.

3. A batyu állapotai

Az export-import mechanizmussal felvértezve immár górcső alá vehetjük a batyu állapotait.
Bundle states

4. Szolgáltatások (service)

Az export-import mechanizmus mellett az OSGi másik sajátossága a szolgáltatás (service) architektúra. A batyuk objektumokat regisztrálhatnak név alatt a szolgáltatáskatalógusban (service registry). A regisztrált objektum egy szolgáltatás interfészt kell implementáljon és az objektum ezen interfész nevén kell regisztrálva legyen. Ezt legegyszerűbb egy példán át megvizsgálni.

A következő a szolgáltatás interfész kódja:

public interface ServiceIf {
  public String message();
  public void setMessage( String message );
}


A szolgáltatás objektum implementálja ezt az interfészt:

public class ServiceObject implements ServiceIf {

  public String message() {
...
  }

  public void setMessage( String message ) {
...
  }

}


A batyu regisztrálja a szolgáltatást (tipikusan a start() metódusban)

void start( BundleContext bc ) {
...
    serviceObject = new ServiceObject();
    serviceDictionary = (Dictionary)new Hashtable();
    myServiceRegistration = bc.registerService(
                       ServiceIf.class.getName(), // szolgáltatásinterfész neve
                       (Object)serviceObject,  // maga a szolgáltatásobjektum
                       serviceDictionary ); // a szolgáltatás tulajdonságai (most üres)
...
}


A szolgáltatást ki kell regisztrálni a katalógusból, ha már nem elérhető (tipikusan a stop() metódusban).

myServiceRegistration.unregister();

Ha valaki szolgáltatást akar használni, ismernie kell a szolgáltatásinterfész nevét. Ennek alapján a BundleContext két metódusával megszerezhető a szolgáltatás objektuma, amin aztán a szolgáltatás metódusai meghívhatók.

BundleContext bc;      // Elmenthető pl. a start metódusban
ServiceReference sr = bc.getServiceReference( ServiceIf.class.getName() );
ServiceIf so = (ServiceIf)bc.getService( sr );
String s = so.message();    // Szolgáltatás hívása az interfészén keresztül


A szolgáltatások megfelelő használatához a batyuknak helyesen kell viselkedniük, különben az OSGi keretrendszer nem tud megfelelően működni. Ha egy batyu referenciát tárol egy másik batyu osztálybetöltője által definiált objektumra, az objektumhoz tartozó osztályt (és annak összes függő osztályát) az Jáva szemétgyűjtője nem tudja eltakarítani, ha a kérdéses batyut eltávolítják vagy újabb verziót installálnak. Ennek megfelelően a batyuknak figyelniük kell az általuk használt szolgáltatásokhoz tartozó batyuk életcikluseseményeire és eldobniuk minden referenciát, ha a batyut eltávolítják vagy frissítik.

5. Jáva biztonság és OSGi keretrendszer

A Jáva 2-es változata egy teljesen megújított biztonsági keretrendszert tartalmaz, ami a korábbi "homokozó" modellel gyökeres szakítást jelent. Ebben a modellben a védelmi kontextusokhoz jogosultsági (Permission) objektumokat rendelnek. A Jáva futtatórendszer nyilvántartja az aktív védelmi kontektusokat és ezen kontextusokhoz rendelt jogosultságok metszetét képezi. Ez az aktív jogosultságkészlet. A védendő kód az érzékeny műveletsorozat végrehajtása előtt ellenőrzi, az aktív jogosultságkészletben van-e olyan jogosultság, amelyik a művelethez szükséges jogosultágot implikálja. (Pontos egyezés nem szükséges, pl. FilePermission( "read,write", "<<ALL FILES>>" ) implikálja a FilePermission( "read", "myfile.txt") jogosultságot vagy pl. AllPermission implikál bármilyen jogosultságot). A biztonsági keretrendszer alapértelmezett implementációja a védelmi kontextusokat a Jáva kódbázishoz rendeli, pl. meg lehet adni a file://javalibs/speclib.jar kódbázishoz tartozó jogosultságokat. A Jáva futtatórendszer nyilvántartja, milyen kódbázisokon - védelmi zónákon - haladt át a hívás és az aktív jogosultságkészlet a hívás által végigjárt összes kódbázis jogosultságainak metszete.

Az OSGi keretrendszerre a jogosultsági rendszer kétféleképpen hat. Egyfelől a keretrendszer definiálja, milyen jogosultságok kellenek bizonyos szolgáltatások igénybevételéhez.  Pl. PackagePermission( "com.mypackage", "EXPORT" ) megengedi a batyunak, hogy a com.mypackage Jáva package-t exportálja. Egy másik példa: ServicePermission( "com.myservice", "GET") megengedi a batyunak, hogy megszerezze a com.myservice szolgáltatás interfész névvel regisztrált szolgáltatás szolgáltatásobjektumát. A paraméterek nélküli AdminPermission megengedi a batyunak, hogy meghívjon OSGi keretrendszer menedzsmentfunkcionalitást, pl. installáljon egy batyut.

Másfelől batyunként lehet beállítani a jogosultságokat, tehát nem kell a kódbázissal bíbelődni. Ez a Permission Admin szolgáltatás feladata. A Permission Admin segítségével lekérdezhetők és módosíthatók a batyuk jogosultságai. Módosításhoz persze AdminPermission kell.

6. Fontosabb OSGi szolgáltatások

Az OSGi egy tucatnyi szolgáltatást definiál. Ezeknek többsége opcionális. Az alábbiakban lássunk néhányat a fontosabbakból.