2010. július 10., szombat

OSGi szárnypróbálgatás

Bár biztosan sokan ismeritek, de legalább hallottatok az Open Services Gateway Initiative-ról, ismertebb nevén az OSGi-ről, gondoltam én is kísérletet teszek rendszer a bemutatására. A témában kutakodva azonban találtam egy rövid írást Paller Gábor kollégától, melyben olyan érthetően megfogalmazza a technológia lényegét, hogy bizton állíthatom nekem sem sikerülne jobban, ezért meg sem próbálom. Az elméleti alapok elsajátítása után a gyakorlati megvalósításra szeretném helyezni a hangsúlyt.
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.
    

Nincsenek megjegyzések:

Megjegyzés küldése