JUG Sardegna supports Devoxx 2012
Vuoi ricevere uno zainetto? Clicca qui

Java User Groups
Java.net Partner
Get Firefox!

Jmx2

articolo di MassimilianoDessi

StandardMBean, MBeanServer MX4J e Sun R.I.
Java Management extensions parte 2


Introduzione

Vedremo in questo articolo come usare le API JMX per monitorare e gestire una risorsa generica gestendola come StandardMBean.
Per lÂ’esempio useremo sia lÂ’implementazione di riferimento (R.I.) della Sun versione 1.2.1, sia una implementazione Open Source, MX4J versione 2.0.1, in maniera da poter valutare le differenze dÂ’uso tra le due.
Come detto anche nellÂ’articolo precedente, le risorse vengono usate come StandardMBean quando le si vuole gestire, e si desidera effettuarne il controllo e il monitoraggio al trascorrere del tempo.

A questo scopo per semplicità di comprensione dellÂ’uso pratico delle API JMX e per facilità di verifica con una web-application, prenderemo come risorsa una Servlet. In particolare prenderemo una esempio illustrato nel libro "Java Servlet Programming" (Jason Hunter e William Crawford).
Questa Servlet si occupa di contare le proprie istanze, gli accessi ad ogni singola istanza e gli accessi totali fra tutte le istanze; renderemo questi conteggi visualizzabili da browser come risultato della chiamata, questo ci servirà come confronto con la console JMX.

Nella Servlet la variabile contatoreServlet tiene traccia degli accessi alla singola istanza della Servlet, le variabili istanze e contatoreTraIstanze hanno la particolarità di essere statiche perché, dovendo contare rispettivamente il numero delle istanze e di tutti gli accessi ad esse, non vanno perse.
Il numero delle istanze viene conservato in una Hashtable in maniera da non duplicare le istanze stesse.



package esempi.jmx;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.ServletContext;
import java.io.*;
import java.util.*;

/**
 *@author  Dessì Massimiliano
 */

public class ServletJmx extends HttpServlet {

 public void init() throws ServletException{}

   public void doGet(HttpServletRequest request, HttpServletResponse response)      throws  ServletException, IOException {

      contatoreServlet++;
      istanze.put(this, this);
      contatoreTraIstanze++;
      PrintWriter out = response.getWriter();
      out.println("ServletJmx di esempio");
out.println("Accessi alla singola istanza    :"+getNumeroAcessiSingolaIstanza());
      out.println("Numero istanze :"+getNumeroIstanze());
      out.println("Accessi totali :"+getNumeroAcessiTotali());
   }



   public void doPost(HttpServletRequest request, HttpServletResponse response)   throws  ServletException, IOException {
      doGet(request, response);
   }


   public int getNumeroIstanze() {
      return esempi.jmx.ServletJmx.istanze.size();
   }



   public int getNumeroAcessiSingolaIstanza() {
      return this.contatoreServlet;
   }


   public int getNumeroAcessiTotali() {
      return esempi.jmx.ServletJmx.contatoreTraIstanze;
   }



   static int contatoreTraIstanze = 0;
   int contatoreServlet = 0;
   static Hashtable istanze = new Hashtable();
}


 



Se mettessimo subito all'opera questa Servlet, essa ci mostrerebbe sul browser i valori delle tre variabili di conteggio.





Ora vedremo i passi necessari per gestire attraverso una interfaccia di management questa Servlet, che diventerà una risorsa monitorabile e gestibile.
Per essere gestita e monitorata, la Servlet che abbiamo chiamato ServletJmx dovrà implementare l' interfaccia ServletJmxMBean.
ServletJmxMBean contiene i metodi che abbiamo scelto per poter gestire le varie caratteristiche della Servlet che vogliamo gestire dalla console JMX.
Dalla console di gestione che sceglieremo visualizzabile su canale Http, gestiremo i valori delle tre variabili di conteggio, con la possibilità di azzerare questi contatori.
Non esponiamo la possibilità di distruggere le istanze con l'invocazione del garbage collector, in quanto è compito interno del Servlet Engine decidere quando questa operazione va compiuta, è comunque nelle nostre possibilità esporre anche questa operazione.


package esempi.jmx;
/**
 *@author  Dessì Massimiliano
 */

public interface ServletJmxMBean  {

  public abstract int getNumeroIstanze();
  public abstract int getNumeroAcessiSingolaIstanza();
  public abstract int getNumeroAcessiTotali();

  public abstract String resetAcessiTotali();
  public abstract String resetAcessiSingolaIstanza();

}
  


Ora che abbiamo la risorsa da gestire e unÂ’interfaccia in cui abbiamo descritto i metodi di gestione, vediamo come procedere.
Per prima cosa vediamo quali pattern dobbiamo necessariamente usare:

1) L' interfaccia di management in cui abbiamo definito le operazioni desiderate deve obbligatoriamente avere il nome che termina con MBean; ogni altro nome diverso sia come maiuscole, minuscole o lÂ’assenza della parola MBean ci verrà segnalata con una eccezione di NotCompliantMBean

2) La classe che implementa l'interfaccia di gestione può avere un numero qualsiasi di costruttori, ma deve averne almeno uno pubblico.
Anche se il compilatore in mancanza ne genera uno, è preferibile dichiarare esplicitamente un costruttore senza argomenti.

3) Nella definizione dei metodi di accesso agli attributi va seguita strettamente la convenzione dei get/set, per i booleani è consuetudine l' “is” davanti al nome della variabile.
Il metodo “setter” deve accettare solo un parametro; se si dovesse scrivere il metodo con un secondo parametro, l'MBeanServer esporrebbe il nostro “setter” come una operazione.
Ricordarsi che i parametri nei set devono essere dello stesso tipo di quelli di ritorno del get corrispondente.

La verifica della correttezza dei pattern sopra elencati, viene operata dall' MBeanServer attraverso l'introspezione degli MBean quando vengono registrati .
Potrebbe infatti capitare che il nostro codice venga compilato correttamente ma che l'MBean non sia aderente alle specifiche (compliant), se non lo fosse, verrebbe notificato attraverso una eccezione nel tentativo di registrarlo.

Vediamo la Servlet con le modificata con lÂ’implementazione della interfaccia ServletJmxMBean:


package esempi.jmx;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
import javax.servlet.ServletException;
import javax.servlet.http.*;

/**
 *@author  Dessì Massimiliano
 */

public class ServletJmx extends HttpServlet implements ServletJmxMBean {



   public ServletJmx() {
      contatoreServlet = 0;
   }


   public void init()
         throws ServletException {
      if (!first) {
        first = true;
        Controller controller = new Controller();
        controller.init(this);
      }
   }



   public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException {
      contatoreServlet++;
      istanze.put(this, this);
      contatoreTraIstanze++;
      PrintWriter out = response.getWriter();
      out.println("ServletJmx");
      out.println("Accessi alla singola istanza : ".concat(String.valueOf(String.valueOf(getNumeroAcessiSingolaIstanza()))));
      out.println("Numero istanze : ".concat(String.valueOf(String.valueOf(getNumeroIstanze()))));
      out.println("Accessi totali : ".concat(String.valueOf(String.valueOf(getNumeroAcessiTotali()))));
   }



   public void doPost(HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException {
      doGet(request, response);
   }


   public int getNumeroIstanze() {
      return istanze.size();
   }


   public int getNumeroAcessiSingolaIstanza() {
      return contatoreServlet;
   }


   public int getNumeroAcessiTotali() {
      return contatoreTraIstanze;
   }


   public String resetAcessiTotali() {
      contatoreTraIstanze = 0;
      return resetContatoreAccessiTotali;
   }


   public String resetAcessiSingolaIstanza() {
      contatoreServlet = 0;
      return resetContatoreAccessiSingolaIstanza;
   }



   static int contatoreTraIstanze = 0;
   int contatoreServlet;
   static Hashtable istanze = new Hashtable();
   static boolean first = false;
   private final String resetContatoreAccessiSingolaIstanza = "Contatore accessi singola istanza azzerato";
   private final String resetContatoreAccessiTotali = "Contatore accessi totali azzerato";

}


MBeanServer
A questo punto ci ritroviamo con una ServletJmx gestibile mediante l'implementazione della interfaccia; ora ci dovremo appoggiare alla infrastruttura JMX per poter lavorare.
Sono necessari in particolare l'MBeanServer a cui si deve registrare la ServletJmx e un adattatore opportuno per poter visualizzare su browser web il funzionamento di tutta l'infrastruttura e della nostra Servlet.
Qui iniziamo a vedere le differenze tra la R.I. e MX4J.
Nella R.I., lÂ’adaptor si chiama HtmlAdaptor; per il suo funzionamento è necessario passargli il numero di porta sul quale deve mettersi in ascolto e registrarlo allÂ’MBeanServer, in MX4j si chiama HttpAdaptor e ha bisogno di un passaggio ulteriore rispetto allÂ’HtmlAdaptor per essere visualizzato su browser web; poiché lÂ’HttpAdaptor espone i risultati in formato xml, per poterlo visualizzare in maniera adeguata è necessario applicare una trasformazione xsl attraverso un altro MBean deputato a questo processo.
La differenza alla fine è di quattro righe in più di codice nel caso di MX4J, ma con la possibilità di ottenere informazioni in formato xml che la R.I. non dà.

Poiché il servlet engine può istanziare più di una ServletJmx, poniamo in una altra classe che chiamiamo Controller la logica per ottenere l' MBeanServer e istanziare l' HtmlAdaptor e lÂ’HttpAdaptor.
La servlet istanzia solo una volta la classe Controller di cui richiama il metodo init() passandole un riferimento a se stessa.


public void init(ServletJmx servlet) {
      try {
        server = MBeanServerFactory.createMBeanServer();
        ObjectName objName = new ObjectName("UserDomain:name=JmxServlet");
        server.registerMBean(servlet, objName);
        _mbeans.add(objName);
        createHTMLAdapterSun();
        createHTMLAdapterMx4j();

      catch (MalformedObjectNameException mon) {
        System.out.println(" MalformedObjectNameException");
      catch (NotCompliantMBeanException nc) {
        System.out.println("NotCompliantMBeanException ");
      catch (MBeanRegistrationException re) {
        System.out.println("MBeanRegistrationException");
      catch (InstanceAlreadyExistsException iae) {
        System.out.println("InstanceAlreadyExistsException ");
      catch (Exception ex) {
        ex.printStackTrace();
      }
   }


La classe Controller nel suo metodo init() (che ricordiamo verrà chiamato solo una volta dalla servlet), si farà dare un oggetto MBeanServer che non verrà deallocato dalla garbage collector perchè si è usato il metodo createMBeanServer() che fa in modo che la factory mantenga il riferimento all' MBeanServer.
L' istruzione successiva istanzia un objectName che serve all' MBeanServer per identificare l' MBean , lo registra passando sia la Servlet sia l'objectname che la identifica univocamente, lo aggiunge all' arraylist degli MBean registrati.
Prima di proseguire nella descrizione del codice, spendiamo alcune parole per descrivere in modo sommario i gruppi funzionalità forniti dallÂ’ MBeanServer:

1. Metodi di istanziazione e registrazione
2. Manipolazione indiretta degli MBean
3. Notifiche
4. Query
5. Utility

Alcuni dei metodi dei primi due gruppi sono stati usati nel codice di esempio, mentre le utime tre caratteristiche verranno illustrate negli esempi dei prossimi articoli. Iniziamo a dire per quanto riguarda le notifiche , che anche attraverso lÂ’MBeanServer è possibile registrare la richiesta di ricevimento notifiche da qualsiasi MBean registrato, che le notifica in broadcast.
Le query invece servono per ottenere degli MBean registrati, vengono usate nellÂ’ uso dei connettori (connectors) e negli adattatori di protocollo (protocol adaptors).
Le utility servono per avere informazioni sugli MBean registrati, il loro numero, la deserializzazione dello stato in cui si trovano.
Procediamo ora con la descrizione del codice.

HtmlAdaptor Sun R.I.
Una volta eseguite le operazioni di registrazione e di aggiunta alla lista degli MBean, è necessario visualizzare il funzionamento per mezzo di un HtmlAdapter che è un MBean fornito già pronto per l' utilizzo dalle API JMX della Sun.


   private void createHTMLAdapterSun() throws Exception {

      int portNumber = 8082;
      _html = new HtmlAdaptorServer(portNumber);
      ObjectName html_name = null;
      html_name = new ObjectName("Adaptor:name=html,port=".concat(String.valueOf(String.valueOf(portNumber))));
      server.registerMBean(_html, html_name);
      _mbeans.add(html_name);
      _html.start();
   }


La sintassi per la registrazione presso MBeanServer è la stessa vista in precedenza, anche questo adapter è infatti un MBean , gli è però necessario il numero di porta su cui si dovrà mettere in ascolto e il comando di partenza (start())

HttpAdaptor MX4J
Nel caso dellÂ’ adaptor di MX4j come prima differenza vediamo la possibilità di settare lÂ’ host nel caso in cui volessimo maggiore sicurezza e non vogliamo che la console di gestione sia accessibile direttamente da macchine esterne.
Una volta fatto partire lÂ’adaptor, viene creato e registrato un processore XSLT (anche esso MBean) .
LÂ’output dellÂ’adaptor viene trasformato dallÂ’ XSLTProcessor con il metodo setAttribute del server.


   private void createHTMLAdapterMx4j() throws Exception {

      HttpAdaptor adaptor = new HttpAdaptor();
      ObjectName http = new ObjectName("Adaptor:name=HttpAdaptor");
      server.registerMBean(adaptor, http);
      adaptor.setPort(8081);
      adaptor.setHost("localhost");
      adaptor.start();
      XSLTProcessor xslt = new XSLTProcessor();
      ObjectName processorXSLT = new ObjectName("Processor:name=XSLTProcessor");
      server.registerMBean(xslt, processorXSLT);
      server.setAttribute(http, new Attribute("ProcessorName", processorXSLT));
   }


Ora andiamo a provare il codice: una volta compilate le classi e messe nella cartella WEB-INF/classes dentro la nostra webapp ,dopo aver mappato la servlet nel file web.xml della nostra applicazione, facciamo partire tomcat o un altro servlet-engine di nostra preferenza.
NellÂ’url del browser chiamiamo la nostra servlet, con il nome con la quale è stata mappata: otterremo la schermata con il numero di accessi alla singola istanza, gli accessi totali e il numero di istanze create:





fin qui nulla di nuovo rispetto a ciò che ci si aspettava. Ora richiamiamo nell url la porta 8081 ( quella assegnata all' HttpAdaptor nel codice) e vedremo :




ora chiamiamo anche lÂ’ url 8082 () assegnato allÂ’ HtmlAdaptor nel codice)




nella parte inferiore vediamo la lista degli MBean, il quale non è altro che la visualizzazione dell' ArrayList al quale abbiamo aggiunto la ServletJmx e l' HtmlAdapter.
Andando sul link della ServletJmx vedremo la schermata con la visualizzazione dei valori che sono quelli che la servlet ci ha mostrato nella sua chiamata diretta. Qui possiamo anche scegliere un tempo in secondi come intervallo di controllo dello stato degli attributi.
Richiamando più volte la servlet dal browser vedremo anche nella interfaccia di gestione i valori che i contatori delle istanze assumono.

più in basso nella pagina vediamo anche la lista delle operazioni che abbiamo esposto tramite l' interfaccia di gestione



possiamo fare delle prove con i reset e vedere quello che restituisce in output la servlet nel browser, possiamo anche provare a cancellarla dagli MBean registrati con il bottone unregister,




Conclusioni
Con questo semplicissimo esempio abbiamo visto come sia possibile monitorare e gestire una singola risorsa trattandola da StandardMBean, cosa che assolve alla maggior parte della casistica delle risorse da monitorare.
Abbiamo visto attraverso la console di gestione operazioni che normalmente vengono svolte in maniera applicativa “dietro le quinte”, MX4J viene infatti usato in Tomcat 4.1.x e 5.x, un esempio “visibile ” si può avere abilitando la console di Amministrazione di Tomcat (Admin) e quella di gestione (Manager).







NellÂ’ esempio di questo articolo abbiamo usato JMX “localmente” alla applicazione, è infatti possibile anche lÂ’uso dellÂ’ MBEAN server in maniera remota (API JMX Remote).
Si è intravista la potenza di queste API che in uno scenario in cui la totalità degli oggetti di una applicazione (dalla più semplice alla più complessa ) siano MBean rende possibile la totale gestione dei componenti , lÂ’autodiagnosi e lÂ’ hot-deploy.
Nei prossimi articoli indagheremo su altre caratteristiche e altre possibilita offerte da queste API in scenari differenti.


Riferimenti

MX4J

Sun JMX Reference Implementation


Bibliografia

Java Management Extensions (J.Steven Perry) ed.O'Reilly

Java Servlet Programming (Jason Hunter e William Crawford) ed.O'Reilly




Ringraziamenti

Simone Bordet per l'aiuto e i chiarimenti su MX4J


VeryQuickWiki Version 2.7.8 | Admin
Copyright © 2003-20011 Java User Group Sardegna Onlus. - Java, the Java Coffee Cup Logo and the Duke Logo are trademarks or registered trademarks of Oracle corporation in the U.S. and other countries.