Administration des ressources avec JMX

Selim KEBIR

Cet article a été rédigé pour permettre d'étudier les différentes couches d'une application répondant aux spécifications JMX. Les principales fonctionnalités des clients JMX sont discutées et accompagnées de plusieurs exemples illustratifs. ♪

- Titre : Administration des ressources avec JMX

- Auteur : Selim KEBIR

- Parution : 10 octobre 2008

- Licence :

Sommaire

- I. Introduction

- I-A. JMX

- I-B. les MBeans

- I-C. Activer/Désactiver le MBeanServer

- I-C-1. Activer l'agent JMX

- I-C-2. Désactiver l'agent JMX

- I-D. L'API pour interroger le MBeanServer

- I-E. JConsole

- I-F. Survol des MBean présents par défaut

- I-F-1. Gestion de la mémoire

- I-F-2. Gestion des threads

- I-F-3. Gestion des niveaux logs

- II. Exemple de connexion à un agent JMX

- II-A. Création d'un connecteur RMI

- II-B. Utilisation d'un proxy pour travailler sur un MBean distant

- II-B-1. Sous J2SE 5.0

- II-B-2. Sous J2SE 6.0

- II-C. Utilisation d'un filtre pour récupérer plusieurs MBeans d'un agent JMX

- II-C-1. Exemple

- III. Exemple d'administration d'un MBean avec JConsole

- III-A. Boite de dialogue de connexion

- III-B. Création du MBean et de l'agent JMX

- III-B-1. Le MBean

- III-B-2. L'agent JMX

- III-C. Connexion à l'agent

- III-D. Localisation du MBean

- III-E. Accès aux attributs du MBean

- III-F. Invocation des opérations du MBean

- IV. Sécurisation de l'accès aux MBeans

- IV-A. Création des variables d'environnement

- IV-B. Le fichier jmx.password

- IV-C. le fichier jmx.access

- V. Utilisation de HTML Adaptor

- V-A. Agent view

- V-B. MBean view

- VI. Spring JMX

- VI-A. Introduction

- VI-B. Enregistrement des beans dans un MBeanServer

- VI-C. Création d'un MBeanServer

- VI-D. Création d'un connecteur serveur

- VI-E. Sécurisation de l'accès au MBeanServer

- VII. Les MXBeans

- VII-A. Introduction

- VII-B. Problème avec les MBeans

- VII-C. Comment éviter ce problème avec les MXBeans

- VIII. Téléchargements des sources

- IX. Conclusion

- X. Remerciements

I - Introduction

I-A - JMX

Issu principalement de la spécification JSR 3 et JSR 255, JMX est un standard développé par JCP (Java Community Process) pour la gestion et le suivi des applications et services. Ce standard définit une architecture de gestion, des design patterns, des API, et des services pour construire des solutions distribuées, dynamiques, et modulaires pour la gestion de ressources de tout type, L'API JMX rend possible la gestion d'équipements logiciels ou matériels allant de simples téléphones à des serveurs en passant par des périphériques et des applications, l'utilisation de la technologie JMX pour gérer les applications et les services augmente leurs flexibilités en rendant les applications plus faciles à installer, à configurer et à maintenir.

Pour être administrables, les ressources logicielles ou matérielles doivent être instrumentées sous la forme de composants appelés MBeans (Managed Beans). Ces composants fournissent une interface d'administration et l'instrumentation nécessaire permettant de contrôler les ressources associées encapsulées dans le MBean. L'interface d'administration définie par JMX doit permettre aux vendeurs de composants de produire des composants administrables et aux vendeurs d'outils d'administration d'utiliser cette interface pour administrer ces composants. Contrairement à SNMP, JMX prend en compte les différents métiers intervenants dans la conception et l'administration d'application.

JMX fait partie du kit de développement Java depuis la version 5.0. Il a subi dans la version 6.0 de très brèves améliorations.

Actuellement, JCP réalise la JSR 262 qui définit un connecteur JMX utilisant les web services, pour permettre une administration native des MBeans. Les clients JMX utilisant ce type de connecteur n'auront pas à être écrits en Java.

I-B - les MBeans

Un MBean est un objet Java qui constitue une interface de communication avec un élément logiciel ou physique. Plus précisément, un MBean est un JavaBean respectant les designs patterns lexiques suivants.

1. Le nom de l'interface correspond au nom de la classe du MBean suffixé par la chaîne ''MBean''. Ainsi le nom de l'interface correspondant au MBean de classe X sera XMBean.

2. Pour chaque attribut public représentant la ressource administrée, il doit exister une méthode de type get et/ou set permettant respectivement de lire et/ou de modifier la valeur de cet attribut. Le nom de ces méthodes dépend du nom de l'attribut associé. La déclaration de ces méthodes doit respecter le design pattern lexique suivant :

public AttrType getAttrName();   // "getter"
public void setAttrName(AttrType val);   // "setter"

AttrType peut être n'importe quel type Java, AttrName est sensible à la casse, donc getattr1() et getAttr1() définissent deux attributs différents respectivement : Attr1 et attr1. Si le getter et le setter de l'attribut sont présents, alors l'attribut peut être lu et changé (read-write), si le getter est présent, mais pas le setter alors l'attribut est en lecture seule (read-only), enfin si le setter est présent, mais pas le getter alors l'attribut est en écriture seule (write-only). Si AttrType est boolean, alors le getter peut être défini par le design pattern lexique suivant :

public boolean isAttrName();

Un MBean est identifié dans le serveur par son nom d'objet ou ObjectName. Un ObjectName est composé de deux parties, le domaine et les propriétés. Le domaine est une chaine de caractères n'incluant pas le caractère deux-points (:). Si le domaine inclut le caractère étoile (*) ou le point d'interrogation (?), alors l'object name est un pattern. L'étoile (*) signifie n'importe quelle séquence de 0 ou plus caractères, et le point d'interrogation (?) signifie un seul caractère. Les propriétés sont souvent de la forme type=XYZ.

I-C - Activer/Désactiver le MBeanServer

I-C-1 - Activer l'agent JMX

Pour administrer une application Java en utilisant l'API JMX, vous devez faire ceci.

1. Activer l'agent JMX (un autre nom du MBeanServer) lors du lancement de l'application. Vous pouvez activer l'agent JMX pour l'administration locale, pour un client JMX s'exécutant sur le même système et aussi pour l'administration distante, pour un client JMX s'exécutant sur un système distant.

2. Administrer l'application Java avec un client JMX conforme à la spécification JMX, comme JConsole.

Sous la version 5.0 de Java SE, pour permettre aux clients JMX d'accéder à un agent local, vous devez définir la propriété système com.sun.management.jmxremote au lancement de votre application. Par exemple, pour activer l'agent JMX pour l'application Notepad, vous devez lancer l'application avec la commande suivante.

java -Dcom.sun.management.jmxremote -jar Notepad.jar

Dans Java SE 6, il n'est plus nécessaire de définir cette propriété système. N'importe quelle application lancée dans la plateforme Java SE 6 pourra être administrée en local. Vous n'aurez qu'à exécuter l'application avec la ligne de commande suivante :

java -jar Notepad.jar

Une fois Notepad lancée, un client JMX peut administrer l'application Notepad.

I-C-2 - Désactiver l'agent JMX

Dans certains cas, ou pour quelques raisons de sécurité, un développeur peut désactiver l'agent JMX au lancement de son application dans la plateforme Java SE 6. Ceci est fait en exécutant l'application avec le paramètre -XX:+DisableAttachMechanism. Par exemple, pour désactiver l'agent JMX pour l'application Notepad, vous devez lancer l'application avec la commande suivante :

java -XX:+DisableAttachMechanism -jar Notepad.jar

I-D - L'API pour interroger le MBeanServer

Le serveur de MBean (MbeanServer) joue le rôle d'un référentiel qui maintient une liste de tous les MBeans. Toutes les opérations d'administration appliquées sur un MBean sont effectuées à partir de l'API du MBeanServer. Cette API définit des méthodes permettant :

1. D'instancier et d'enregistrer un nouveau MBean dans le serveur. On peut noter que le concepteur d'un MBean peut contrôler la manière dont un MBean est enregistré/déenregistré dans le serveur. Un MBean peut définir des méthodes qui seront appelées avant et après son enregistrement (ou son désenregistrement) ;

2. D'accéder aux attributs d'un MBean et d'invoquer ses opérations.

Il existe six méthodes d'accès aux MBeans enregistrés sur un MBeanServer

Object getAttribute(ObjectName objname, String attrname);
AttributeList getAttributes(ObjectName objname, String[] attrnames);
MBeanInfo getMBeanInfo(ObjectName objname);
Object invoke(ObjectName objname, String method, Object[] params, String[] signature);
void setAttribute(ObjectName objname, Attribute attribute);
AttributeList setAttributes(ObjectName objname,  AttributeList attributes);

Remarquons que :

1. Le paramètre de type ObjectName référence le nom avec lequel le MBean a été enregistré sur le MbeanServer ;

2. Les méthodes getAttribute() et getAttributes() retournent les valeurs des attributs du MBean spécifiés ;

3. Les méthodes setAttribute et setAttributes() modifient les valeurs des attributs du MBean spécifiés ;

4. La méthode invoke() exécute une opération sur le MBean spécifié ;

5. L'objet de type MBeanInfo retourné par la méthode getMBeanInfo() encapsule des métadonnées qui décrivent l'interface du MBean enregistré sous le nom spécifié par ObjectName. La Figure suivante présente la structure statique de ses informations.

Le MBeanServer utilise un MBeanInfo pour implémenter les méthodes d'accès au MBean, le MBeanServer s'assure qu'aucune méthode n'est invoquée et aucun attribut n'est lu ou mis à jour s'il n'est pas décrit dans le MBeanInfo. Un client JMX peut utiliser getMbeanInfo() pour récupérer une description complète de l'interface de gestion du MBean, comme c'est le cas pour JConsole qui utilise MBeanInfo pour afficher des informations sur les MBeans sous forme arborescente.

I-E - JConsole

JConsole est un outil de suivi conforme à la spécification JMX (Java Management Extensions). JConsole utilise l'instrumentation de la machine virtuelle Java pour fournir des informations sur les performances et la consommation des ressources des applications en cours d'exécution sur la plateforme Java. L'exécutable de JConsole se trouve dans JDK_HOME/bin, où JDK_HOME est le répertoire où est installé le JDK. Si ce répertoire est dans la variable d'environnement Path, vous pouvez lancer JConsole simplement en tapant JConsole dans la ligne de commande. Sinon, vous devez taper le chemin complet de l'exécutable.

I-F - Survol des MBean présents par défaut

Dans l'onglet MBeans de jconsole, on peut trouver des MBeans prédéfinis par la machine virtuelle, ces MBeans peuvent fournir des informations diverses sur l'état de la machine virtuelle (mémoire libre, Heap, nombre de classes chargées, nombre de threads en cours d'exécution, etc.). Ces informations peuvent être utiles dans certains cas de diagnostic ou de maintenance.

I-F-1 - Gestion de la mémoire

On peut obtenir l'usage actuel de la mémoire heap en consultant la valeur de l'attribut HeapMemoryUsage du MBean Memory.

I-F-2 - Gestion des threads

Ou obtenir le nombre de threads en cours d'exécution sur la machine virtuelle.

I-F-3 - Gestion des niveaux logs

Le MBean logging offre la possibilité d'effectuer les tâches suivantes :

1. Récupérer le nom du niveau de log associé au loggeur spécifié ;

2. Récupérer la liste des loggeurs actuellement enregistrés ;

3. Récupérer le nom du père d'un loggeur ;

4. Modifier un loggeur spécifique vers le niveau spécifié.

II - Exemple de connexion à un agent JMX

II-A - Création d'un connecteur RMI

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:9999/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

Le code précédent crée un connecteur RMI client qui est configuré pour se connecter à un connecteur RMI serveur créé par un agent JMX. Comme vous le voyez, on définit l'URL de l'agent. Cela permet au connecteur client de récupérer un stub du connecteur RMI serveur via le registre RMI s'exécutant sur le port 9999 dans la machine locale. Le connecteur client créé est une instance de l'interface JMXConnector en appelant la méthode connect() de la classe JMXConnectorFactory. La méthode connect() est appelée avec comme paramètre l'URL de l'agent JMX distant. Une instance de MBeanServerConnection, nommée mbsc, est créée en appelant la méthode getMBeanServerConnection() de l'instance de JMXConnector appelée jmxc. Le connecteur client est maintenant connecté au MBeanServer créé par l'agent JMX distant, il peut dorénavant enregistrer des MBeans et effectuer des opérations d'une manière transparente sur eux.

II-B - Utilisation d'un proxy pour travailler sur un MBean distant

L'utilisation d'un proxy signifie travailler sur un MBean distant, comme si on avait son objet en local, c'est-à-dire pouvoir invoquer ses méthodes comme si l'objet existait en local.

II-B-1 - Sous J2SE 5.0

La création d'un proxy sous J2SE 5.0 est effectuée par la méthode newProxyInstance de la classe javax.management.MBeanServerInvocationHandler. Cette méthode retourne un objet qui peut être casté vers le type de l'interface du MBean.

Exemple :

HelloMBean proxy = (HelloMBean)
MBeanServerInvocationHandler.newProxyInstance(mbsc,name,HelloMBean.class,false);

En supposant par exemple que HelloMBean est défini comme suit :

public interface HelloMBean {
public void sayHello();

public int add(int x, int y);

public String getHelloWord();

public void setHelloWord(String value);
}

Alors, on peut exécuter :

1. proxy.getHelloWord() qui va résulter en un appel de mbsc.getAttribute(name,"HelloWord") ;

2. proxy.setHelloWord("bonjour") qui va résulter en un appel de mbsc.setAttribute(name,new Attribute("HelloWord","bonjour")) ;

3. proxy.sayHello() qui va résulter en un appel de mbsc.invoke(name,"sayHello",null).

II-B-2 - Sous J2SE 6.0

La création d'un proxy sous J2SE 6.0 est effectuée par la méthode newMBeanProxy de la classe javax.management.JMX. Cette méthode retourne directement une instance du MBean sans être obligé de faire un casting, ceci est effectué en utilisant les types génériques de Java.

Exemple :

HelloMBean proxy = JMX.newMBeanProxy(mbsc, name, HelloMBean.class);

Avec HelloMBean définit comme précédemment.

II-C - Utilisation d'un filtre pour récupérer plusieurs MBeans d'un agent JMX

Un utilisateur veut dans certains cas, récupérer un ensemble de MBeans répondant à un critère. Cela peut être effectué avec la méthode queryNames() de l'interface MBeanServerConnection. La méthode queryNames() est définie comme suit :

Set<ObjectName> queryNames(ObjectName name,QueryExp query) throws IOException

Cette méthode récupère le nom de tous les MBeans, le nom d'un ensemble de MBeans spécifié par pattern matching sur l'ObjectName fourni en paramètre et/ou une requête QueryExp.

Paramètres :

1. name - Le pattern du nom d'objet identifiant les noms des MBeans qui doivent être récupérés. Si sa valeur est null alors le nom de tous les MBeans enregistrés sera récupéré ;

2. query - L'expression utilisée comme filtre pour sélectionner les MBeans. Si sa valeur est null, aucun filtre ne sera utilisé pour sélectionner les noms des MBeans.

II-C-1 - Exemple

Set<ObjectName> ensemble = mbsc.queryNames(new ObjectName("Hello:*"),Query.eq(Query.attr("HelloWord"),Query.value("Bonjour")));

La requête précédente permet de récupérer tous les noms d'objets dont le domaine est égal à Hello, et ayant l'attribut HelloWord égal à « Bonjour ». La classe Query fournit une multitude de méthodes pour construire une expression qui servira de filtre pour récupérer les noms des MBeans. parmi ces méthodes on peut citer :

and(QueryExp q1, QueryExp q2) // retourne une expression qui est une conjonction de deux autres expressions.
attr(String name) // retourne une expression représentant la valeur d'un attribut.
eq(ValueExp v1, ValueExp v2) // retourne une expression qui est une égalité de deux autres expressions.
value(String val) // retourne une expression représentant un String.

III - Exemple d'administration d'un MBean avec JConsole

Depuis la version 5.0 de la plateforme Java 2 Standard Edition, JMX est entièrement intégré dans l'API, et un outil qui s'appelle JConsole est fourni avec le JDK, il permet de gérer des MBeans dans des processus locaux, ou distants en utilisant un connecteur RMI.

Nous allons montrer pas à pas comment se fait l'administration d'une ressource grâce à l'outil JConsole.

III-A - Boite de dialogue de connexion

Quand on lance JConsole, on obtient la boite de dialogue de connexion, la boite de dialogue offre deux possibilités, on peut soit se connecter à un processus local ou distant. Dans la capture d'écran suivante, on a trois processus locaux, sms3.jar, appel3.jar, et JConsole lui-même.

Dans notre exemple de démonstration, nous allons administrer un processus local, mais en utilisant une URL distante (avec localhost comme nom de machine).

III-B - Création du MBean et de l'agent JMX

III-B-1 - Le MBean

Le MBean qu'on va utiliser dans cet exemple est défini par la classe et l'interface suivantes :

HelloMBean.java

package org.sdf;

public interface HelloMBean {
public void sayHello();

public int add(int x, int y);

public String getHelloWord();

public void setHelloWord(String value);
}

Hello.java

package org.sdf;

public class Hello implements HelloMBean {

private String helloWord;

public int add(int x, int y) {
return x + y;
}

public String getHelloWord() {
return helloWord;
}

public void sayHello() {
System.out.println(helloWord);
}

public void setHelloWord(String value) {
helloWord = value;
}
}

Dans l'exemple, on peut constaté que la ressource qu'on va instrumenter a un attribut de type String appelé HelloWord qui peut être lu et modifié (get et set), en plus de cet attribut, on a deux opérations : sayHello() et add(int x,int y).

III-B-2 - L'agent JMX

L'agent a pour but de faire la liaison entre les MBeans et les applications d'administration. Il réalise le pont entre les deux et se porte garant de la qualité de service.

Concrètement, un agent JMX se compose d'un MBean Server et de l'ensemble des MBeans enregistrés auprès de lui. Il délivre des services que des connecteurs de la couche services distribués appellent. Toutes les opérations effectuées sur un MBean se font au travers d'un agent (un MBean Server).

Notre agent sera un programme Java qui va créer un MBeanServer et enregistrer une instance de la classe Hello dans ce serveur, et créer un connecteur RMI pour ce serveur, afin qu'on puisse s'y connecter avec JConsole en utilisant l'URL fournie.

Main.java

package org.sdf;

import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class Main {

/**
* @param args
*/
public static void main(String[] args) {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
LocateRegistry.createRegistry(9999);
ObjectName name = new ObjectName("Hello:type=HelloObject");
Hello h = new Hello();
h.setHelloWord("Bonjour");
mbs.registerMBean(h, name);
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi://localhost/jndi/rmi://localhost:9999/server");
JMXConnectorServer cs = JMXConnectorServerFactory
.newJMXConnectorServer(url, null, mbs);
cs.start();
System.out.println("ecoute ....");
while (true) {
System.out.println(h.getHelloWord());
Thread.sleep(3000);
}
} catch (Exception e) {
}
}
}

III-C - Connexion à l'agent

Après avoir compilé et exécuté notre agent, on lance jconsole, et on entre l'URL de l'agent dans la partie remote process.

III-D - Localisation du MBean

Une fois la connexion établie, dans l'onglet MBeans de JConsole, on trouve tous les MBeans enregistrés sur le serveur auquel on s'est connecté dans notre cas, on trouve HelloObject.

III-E - Accès aux attributs du MBean

Une fois le MBean localisé, on peut accéder à ses attributs et éventuellement les changer s'ils ne sont pas read-only, dans l'exemple démonstratif le MBean HelloObject a un attribut qui se nomme HelloWord qui a comme valeur « Bonjour ».

III-F - Invocation des opérations du MBean

On peut également appeler les opérations fournies par un MBean, dans l'exemple démonstratif, on appelle la méthode add avec comme paramètres 5 et 6, elle retourne la valeur 11.

IV - Sécurisation de l'accès aux MBeans

Afin de restreindre l'accès aux MBeans, Sun a mis à disposition des développeurs un mécanisme de contrôle d'accès aux MBeans avec un nom d'utilisateur et un mot de passe. Dans ce qui suit, nous donnerons un exemple démonstratif accompagné de quelques explications.

Remarque

La sécurisation de l'accès aux MBeans avec un mot de passe ne marche qu'en connexion distante, les utilisateurs locaux pourront accéder aux MBeanx sans aucune restriction.

IV-A - Création des variables d'environnement

MainSecurity.java

import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class MainSecurity {

public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
LocateRegistry.createRegistry(9999);
Map env = new HashMap();
env.put("jmx.remote.x.password.file", "jmx.password"); // ligne 1
env.put("jmx.remote.x.access.file", "jmx.access");     // ligne 2
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi://localhost/jndi/rmi://localhost:9999/server");
JMXConnectorServer cs = JMXConnectorServerFactory
.newJMXConnectorServer(url, <b>env</b>, mbs);
cs.start();
while (true) {
Thread.sleep(1000);
}
}
}

Dans l'exemple, on définit deux variables d'environnement : jmx.remote.x.password.file et jmx.remote.x.access.file, ces deux variables doivent avoir respectivement comme valeur le fichier qui contient le nom d'utilisateur et le mot de passe de chaque utilisateur et le fichier qui contient les droits d'accès de chaque utilisateur. La structure de chacun de ces fichiers va être discutée dans ce qui suit.

IV-B - Le fichier jmx.password

jmx.password

selim mouna
sdfsdf 123456

Le format d'une entrée de ce fichier est très simple, il est défini comme suit :

<username> <password>

où username et password sont respectivement le nom d'utilisateur et le mot de passe d'un utilisateur. Dans notre fichier, on a deux utilisateurs, "selim" et "sdfsdf", qui ont respectivement pour mot de passe "mouna" et "123456".

IV-C - le fichier jmx.access

jmx.access

sdfsdf readonly
selim readwrite

Le format d'une entrée de ce fichier est très simple, il est défini comme suit :

<username> <droits>

où username et droits sont respectivement le nom d'utilisateur et le droit d'accès d'un utilisateur. Dans notre cas, l'utilisateur sdfsdf a le droit de lire, mais n'a pas le droit de modifier les attributs des MBeans contrairement à "selim" qui peut lire et modifier les attributs des MBeans.

V - Utilisation de HTML Adaptor

HTML Adaptor est fourni par Sun microsystems avec l'implémentation de référence de JMX (il n'est pas fourni avec J2SE 5.0). HTML Adaptor vous donne la possibilité d'administrer des MBeans en utilisant un navigateur web. Il permet d'interagir avec l'agent afin de voir les MBeans enregistrés et leurs attributs. En d'autres termes, HTML Adaptor est un outil simple et puissant qui vous permet de :

- voir les valeurs des attributs des MBeans s'ils sont readable ;

- changer les valeurs des attributs des MBeans s'ils sont writable ;

- invoquer des méthodes.

Le code suivant illustre un Agent JMX qui utilise HTML Adaptor.

MainAdaptor.java

package org.sdf;

import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.sun.jdmk.comm.HtmlAdaptorServer;

public class MainAdaptor {

/**
* @param args
*/
public static void main(String[] args) {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("Hello:type=HelloObject");
Hello h = new Hello();
h.setHelloWord("Bonjour");
mbs.registerMBean(h, name);
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
adapter.setPort(8000);
mbs.registerMBean(adapter, new ObjectName(
"AgentAdaptor:name=htmladapter,port=8000"));
adapter.start();
while (true) {
System.out.println(h.getHelloWord());
Thread.sleep(3000);
}
} catch (Exception e) {
}

}

}

Pour essayer HTML Adaptor avec cette application, il vous faudra :

1. Télécharger l'implémentation de référence de JMX. Ceci est nécessaire parce que MainAdaptor utilise la classe HTMLAdaptor, qui se trouve dans l'implémentation de référence de JMX, vous avez seulement besoin du fichier jmxtools.jar ;

2. Compiler tous les fichiers .java. Vérifier que jmxtools.jar est dans votre classpath ;

3. Exécuter MainAdaptor. Cette application va être exécutée en tant que serveur ;

4. Se connecter à l'agent JMX en entrant l'URL http://localhost:8000 dans votre navigateur web.

V-A - Agent view

Cette page fournit la liste des MBeans contenus dans cet agent. Vous pouvez filtrer cette liste pour avoir une vue plus raffinée.

V-B - MBean view

Cette page fournit des détails à propos d'un MBean spécifique. Ici vous pouvez lire et modifier les attributs et invoquer les méthodes des MBeans.

VI - Spring JMX

VI-A - Introduction

Le support de JMX dans Spring offre des fonctionnalités simples et transparentes pour intégrer votre application Spring dans une infrastructure JMX. Spring JMX vous permet :

- d'enregistrer automatiquement n'importe quel bean Spring en tant que MBean JMX ;

- de contrôler l'interface d'administration de vos beans avec un mécanisme flexible ;

- d'exposer vos MBeans d'une manière déclarative avec les connecteurs JSR-160 ;

- …

Ces fonctionnalités sont conçues de façon à travailler sur vos composants Spring et JMX indépendamment, dans la plupart des cas, votre application n'a pas besoin d'être au courant de Spring ou JMX pour bénéficier des avantages de Spring JMX.

VI-B - Enregistrement des beans dans un MBeanServer

La classe principale de Spring JMX est MBeanExporter. Cette classe est responsable de prendre en charge et d'enregistrer vos beans Spring dans un MBeanServer. Par exemple, on considère la classe suivante :

package org.sdf;

public class HelloBean implements IHelloBean {

private String helloWord;

public int add(int x, int y) {
return x + y;
}

public String getHelloWord() {
return helloWord;
}

public void sayHello() {
System.out.println(helloWord);
}

public void setHelloWord(String value) {
helloWord = value;
}
}

Pour exposer les propriétés et les méthodes de ce bean en tant qu'attributs et opérations d'un MBean, vous devez simplement configurer une instance de la classe MBeanExporter dans votre fichier de configuration comme suit :

<beans>

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>

<bean id="testBean" class="org.sdf.HelloBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>

</beans>

La définition pertinente de bean du fichier de configuration précédent est le bean exporté. Sa propriété « beans » permet de savoir exactement quels beans doivent être exportés aux MBeanServer. En général la clé de chaque entrée dans la Map beans est utilisée comme ObjectName pour le bean correspondant à la valeur de l'entrée. Avec cette configuration, le bean testBean est exposé en tant que MBean sous le nom bean:name=testBean1.

VI-C - Création d'un MBeanServer

Dans la configuration précédente, on a supposé que l'application est exécutée dans un environnement qui a un MBeanServer en cours d'exécution. Dans ce cas, Spring va essayer de localiser le MBeanServer en cours d'exécution et enregistrer vos beans dans ce serveur. Cette fonctionnalité est très utile quand votre application est exécutée dans un conteneur comme Tomcat ou Jetty qui a son propre MBeanServer. Toutefois, cette approche n'est pas utilisée si votre application est de type standalone, ou si elle est exécutée dans un conteneur qui n'offre pas un MBeanServer. Pour contourner ce problème, vous pouvez créer une instance de MBeanServer de façon déclarative, en ajoutant une instance de la classe org.springframework.jmx.support.MBeanServerFactoryBean dans votre configuration. Vous pouvez aussi vous assurer que le MBeanServer que vous avez créé sera utilisé en positionnant la propriété server de l'instance de MBeanExporter à la valeur retournée par MBeanServerFactoryBean.

Exemple :

<beans>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>

<bean id="testBean" class="org.sdf.HelloBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>

</beans>

VI-D - Création d'un connecteur serveur

Spring JMX offre la possibilité de créer un connecteur serveur pour permettre l'accès au MBeanServer à distance. Pour cela il utilise le connecteur JSR-160.

Exemple :

<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:9999/server"/>
</bean>

VI-E - Sécurisation de l'accès au MBeanServer

Comme vu précédemment pour la sécurisation des MBeans, Spring JMX offre un mécanisme de sécurité semblable à celui vu précédemment. Pour cela il suffit de définir la propriété environnement du connecteur.

Exemple :

<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:9999/server"/>
<property name="environment">
<map>
<entry key="jmx.remote.x.password.file" value="jmx.password"/>
<entry key="jmx.remote.x.access.file" value="jmx.access"/>
</map>
</property>
</bean>

À noter que pour utiliser le connecteur RMI, une instance de RMIRegistry doit être lancée. Vous pouvez facilement lancer le registre RMI en utilisant ce bout de configuration :

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="9999"/>
</bean>

VII - Les MXBeans

VII-A - Introduction

La chose la plus importante qu'apportent les MXBeans est que n'importe quel client JMX sera capable de les gérer. Les MXBeans offrent un moyen convenable de rassembler des attributs, sans que le client soit configuré pour gérer cet ensemble. Les MXBeans se déclaraient de la même façon que les MBeans classiques, c'est-à-dire le nom de l'interface suffixé avec "MXBeans", mais contrairement aux MBeans classiques, pour une interface quelconque nommée "XYZMXBean", il n'est plus obligatoire de nommer la classe "XYZ", chaque méthode dans l'interface va définir un attribut ou une opération du MXBean. L'annotation @MXBean peut aussi être utilisée pour annoter l'interface Java, au lieu de suffixer le nom de cette interface avec MXBean.

VII-B - Problème avec les MBeans

Dans la conception d'une application, un développeur pourrait être contraint de rassembler quelques attributs dans une même classe, pour faciliter la lecture ou pour des soucis de design.

Exemple :

package org.sdf;

public class User {
private String nom;
private String prenom;
private int numero;
private String rue;
private String ville;

public int getNumero() {
return numero;
}

public void setNumero(int numero) {
this.numero = numero;
}

public String getRue() {
return rue;
}

public void setRue(String rue) {
this.rue = rue;
}

public String getVille() {
return ville;
}

public void setVille(String ville) {
this.ville = ville;
}

public void setNom(String nom) {
this.nom = nom;
}

public void setPrenom(String prenom) {
this.prenom = prenom;
}

public String getNom() {
return nom;
}

public String getPrenom() {
return prenom;
}
}

Le développeur de cette classe pourrait rassembler les attributs numero, rue et ville dans une seule et même classe.

Ceci donnera :

package org.sdf;

public class Adresse {
private int numero;
private String rue;
private String ville;

public Adresse(int numero, String rue, String ville) {
super();
this.numero = numero;
this.rue = rue;
this.ville = ville;
}

public int getNumero() {
return numero;
}

public void setNumero(int numero) {
this.numero = numero;
}

public String getRue() {
return rue;
}

public void setRue(String rue) {
this.rue = rue;
}

public String getVille() {
return ville;
}

public void setVille(String ville) {
this.ville = ville;
}
}

Et la classe User sera simplement comme suit :

package org.sdf;

public class User {
private String nom;
private String prenom;
private Adresse adresse;

public Adresse getAdresse() {
return adresse;
}

public String getNom() {
return nom;
}

public String getPrenom() {
return prenom;
}

public void setAdresse(Adresse adresse) {
this.adresse = adresse;
}

public void setNom(String nom) {
this.nom = nom;
}

public void setPrenom(String prenom) {
this.prenom = prenom;
}
}

Maintenant, supposons que le développeur veut faire un MBean de la classe User, il n'aura qu'à implémenter une interface nommé UserMBean définie comme suit :

package org.sdf;

public interface UserMBean {

public String getNom();

public void setNom(String nom);

public String getPrenom();

public void setPrenom(String prenom);

public Adresse getAdresse();

public void setAdresse(Adresse adresse);

}

Lorsque le développeur enregistrera un MBean de la classe user dans un MBeanServer, un problème va survenir lors de l'accès à ce MBean avec un client JMX tel que JConsole.

Comme illustré dans la capture d'écran, JConsole sera incapable de récupérer la valeur de l'adresse, il va la marquer Unavailable.

VII-C - Comment éviter ce problème avec les MXBeans

La déclaration d'un MXBean est très similaire à celle d'un MBean. L'interface UserMBean de l'exemple précédent sera définie comme un MXBean comme suit :

UserMXBean.java

package org.sdf;

public interface UserMXBean {

public String getNom();

public void setNom(String nom);

public String getPrenom();

public void setPrenom(String prenom);

public Adresse getAdresse();

public void setAdresse(Adresse adresse);
}

Pour permettre aux clients JMX de gérer la valeur de l'attribut Adresse, on doit ajouter l'annotation @ConstructorProperties({"numero", "rue", "ville"}) devant le constructeur de la classe Adresse, comme suit :

Adresse.java

package org.sdf;

import java.beans.ConstructorProperties;

public class Adresse {
private int numero;
private String rue;
private String ville;

@ConstructorProperties( { "numero", "rue", "ville" })
public Adresse(int numero, String rue, String ville) {
super();
this.numero = numero;
this.rue = rue;
this.ville = ville;
}

public int getNumero() {
return numero;
}

public void setNumero(int numero) {
this.numero = numero;
}

public String getRue() {
return rue;
}

public void setRue(String rue) {
this.rue = rue;
}

public String getVille() {
return ville;
}

public void setVille(String ville) {
this.ville = ville;
}
}

Cette annotation permet de faire savoir au client JMX que tel paramètre du constructeur est associé à tel attribut. Lorsqu'un client JMX accède à ce MXBean, il pourra gérer la valeur de l'attribut adresse.

VIII - Téléchargements des sources

Télécharger

IX - Conclusion

Le modèle de programmation JMX est à la fois simple et puissant, il peut refléter le comportement des différentes ressources matérielles ou logicielles en utilisant les conventions de la technologie JavaBeans.

X - Remerciements

Tout d'abord, je tiens à remercier RideKick et acacia d'avoir pris la peine de relire et corriger cet article.

Je remercie également Ricky81 et hikage pour leurs précieux commentaires. Car sans eux, cet article n'aurait pas vu le jour.