Mbeans dinamici
L'utilizzo di mbeans dinamici permette una maggiore flessibilità e dinamicità nella definizione della interfaccia di management che in questo caso non è fissa e tipata ma generica e de-tipata.
I metodi offerti da questo genere di componenti permettono all'agent di scoprire dinamicamente attributi e funzionamento dell'oggetto gestito.
Questo genere di componenti è utile in quei casi in cui si prevede che l'interfaccia di management possa cambiare spesso, oppure quando è necessario non definire in fase di compi-lazione l'interfaccia: in questo caso specifico gli mbeans ad esempio permettono di utilizzare file XML con i quali specificare l'interfaccia a runtime.
Un altro caso in cui trovano applicazione questi componenti è quando si deve applicare JMX a risorse già esistenti o che non seguono lo standard delle convenzioni sui nomi di MBeans e Java Beans.
In questo caso gli MBeans dinamici si comportano come adapter interponendosi fra l'agent e la risorsa gestita invocandone i metodi appropriati.
Altra caratteristica importante è la definizione di metadati che sono a carico dell'mbean (e quindi del programmatore) e non dell'agent che esegue questo compito tramite l'introspezione.
Interfaccia
Per poter creare un mbean dinamico è necessario estendere l'interfaccia DynamicMBean, che espone una serie di metodi per la determinazione degli attributi e per invocare i metodi. I me-todi relativi agli attributi, così già come quelli dell'invocazione negli MBeans Standard, sono dinamici e detipati: per questo come verrà mostrato è necessaria una certa disciplina nella loro implementazione.
public interface DynamicMBean{
Object getAttribute(String attribute)
AttributeList getAttributes(String[] attributes)
MBeanInfo getMBeanInfo()
Object invoke(String actionName, Object[] params, String[] signature)
void setAttribute(Attribute attribute)
AttributeList setAttributes(AttributeList attributes)
Si noti come il metodo invoke() in questa caso accetti oggetti stringhe non solo nei parametri ma anche un array di stringhe per la firma del metodo da invocare.
I due array di oggetti devono essere ovviamente della stessa dimensione ed i valori contenuti devono corrispondere.
Il metodo getMBeanInfo() restituisce un oggetto di tipo MbeanInfo, che permette di descrivere completamente il bean tramite metadati su attributi, metodi, costruttori e notifiche di eventi. Ognuno di questi metadati corrisponde una classe apposita che verrà presentata poco più avan-ti. Nel proseguo dell'articolo viene mostrato un esempio di implementazione del metodo relati-vo alla versione dinamica di UserMBean visto in precedenza.
Il metodo getAttribute() ha la seguente firma
Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException
Si noti il rilancio della AttributeNotFoundException che deve essere propagata in caso l'attributo richiesto non esista.
Ad esempio il metodo getName() visto nell'mbean Hello potrebbe in questo caso diventare
public String getAttribute (String attribute) throws AttributeNotFoundException,
AttributeInvalidException
MBeanException,ReflectionException{
if (attribute.equals("name")
return name;
// mettere qui altri controlli e restituire l'attributo corrispondente
. . .
// se alla fine nessun attributo viene restituito significa che l'attributo non
// esiste
// o il nome non è esatto
throw new AttributeNotFoundException("Attributo "+attribute+" non trovato");
}
In questo caso si effettua una semplice comparazione con il parametro passato e gli attributi del bean: i nomi reali degli attributi e quelli esposti dall'interfaccia sono del tutto slegati. La logica di comparazione all'interno del metodo definisce di fatto il set di attributi validi. Il tipo ritorna-to deve anche corrispondere a quanto definito all'interno del Metadata Object corrispondente all'attributo.
L'eccezione MBeanException serve per incapsulare eccezioni di altro tipo che genericamente vengono create al momento di ricavare una proprietà, come la IOException (se i dati devono essere letti) o la NullPointerException (se i dati non sono validi).
ReflectionException infine può verificarsi in occasione di operazioni di reflection a carico dell'agent. Si parlerà in seguito di questi aspetti.
Cercare di ricavare un attributo null o stringa vuota crea una IllegalArgumentException che viene wrappata dall'agent con una JMRuntimeException.
Il metodo getAttributes() opera restituendo una AttributeList, contenuta nella JMX API e che discende direttamente ArrayList. La lista contiene elementi di tipo Attribute. Il metodo non se-gnala eccezioni di nessun tipo in caso di errore nel ricavare gli attributi: tutti gli attributi che generano un qualche problema nell'operazione di lettura vengono lasciati fuori dalla lista.
I metodi setAttribute() e setAttributeList() servono per impostare gli attributi del bean. Anche in questo caso, come per i getXXX è compito del programmatore cercare la corrispondenza giusta fra il nome dell'attributo e l'attributo vero e proprio. La firma completa del primo meto-do è
void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeException MBeanException,ReflectionException
Dove le eccezioni rilanciate anche in questo caso consentono di avere informazioni in caso di errori sui nomi (AttributeNotFoundException) o di tipi non compatibili (InvalidAttributeException). Le eccezioni checked devono invece essere wrappate e rilanciate come MBeanException.
public String setAttribute (Attribute attribute) throws
AttributeNotFoundException,
AttributeInvalidException,
MBeanException, ReflectionException{
if (attribute == null)
throw new AttributeNotFoundException("Attributo nullo");
if (attribute.getName().equals("Name"))
this.name = (String)attribute.getValue();
}
Il metodo setAttributes() si comporta in modo simile al getAttributes(). Riceve in ingresso un AttributeList e non genera errori o messaggi in caso di errori nella associazione fra nome attributo ed attributo stesso.
Invocazione
Con i bean dinamici è necessario anche ridefinire il metodo invoke() in modo da consentire l'esatta invocazione del metodo corrispondente. Ad esempio la versione dinamica del MBean potrebbe avere il metodo invoke() così definito
public Object invoke(String actionName, Object[] params, String[]
signature)throws MBeanException, ReflectionException{
if (actionName == null || actionName.equals(""))
throw new IllegalArgumentException("Nessuna operazione definita");
// mappa il nome della operazione sui metodi da eseguire
if(actionName.equals(PRINT_INFO)){
return this.printInfo();
}
if(actionName.equals(ADD_NUMBER)){
this.addPhoneNumber((String)params[0]);
return null;
}
if(actionName.equals(REMOVE_NUMBER)){
this.removePhoneNumber((String)params[0]);
return null;
}
else throw new
UnsupportedOperationException("Operazione "+actionName+" sconosciuta");
}
Il metodo getMBeanInfo()
Il metodo getMBeanInfo() permette all'agent di ricavare tutte le informazioni relative al bean. Come si può vedere dal codice seguente esso confeziona tutta una serie di metadati e poi li restituisce in un MBeanInfo.
public MBeanInfo getMBeanInfo(){
// per determinare se l'attributo leggibile e/o scrivibile
final boolean READABLE = true;
final boolean WRITABLE = true;
// per determinare se attributo è ricavabile nella forma booleana isAttribute()
final boolean IS_GETTER = true;
// descrizione della classe dell'MBean
String className = this.getClass().getName();
String description =
"Una risorsa per utenti con interfaccia di gestione dinamica";
// metadata per l'attributo Id
MBeanAttributeInfo id = new MBeanAttributeInfo(ID, // nome attributo
String.class.getName(), // tipo attributo
"Identificatore unico utente", // descrizione attributo
READABLE, !WRITABLE, !IS_GETTER // accesso attributo
);
// metadata per l'attributo Name
MBeanAttributeInfo name = new MBeanAttributeInfo(NAME, // nome attributo
String.class.getName(), // tipo attributo
"Nome utente", // descrizione attributo
READABLE, // accesso attributo
WRITABLE,
!IS_GETTER
);
// metadata per l'attributo Address
MBeanAttributeInfo address = new MBeanAttributeInfo(ADDRESS, // nome attributo
String.class.getName(), // tipo attributo
"Indirizzo utente", // descrizione attributo
READABLE, WRITABLE, !IS_GETTER // accesso attributo
);
// metadata per l'attributo Password
MBeanAttributeInfo password = new MBeanAttributeInfo(PASSWORD,
// nome attributo
String.class.getName(), // tipo attributo
"Identificatore unico dello user", // descrizione attributo
READABLE, !WRITABLE, !IS_GETTER // accesso attributo
);
// metadata per l'attributo PhoneNumbers
MBeanAttributeInfo numbers = new MBeanAttributeInfo(PHONENUMBERS,
// nome attributo
Vector.class.getName(), // tipo attributo
"Identificatore unico dello user", // descrizione attributo
READABLE, !WRITABLE, !IS_GETTER // accesso attributo
);
// metadata per l'operazione printInfo()
MBeanOperationInfo printInfo = new MBeanOperationInfo(PRINT_INFO,
// nome operazione
"Restituisce le informazioni sull'utente", // descrizione testuale
null, // signature
String.class.getName(), // tipo ritornato
MBeanOperationInfo.INFO); // impatto
// metadata per l'operazione addPhoneNumber()
MBeanOperationInfo addPhoneNumber = new MBeanOperationInfo(ADD_NUMBER,
// nome operazione
"Aggiunge un numero di telefono", // descrizione testuale
new MBeanParameterInfo[] { // signature
new MBeanParameterInfo("number",String.class.getName(),
"Il numero da aggiungere")
},
void.class.getName(), // tipo ritornato
MBeanOperationInfo.ACTION // impatto
);
// metadata per l'operazione removePhoneNumber()
MBeanOperationInfo removePhoneNumber = new MBeanOperationInfo(REMOVE_NUMBER,
// nome operazione
"Rimuove un numero di telefono", // descrizione testuale
new MBeanParameterInfo[] { // signature
new MBeanParameterInfo("number",
String.class.getName(),
"Il numero da aggiungere")
},
void.class.getName(), // tipo ritornato
MBeanOperationInfo.ACTION // impatto
);
//mbean costruttore
MBeanConstructorInfo defaultConstructor = new
MBeanConstructorInfo("Default Constructor",
"Crea una nuova istanza di utente", null);
// attribute Constructor and operation list
MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[]{id, name, address,
numbers, password};
MBeanConstructorInfo[] constructors = new
MBeanConstructorInfo[]{defaultConstructor};
MBeanOperationInfo[] operations = new MBeanOperationInfo[]{printInfo,
addPhoneNumber, removePhoneNumber};
return new MBeanInfo(className, description, attributes, constructors,
operations, null);
}
Il codice completo dell'esempio
Di seguito è riportato il codice completo della classe DynamicUser che può essere scaricata nella sezione risorse e link
public MBeanInfo getMBeanInfo(){
// Attributi boolenani per determinare
// se l'attributo leggibile e/o scrivibile
final boolean READABLE = true;
final boolean WRITABLE = true;
// Attributi boolenano per determinare se un attributo
// è ricavabile nella forma booleana isAttribute()
final boolean IS_GETTER = true;
// descrizione della classe dell'MBean
String className = this.getClass().getName();
String description =
"Una risorsa per utenti con interfaccia di gestione dinamica";
// metadato per l'attributo Id
MBeanAttributeInfo id = new MBeanAttributeInfo(ID, // nome attributo
String.class.getName(), // tipo attributo
"Identificatore unico utente",
// descrizione attributo
READABLE, !WRITABLE, !IS_GETTER // accesso
);
// metadato per l'attributo Name
MBeanAttributeInfo name;
name = new MBeanAttributeInfo(NAME, // nome attributo
String.class.getName(), // tipo attributo
"Nome utente", // descrizione attributo
READABLE, WRITABLE, !IS_GETTER
// accesso attributo
);
// metadato per l'attributo Address
MBeanAttributeInfo address;
address = new MBeanAttributeInfo(ADDRESS, // nome attributo
String.class.getName(), // tipo attributo
"Indirizzo utente", // descrizione attributo
READABLE, WRITABLE, !IS_GETTER // accesso
);
// metadato per l'attributo Password
MBeanAttributeInfo password;
password = new MBeanAttributeInfo(PASSWORD, // nome attributo
String.class.getName(), // tipo attributo
"Identificatore unico dello user",
// descrizione
READABLE, !WRITABLE, !IS_GETTER // accesso
);
// metadato per l'attributo PhoneNumbers
MBeanAttributeInfo numbers;
numbers = new MBeanAttributeInfo(PHONENUMBERS, // nome attributo
Vector.class.getName(), // tipo attributo
"Identificatore unico dello user",
// descrizione
READABLE, !WRITABLE, !IS_GETTER // accesso
);
// metadato per l'operazione printInfo()
MBeanOperationInfo printInfo;
printInfo = new MBeanOperationInfo(PRINT_INFO, // nome
"Fornisce informazioni utente",
// descrizione
// testuale
null, // signature
String.class.getName(), // tipo ritornato
MBeanOperationInfo.INFO); // impatto
// metadato per l'operazione addPhoneNumber()
MBeanOperationInfo addPhoneNumber;
addPhoneNumber = new MBeanOperationInfo(ADD_NUMBER, // nome operazione
"Aggiunge un numero di telefono",
// descr.
new MBeanParameterInfo[] { // signature
new MBeanParameterInfo(
"number",
String.class.getName(),
"Il numero da aggiungere")
},
void.class.getName(), // tipo ritornato
MBeanOperationInfo.ACTION // impatto
);
// metadato per l'operazione removePhoneNumber()
MBeanOperationInfo removePhoneNumber;
removePhoneNumber = new MBeanOperationInfo(REMOVE_NUMBER, // nome operazione
"Rimuove un numero di telefono",
// descr
new MBeanParameterInfo[] {
// signature
new MBeanParameterInfo("number",
String.class.getName(),
"Il numero da aggiungere")
},
void.class.getName(),
// tipo ritornato
MBeanOperationInfo.ACTION
// impatto
);
//mbean costruttore
MBeanConstructorInfo defaultConstructor;
defaultConstructor = new MBeanConstructorInfo("Default Constructor",
"Crea una nuova istanza di utente",
null);
// lista attributo costruttore e metodi
MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[]{id, name,
address,
numbers,
password};
MBeanConstructorInfo[] constructors;
constructors = new MBeanConstructorInfo[]{defaultConstructor};
MBeanOperationInfo[] operations;
operations = new MBeanOperationInfo[]{printInfo,
addPhoneNumber,
removePhoneNumber};
return new MBeanInfo(className, description, attributes,
constructors, operations, null);
}
public String printInfo(){
return "ID: "+ Id +"\n name: "+Name+"\naddress: "+
Address+"\npassword: "+Password+"\nPhoneNumbers: "+
PhoneNumbers;
}
// metodi di management JMX
public void addPhoneNumber(String number){
PhoneNumbers.addElement(number);
}
public void removePhoneNumber(String number){
PhoneNumbers.remove(number);
}
public DynamicUser() {}
}
Conclusioni
Questo mese si è visto un altro importante pezzo del framwork JMX. L'esempio allegato dovrebbe poter dare maggiore spiegazione del funzionamento e del significato di quanto visto.
Nelle prossime puntate gli argomenti che verranno trattati daranno maggiore giustificazione di quanto qui mostrato
Link e risorse
[JMXSpec] - JMX Sun official reference: http://java.sun.com/products/JavaManagement
[JMXRI] - JMX Reference Implementation http://java.sun.com/products/JavaManagement/download
[TIV] IBM JMX Tivoli implementation : http://www.alphaworks.ibm.com
[ANET] AdvanceNet JMX implementation: http://www.advancenet.com
[JMXJB] "JMX: managing J2EE with Java Management Extensions" di J. Lindfors, M.Fleury, The JBoss Group; Ed. SAMS.
Scarica i sorgenti completi
