OSGi pluginek fejlesztésére Eclipse IDE-t választottam, mely amellett, hogy ismeri a MANIFES.MF állomány függőségeit kezelni, rendelkezik beépített OSGi konténerrel (Equinox) is. Szerencsére a hazai bloggerek már több ízben is feszegették a témát, ennek köszönhetően pl. a jTechnics hasábjain olvashatunk a különféle konténer-implementációkról.
Megvalósítandó feladatként készítsünk egy bundlet, amely kiajánl egy szolgáltatást, amit egy másik bundeből meghívunk.
A szerviz:
- Első lépésként hozzunk létre az Eclipsben egy "Plug-in Project"-et service néven, és az "on OSGi framework" opciónál állítsuk be az Equinox-ot.
- Hozzuk létre a service.SimpleService interfészt:
package service; public interface SimpleService { public void echo(Object message); }
- Majd hozzuk létre service.SimpleServiceImpl néven az szolgáltatás implementációját:
package service; public class SimpleServiceImpl implements SimpleService { @Override public void echo(Object message) { System.out.println(message); } }
- Szerkesszük a service.Activator osztályt a következőre:
package service; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; public class Activator implements BundleActivator { @Override public void start(BundleContext context) throws Exception { context.registerService(SimpleService.class.getName(), new SimpleServiceImpl(), new java.util.Hashtable()); //Check service ServiceTracker serviceTracker = new ServiceTracker(context, SimpleService.class.getName(), null); serviceTracker.open(); SimpleService simpleLogService = (SimpleService) serviceTracker.getService(); if(simpleLogService != null) System.out.println("Service started."); else System.out.println("Service init failed!"); serviceTracker.close(); } @Override public void stop(BundleContext context) throws Exception { System.out.println("Service stopped."); } }
- A META-INF/MANIFEST.MF állomány szerkesztésével készíthetjük fel bundlet a konténerrel való együttműködésre. Eclipsben az Import-Package szerkesztésével tudjuk a "Plug-in Dependencies"-eket szerkeszteni, az IDE automatikusan importálja az itt megadott csomagokat. Fontos továbbá, hogy az Export-Packagenél kiajánljuk a service csomagot, mert ellenkező esetben a kliens oldalon "class SimpleServiceImpl cannot be cast to class SimpleService" exceptiont fogunk kapni.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Service Bundle-SymbolicName: service Bundle-Version: 1.0.0 Bundle-Activator: service.Activator Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: org.osgi.framework;version="1.3.0",org.osgi.util.tracker;version="1.3.1" Export-Package: service;version="1.0.0"
- A MANIFEST.MF állomány->jobbklikk->Run As->Run Configuration ablakban tudjuk beállítani a konténert a teszteléshez.
- Fordításhoz vagy írunk saját ant/maven scriptet, vagy Exportáljuk a bundlet JAR formájában. Szeretném megjegyezni, hogy az Eclipse alapértelmezetten generálja a MANIFEST.MF állományt a jar-ba, viszont ebben az esetben a konténer nem fog tudni mit kezdeni a bundleval, ezért a "Use existing manifest from workspace" opció alatt (utolsó lépés) adjuk meg a megfelelő állományt.
A kliens:
- Ismét egy hasonlóan paraméterezett "Plug-in Projct"-re lesz szükségünk, de ezúttal caller néven.
- A caller.Activate osztályt hozzuk az alábbi formára:
package caller; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import service.SimpleService; public class Activator implements BundleActivator { @Override public void start(BundleContext context) throws Exception { //Another way to connect a service. ServiceReference reference = context.getServiceReference(SimpleService.class.getName()); SimpleService service = (SimpleService) context.getService(reference); if (service != null) service.echo("Service called."); else System.out.println("Service call error!"); } @Override public void stop(BundleContext context) throws Exception { } }
- A service.SimpleService interfészt mindenképpen tegyük elérhetővé ebben a projektben, az egyszerűség kedvéért hozzuk létre az objektumot.
- A META-INF/MANIFEST.MF állomány tartalma legyen a következő:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Caller Bundle-SymbolicName: caller Bundle-Version: 1.0.0 Bundle-Activator: caller.Activator Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: org.osgi.framework;version="1.3.0",service;version="1.0.0"
Fontos, hogy a szervizben exportált service csomagot itt importáljuk. - A szerviznél leírtakkal azonos módon tesztelhetjük és fordíthatjuk a bundlet (nekem voltak problémák, amikor az IDE-ből próbáltam tesztelni a két bundlet együtt).
A konténer:
Utolsó egységként a konténert kell megismernünk, melynek vezérlése eltérő a különböző implementációk esetén. Az Equinoxot választva az alábbiakat kell tennünk.- Szerezzük be a nekünk megfelelő verziót, jelenleg a 3.6-os a legfrissebb.
- A `java -jar org.eclipse.osgi_3.6.0(.*?).jar -console` parancs futtatásával indíthatjuk el a konténert.
- Installáljuk a bundlekat:
osgi> install file:///path/to/file/service.jar Bundle id is 6 osgi> install file:///path/to/file/caller.jar Bundle id is 7
Eltávolítani az uninstall paranccsal, frissíteni pedig az updatetel lehet. - Az ss paranccsal lekérdezhetjük a telepített bundlek állapotát, mindkettőnek INSTALLED állapotban kell lennie:
osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.6.0.v20100517 6 INSTALLED service_1.0.0.qualifier 7 INSTALLED caller_1.0.0.qualifier
- Utolsó lépésként indítsuk el mindkét bundlet:
osgi> start service Service started. osgi> start caller Service called.
A bundle neve helyett használhatjuk az azonosítóját. - A stop paranccsal állítjuk le a bundlekat:
osgi> stop service Service stopped.