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
|