OSGi Aplikační programování v Javě (BI-APJ) - 6 Ing. Jiří Daněček Katedra softwarového inženýrství Fakulta informačních technologií ČVUT Praha Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti
Úvod OSGi (The Open Services Gateway Initiative) definuje architekturu pro vývoj a deplojment modulárních aplikací a knihoven.
Vlastnosti OSGi OSGi umožňuje rozdělit aplikaci do mnoha modulů, a tak jednoduše udržovat jejich vzájemné závislosti. Vývoj v OSGi znamená: vyvíjet jednotlivé aplikace s použitím OSGi API, deployovat aplikaci (tj. množinu modulů) do OSGi kontejneru. OSGi specifikace definuje: množinu služeb, které musí implementovat OSGi kontejner, kontrakt mezi kontejnerem a aplikací. Z pohledu vývojáře má OSGi následující výhody: moduly mohou být instalovány, deinstalovány, startovány a ukončovány dynamicky bez restartu kontejneru, aplikace může obsahovat více verzí jednoho modulu současně.
Open source OSGi kontejnery Aplikace, která splňuje specifikaci OSGi může být deployována s libovolným OSGi kontejnerem. V současnosti existují populární open source OSGi kontejenery: Equinox (referenční implementace) používaná v Eclipse, Apache Felix - používaná v NetBeans, Knopflerfish.
Bundle HelloWorld V OSGi technologii se software skládá z bundlů Bundle obsahuje.class soubory a další zdroje (resource) Bundle musí vždy obsahovat: třídu aktivátor (implementaci rozhraní BundleActivator), manifest. import org.osgi.framework.bundleactivator; import org.osgi.framework.bundlecontext; public class Activator implements BundleActivator { public void start(bundlecontext context) System.out.println("Hello world"); public void stop(bundlecontext context) Sytem.out.println("Goodbye World");
Bundle aktivátor Třída implementující BundleActivator obsahuje public konstruktor bez parametrů, metodu start, kterou volá kontejner při startu aplikace (využívá se např. pro vytvoření spojení s databází). Parametrem metody je objekt BundleContext, který umožňuje komunikaci mezi bundlem a kontejenrem metoda stop, volaná kontejnerem při uzavírání bundlu.
Bundle manifest Kvalifikované jméno aktivátoru je uvedeno v manifestu (souboru MANIFEST.MF): Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: HelloWorld Plug-in Bundle-SymbolicName: com.javaworld.sample.helloworld Bundle-Version: 1.0.0 Bundle-Activator: com.javaworld.sample.helloworld.activator Bundle-Vendor: JAVAWORLD Bundle-Localization: plugin Import-Package: org.osgi.framework;version="1.3.0"
Příklad HelloService HelloService.java package com.javaworld.sample.service; public interface HelloService { public String sayhello(); HelloServiceImpl.java package com.javaworld.sample.service.impl; public class HelloServiceImpl implements HelloService { public String sayhello() { System.out.println( "InsideHelloServiceImpl.sayHello()"); return "Say Hello";
Viditelnost Manifest bundlu HelloWorld:... Import-Package: com.javaworld.sample.service; org.osgi.framework Export-Package: com.javaworld.sample.service Manifest bundlu HelloService:... Import-Package: org.osgi.framework Export-Package: com.javaworld.sample.service Import-Package resp. Export-Package je seznam balíků, které chce bundle importovat resp. exportovat. Rozhraní HelloService je nyní přístupné, avšak ne jeho implementace HelloServiceImpl (třída HelloServiceImpl je interní třída bundle HelloService, ke které nemá přístup žádný jiný bundle).
Viditelnost tříd OSGi kontejner zakrývá interní třídy bundlu, protože každý bundle má svůj vlastní zavaděč tříd. Bundle má přístup ke třídám: zaváděným primárním (bootstrap) zavaděčem (balíky java.*), třídám OSGi frameworku (existuje pro ně speciální zavaděč), třídám v JAR souboru bundlu, třídám v importovaných balících. Omezená viditelnost tříd umožňuje bezpečně měnit implementace interních tříd (např. HelloServiceImpl).
Service-oriented aplikace Bundle může exportovat službu, která může být konzumována jiným bundlem nezávislým na implemetaci služby. Poskytovatel služby registruje jako službu POJO (Plain Old Java Object), který implemetuje rozhraní služby. Konzument vyhledává službu podle implementovaných rozhraní. Registrace služby se provádí metodou BundleContext. registerservice jejíž parametry jsou: jméno rozhraní, skutečný objekt implementující službu, vlastnosti (properties) služby - slouží např. k rozlišení stejných služeb (tj. služeb se stejným rozhraním) poskytovaných více bundly
HelloServiceActivator public class HelloServiceActivator implements BundleActivator { ServiceRegistration helloserviceregistration; public void start(bundlecontext context) HelloService helloservice = new HelloServiceImpl(); helloserviceregistration = context.registerservice( HelloService.class.getName(), helloservice, null); // žádné vlastnosti public void stop(bundlecontext context) helloserviceregistration.unregister();
Import služby public class Activator implements BundleActivator { ServiceReference helloservicereference; public void start(bundlecontext context) helloservicereference = context.getservicereference( HelloService.class.getName()); HelloService helloservice = (HelloService)context.getService( helloservicereference); System.out.println(helloService.sayHello()); public void stop(bundlecontext context) context.ungetservice(helloservicereference);
Sledování služeb Registrace a deregistrace služeb může být sledována pomocí následníka třídy ServiceTracker, která obsahuje methody: addingservice - volána při registraci služby, removedservice -volána při deregistraci služby. Konstruktor třídy musí předat jméno rozhraní služby konstruktoru nadtřídy: public class HelloServiceTracker extends ServiceTracker { public HelloServiceTracker(BundleContext context) { super(context, HelloService.class.getName(), null); public Object addingservice( ServiceReference reference){... return super.addingservice(reference);
Aktivátor používající ServiceTracker public class Activator implements BundleActivator { HelloServiceTracker helloservicetracker; public void start(bundlecontext context) helloservicetracker = new HelloServiceTracker(context); helloservicetracker.open(); HelloService helloservice = (HelloService)helloServiceTracker.getService(); System.out.println(helloService.sayHello()); public void stop(bundlecontext context) helloservicetracker.close();