Nella prima parte del tutorial abbiamo introdotto le specifiche OSGI e abbiamo iniziato a vedere il codice di esempio per ottenere un semplice memory monitor per il controllo periodico dello stato di utilizzo della memoria sul nostro application server e in grado di mandare all’occorrenza delle mail di allarme.

Abbiamo già visto i dettagli del BundleMailSender, cioè il modulo per inviare le mail, adesso ci occuperemo del BundleMemoryMonitor.

Il BundleActivator

Partiamo dalla classe BundleActivator che dovrebbe a questo punto essere di facile comprensione:

package it.lcianci.test.osgi;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class MemoryMonitorActivator implements BundleActivator {

private static BundleContext context;
private MemoryMonitor memo;

static BundleContext getContext() {
return context;
}

public void start(BundleContext bundleContext) throws Exception {
MemoryMonitorActivator.context = bundleContext;
memo = new MemoryMonitor(5, 50, bundleContext);
}

public void stop(BundleContext bundleContext) throws Exception {
memo.stopMemoryMonitor();
MemoryMonitorActivator.context = null;
}
}

Come si può vedere, all’interno del metodo start, istanziamo un oggetto di tipo MemoryMonitor e passiamo come parametro il nostro Bundle Context, capiremo più avanti il perchè.

Il file Manifest

Veniamo adesso al file MANIFEST.MF ed al suo contenuto:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: BundleMemoryMonitor
Bundle-SymbolicName: BundleMemoryMonitor;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: it.lcianci.test.osgi.MemoryMonitorActivator
Import-Package: it.lcianci.test.osgi.util,org.osgi.framework;version=”1.3.0″
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Vendor: Luca Cianci
Require-Bundle: BundleMailSender;bundle-version=”1.0.0″

Analizziamo i punti salienti in grassetto:


  • Bundle-SymbolicName presenta un ulteriore attributo (singleton:=true) che mi permette di definire la mia classe come singleton ed evitare quindi di istanziarne più di una copia a seguito di differenti operazioni di start/stop.
  • Import-Package contiene in questo caso anche l’import del mio package presente nel BundleMailSender ed è possibile perchè, se vi ricordate, all’interno dell’altro file manifest avevamo una direttiva di tipo Export-Package. In questo modo possiamo usare dentro il MemoryMonitor la classe MailSender.
  • Require-Bundle specifica la dipendenza ed il legame tra i due bundle. In pratica il deploy del BundleMemoryMonitor non verrà “risolto” se sul server non è presente il BundleMailSender.


>> Nota: Nella prima parte del tutorial non abbiamo parlato in dettaglio degli stati relativi al ciclo di vita di un bundle. Questa immagine può servirci per comprendere il senso della “risoluzione” del deploy, ovvero la fase in cui viene verificata la presenza di tutti i vincoli e le dipendenze.


Dallo stato resolved si passa, in caso di successo, allo stato started e quindi active, stato in cui  il nostro bundle è pronto per essere utilizzato.

Il Memory Monitor

Vediamo adesso il codice relativo al MemoryMonitor vero e proprio.
Inserisco volutamente per esteso l’intero codice che fa uso dell’API JMX per il check sulla memoria. Penso possa essere interessante come argomento a contorno.

package it.lcianci.test.osgi;

import it.lcianci.test.osgi.util.MailSender;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class MemoryMonitor implements Runnable {

   final double UN_MEGA = 0.000000954;
   final int UN_SECONDO = 1000;
   private BundleContext bundleContext;
   private boolean active;
   public int checkTimeout;
   public int checkMemory;
   public MemoryMXBean memBean;

   public MemoryMonitor(int timeout, int memory,BundleContext bc) {
       checkTimeout  = timeout;
       checkMemory   = memory;
       active        = true;
       bundleContext = bc;
       
       new Thread(this).start();
   }

   @SuppressWarnings(“unchecked”)
   public void run() {
       try {
        double usedMem, maxMem;

             while(isRunning()) {
             Thread.sleep(checkTimeout * UN_SECONDO);
               memBean = ManagementFactory.getMemoryMXBean();
               usedMem = Math.rint(memBean.getHeapMemoryUsage().getUsed() * UN_MEGA);
               maxMem  = Math.rint(memBean.getHeapMemoryUsage().getMax() * UN_MEGA);
               System.out.println(this.getClass().getSimpleName()+” : “+usedMem+”/”+maxMem+” Mb”);
               
               if (usedMem > checkMemory) {
                System.out.println(“Soglia di memoria critica!”);
               
                ServiceReference sr = bundleContext.getServiceReference(MailSender.class.getName());
                MailSender ms = (MailSender)bundleContext.getService(sr);
                    ArrayList dest = new ArrayList();
                    dest.add(“luca.cianci@gmail.com”);
                    ms.sendMail(“OSGIMemoryMonitor”, “Memoria critica”, “Soglia di memoria superata”, dest);                               
               }
           }
       }
       catch(Exception e) {e.printStackTrace();}
   }
   
   private boolean isRunning() {
return active;
}
   
   public void stopMemoryMonitor() {
active = false;
}
}

Concentriamoci solo sul codice in grassetto:

  • ServiceReference sr = bundleContext.getServiceReference(MailSender.class.getName());
  • MailSender ms = (MailSender)bundleContext.getService(sr);
  • ArrayList dest = new ArrayList();
  • dest.add(“luca.cianci@bummolo.blogspot.com”);
  • ms.sendMail(“OSGIMemoryMonitor”,”Memoria critica”,”Soglia di memoria superata”,dest);


 

    • Riga 1 – usiamo il Bundle Context per recuperare un oggetto ServiceReference relativo al nostro MailSender. Questo è possibile perchè nel BundleMailSender avevamo registrato il servizio all’interno del BundleContext, ricordate?

 

  • Riga 2 – recuperiamo il servizio dal Bundle Context usando il ServiceReference per effettuare la lookup sul registry dei servizi.
  • Riga 5 – invochiamo il metodo sendMail sull’oggetto MailSender recuperato dal Bundle Context attraverso una lookup nel registry ed esposto come servizio.


Questo schema può chiarire meglio la divisione logica dei layer OSGI così da comprendere l’intero processo:

 

 

  • Generiamo i moduli  e li carichiamo tramite il layer Modules,
  • Gestiamo i moduli tramite il layer Life Cycle,
  • Esponiamo dei servizi tramite il layer Service Registry,
  • Usiamo i servizi esposti tramite il layer Services.


Una volta completato il nostro bundle non ci resta che esportarlo in formato jar. Come già detto in precedenza, se avete usato il wizard dei plugin di Eclipse usate “export, deployable plugin and fragment” altrimenti esportate il jar normalmente avendo cura però di specificare il nostro file manifest appena creato.
Una volta esportato il bundle possiamo effettuarne il deploy su JBoss mediante la console diamministrazione alla porta 9990 dalla sezione “Deployments/Manage Deployments” e poi avviarla. Fatto questo, sempre dalla console di amministrazione, spostandoci sulla sezione “Runtime Operations/OSGI” troverete in elenco il nostro Bundle in stato attivo.Poichè questo bundle è di fatto un Thread, se guardate lo standard output o il file di log scoprirete che ogni 5 secondi viene scritto un messaggio simile al seguente nel caso in cui il valore di soglia di 50Mb venga superato:11:44:22,132 INFO (Thread-80) MemoryMonitor : 69.0/455.0 Mb
11:44:22,134 INFO (Thread-80) Soglia di memoria critica!
11:44:22,136 INFO (Thread-80) Mail di allarme a luca.cianci@bummolo.blogspot..com

Direi che questo termina il nostro tutorial di esempio.

Ho voluto trattare la collaborazione tra differenti bundle perchè mi sembrava uno degli elementi di base per iniziare a comprendere l’interazione tra moduli e al tempo stesso il disaccoppiamento che si viene a creare tra gli stessi. Questo disaccoppiamento ci consente di trattare tutti i moduli separatamente e li rende manutenibili in maniera isolata gli uni dagli altri.

Riferimenti:

La prima parte di questo tutorialhttp://www.osgi.org/Technology/WhatIsOSGihttp://www.osgi.org/Technology/HowOSGihttp://it.wikipedia.org/wiki/OSGihttp://www.vogella.com/articles/OSGi/article.htmlhttp://www.javabeat.net/2011/11/writing-an-osgi-webapplication/https://docs.jboss.org/author/display/AS7/Helloworld+OSGi+quickstarthttp://docs.jboss.org/author/display/JBOSGI/Developer+Documentationhttp://jbossosgi.blogspot.it/2010/11/jboss-as7-osgi-integration.htmlhttp://www.knopflerfish.org/osgi_service_tutorial.htmlhttp://felix.apache.org/site/index.htmlLe immagini sono prese in prestito dal sito OSGI Alliance.