Comprendi i concetti di OSGi. Prova a seguire l'approccio puzzle
Pubblicato: 2013-04-20OSGi
è diventato molto popolare oggi, grazie al suo approccio alla modularità e alla sua capacità di imporre confini logici tra i moduli. Quando lo scopriamo per la prima volta, la domanda è da dove cominciare per capire come funziona?
Per comprendere i concetti di OSGi cercheremo di seguire l'approccio puzzle, l'idea è di iniziare con la parte banale di questa tecnologia, e cercare altre parti relative a quelle trovate. E per assemblare il puzzle saremo assistiti da JArchitect che sarà d'aiuto per rilevare il design interno di OSGi.
Per essere più concreti analizziamo con JArchitect un'applicazione che utilizza la tecnologia OSGi, si tratta del famoso IDE eclipse che utilizza il contenitore OSGi equinozio.
Iniziamo con la tipica definizione OSGi:
OSGi riduce la complessità fornendo un'architettura modulare per gli odierni sistemi distribuiti su larga scala e per piccole applicazioni embedded. Costruire sistemi da moduli interni e standard riduce significativamente la complessità e quindi le spese di sviluppo e manutenzione. Il modello di programmazione OSGi realizza la promessa dei sistemi basati su componenti.
La parte banale di OSGi è la modularità, scopriamo cosa sono i moduli OSGi?
I moduli OSGI sono chiamati Bundle e quindi ogni applicazione è costituita da almeno un bundle.
Questi bundle vengono eseguiti all'interno di un container e, poiché ogni approccio modulare in cui un container gestisce i moduli, la domanda è quale contratto deve implementare ogni modulo per essere integrato nel container?
Prendiamo come esempio il bundle org.eclipse.equinox.jsp.jasper e cerchiamo tutte le sue interfacce implementate, per questo possiamo eseguire la seguente richiesta CQLinq:
1 2 3 4 |
from t in Types where t . ParentProject . Name ==” org . eclipse . equinox . jsp . jasper_1 . 0.300.v20110502 “ let interfaces = t . InterfacesImplemented from i in interfaces select i |
Viene implementata l'interfaccia BundleActivator dal pacchetto OSGi, questa interfaccia contiene due metodi start e stop utili per personalizzare l'avvio e l'arresto del bundle.
Un'altra specificità di un bundle è il suo file manifest, ecco una parte del file manifest org.eclipse.equinox.jsp.jasper:
1 2 3 4 5 6 7 8 9 |
Manifest - Version : 1.0 Bundle - Localization : plugin Bundle - RequiredExecutionEnvironment : CDC - 1.0 / Foundation - 1.0 , J2SE - 1.3 Bundle - SymbolicName : org . eclipse . equinox . jsp . jasper Eclipse - LazyStart : true Eclipse - SourceReferences : scm : cvs : pserver : dev . eclipse . org : / cvsroot / rt : org . eclipse . equinox / server - side / bundles / org . eclipse . equinox . jsp . jaspe r ; tag = v20110502 Bundle - Activator : org . eclipse . equinox . internal . jsp . jasper . Activator |
Come possiamo osservare, questo manifest contiene alcune meta informazioni necessarie al contenitore, come specificare la classe dell'attivatore di bundle che implementa l'interfaccia BundleActivator.
Il bundle rappresenta il nostro primo pezzo del puzzle, ed ecco una rappresentazione semplificata di un bundle:
E solo per avere un'idea di tutti i bundle utilizzati da eclipse, cerchiamo tutte le classi che implementano l'interfaccia BundleActivator.
1 2 |
from t in Types where t . Implement ( “ org . osgi . framework . BundleActivator “ ) select new { t , t . NbBCInstructions } |
Chi gestisce il bundle e invoca i metodi BundleActivator?
Per scoprirlo cerchiamo metodi che invocano direttamente o indirettamente BundleActivator.start
1 2 3 4 |
from m in Methods let depth0 = m . DepthOfIsUsing ( “ org . eclipse . osgi . framework . internal . core . BundleContextImpl . startActivator ( BundleActivator ) “ ) where depth0 > = 0 orderby depth0 select new { m , depth0 } |
Il bundle viene attivato dal framework OSGi quando viene lanciato. La classe framework viene avviata dal contenitore equinox. E per capire meglio cosa succede all'avvio del container, ecco alcune azioni eseguite all'avvio del container:
Quando il contenitore viene avviato, inizializza il framework OSGi, il framework ottiene tutti i bundle installati e per ognuno crea un'istanza della classe BundleHost e archivia in un repository il bundle trovato.
La classe BundleHost implementa l'interfaccia Bundle, che contiene metodi come start, stop, disinstallazione e aggiornamento, questi metodi sono necessari per gestire il ciclo di vita del bundle.
Quindi il nostro secondo pezzo del puzzle è Il contenitore OSGi, lanciato da Equinoxlauncher, che inizializza il framework. La classe framework è responsabile del caricamento dei bundle e li attiva.
Dopo aver scoperto alcuni concetti di base su bundle e container, approfondiamo i bundle e scopriamo come funzionano internamente.
Prendiamo come esempio il bundle org.eclipse.equinox.http.servlet e cerchiamo i metodi invocati dal metodo start della sua classe Activator.
Questo pacchetto crea un servizio e lo registra nel contenitore. Un servizio in OSGi è definito da una classe o interfaccia Java standard. Tipicamente viene utilizzata un'interfaccia Java per definire l'interfaccia di servizio. Il servizio è il metodo preferito che i bundle dovrebbero utilizzare per comunicare tra loro.

Di seguito sono riportati alcuni utili scenari di utilizzo dei servizi:
- Esporta funzionalità da un bundle ad altri bundle.
- Importa funzionalità da altri bundle.
- Registra i listener per gli eventi di altri bundle.
Un'altra osservazione dal grafico delle dipendenze precedente è che viene utilizzata una service factory per creare l'istanza del servizio.
Il nostro terzo pezzo del puzzle è il livello dei servizi OSGi, ogni bundle può utilizzare o dichiarare alcuni servizi, ciò che impone l'approccio alla progettazione dei componenti, ecco la nuova rappresentazione del bundle OSGi.
Se il bundle utilizza i servizi per comunicare con altri bundle, come comunica con altri jar?
Se sviluppiamo un bundle e proviamo a usare una classe da un altro jar, possiamo essere sorpresi che non funzionerà come previsto, il motivo è che ClassLoader è agganciato dal contenitore OSGi, per verificare che cerchiamo quale metodo invoca java. lang.Thread.setContextClassLoader.
1 2 |
from m in Methods where m . IsUsing ( “ java . lang . Thread . setContextClassLoader ( ClassLoader ) “ ) select new { m , m . NbBCInstructions } |
Molti metodi lo invocano incluso EquinoxLauncher. quindi ogni volta che il bundle tenta di creare un'istanza di classe, il contenitore OSGi verificherà se il codice è autorizzato a eseguire questa azione o meno, e qui arriva il ruolo del pacchetto importato ed esportato nel file manifest.
1 2 3 4 |
Export - Package : org . eclipse . equinox . http . servlet ; version =” 1.1.0 ″ Import - Package : javax . servlet ; version =” 2.3 ″ , javax . servlet . http ; version =” 2.3 ″ , org . osgi . framework ; version =” 1.3.0 ″ , org . osgi . service . http ; versi on =” [ 1.2 , 1.3 ) ” |
Il bundle dichiara esplicitamente il pacchetto esportato e importato e, per verificarlo, cerchiamo i pacchetti utilizzati da org.eclipse.equinox.http.servlet bundle e verifichiamo se utilizza solo il pacchetto importato.
1 2 |
from n in Packages where n . IsUsedBy ( “ org . eclipse . equinox . http . servlet_1 . 1.200.v20110502 “ ) select new { n , n . NbBCInstructions } |
Come possiamo osservare, tutti i pacchetti utilizzati sono specificati nel file manifest, nella sezione import package.
Il pacchetto esportato rappresenta comunque il pacchetto che può essere utilizzato da altri bundle. è rappresentato dall'interfaccia ExportedPackage e possiamo cercare le classi contenitore usando questa interfaccia.
1 2 |
from t in Types where t . IsUsing ( “ org . osgi . service . packageadmin . ExportedPackage “ ) select new { t , t . NbBCInstructions } |
Ciò che è interessante con questa nuova funzionalità è che il bundle avrà un confine ben definito e ciò che utilizza e ciò che espone come servizi è molto ben specificato.
Possiamo imporre il controllo dell'utilizzo dei pacchetti importati utilizzando altri strumenti, ad esempio con CQLinq possiamo scrivere delle regole che avvertono ogni volta che un progetto utilizza pacchetti diversi da quelli specificati, ma è meglio avere questo controllo nell'ambiente di esecuzione, quindi lo sviluppatore non può infrangere queste regole.
Questa capacità di trattare i pacchetti importati ed esportati è gestita dal livello dei moduli OSGi ed è stato il nostro quarto pezzo del puzzle.
Torniamo al container OSGi e scopriamo quali servizi fornisce?
Servizi di container
Come abbiamo scoperto prima che il contenitore venga lanciato dalla classe EquinoxLauncher e la classe framework inizializza e lancia i bundle, per rilevare quali servizi vengono forniti cerchiamo tutte le classi utilizzate nel metodo di inizializzazione del framework.
1 2 |
from t in Types where t . IsUsedBy ( “ org . eclipse . osgi . framework . internal . core . Framework . initialize ( FrameworkAdaptor ) “ ) select new { t , t . NbBCInstructions } |
Alcune classi sono già state scoperte in precedenza come BundleRepository,BundleHost,PackageAdminImpl e ServiceRegistry.
E le altre classi:
- StartLevel Manager:
Ciascun OSGi Bundle è associato a un livello di avvio che consente al server di controllare il relativo ordine di avvio e arresto dei bundle. Devono essere attivi solo i bundle che hanno un livello iniziale inferiore o uguale al livello iniziale attivo del framework del server. Di solito, un pacchetto con un livello iniziale più piccolo tende a essere avviato prima. - Amministratore di sicurezza:
Il livello di sicurezza gestisce gli aspetti di sicurezza limitando la funzionalità del pacchetto a capacità predefinite. - Manager di eventi:
Il servizio Event Admin fornisce un modello di pubblicazione e sottoscrizione per la gestione degli eventi. È realizzato secondo la specifica OSGi Event Admin Service. Il servizio Event Admin invia eventi tra editori di eventi e sottoscrittori di eventi (gestori di eventi) interponendo un canale di eventi. Gli editori pubblicano gli eventi sul canale e il canale degli eventi definisce quali gestori devono essere notificati. Pertanto editori e gestori non hanno una conoscenza diretta l'uno dell'altro, il che semplifica la gestione degli eventi.
Il quadro completo dell'OSGi
Assembliamo tutti i pezzi del puzzle descritti prima e avremo la seguente immagine OSGi:
Questa architettura ha i seguenti interessanti vantaggi:
- Semplice.
- Complessità ridotta.
- Distribuzione facile.
- Sicuro.
Ciò che lo rende molto attraente e vale una deviazione, e non perderai tempo se lo studi in profondità.