JMX (Java Management eXtension)! Le seul nom peut faire peur et pourtant, cette technologie peut très facilement être intégrée dans vos applications Java de la plus simple à la plus complexe sans surcout particulier. Elle est en effet incluse dans Java SE depuis la version 1.5. Tout d'abord, à quoi cela peut t'il servir? Le premier usage de base est que JMX permet de surveiller les ressources utilisées par une application (mémoire, CPU, nombre de threads, système d'exploitation, etc). Ces informations sont fournies de base par la machine virtuelle. Vous pouvez cependant rajouter des informations spécifiques à votre application. L'API JMX vous permet de :
- Exposer des propriétés de configuration qui pourront être consultées ou modifiées.
- Exposer des méthodes qui permettront de contrôler l'application pour l'arrêter, démarrer des modules, etc.
- Afficher des notifications d'alarmes
Dans des systèmes en production, JMX peut être intégré à des systèmes de monitoring d'applications tels que Zabbix, Nagios pour n'en citer que quelques uns. Il peut également être utilisé avec JConsole qui est une application gratuite livrée avec le JDK depuis la version 1.5.
Exposer des attributs avec JMX
L'une des utilisations les plus simples de JMX consiste à exposer des propriétés de configuration. Il faut pour cela déclarer une interface et son implémentation. Il n'y a qu'une seule contrainte de nommage de l'interface. Si le nom de la classe est Test, l'interface correspondante doit s'appeler TestMBean. Le MBean ajouté en fin de classe permet ainsi d'indiquer les attributs et méthodes qui seront exposés, la classe d'implémentation peut contenir d'autres classes publiques qui ne seront pas exposées.
Voici un premier exemple avec une classe de configuration de base de données. Je vais donc appeler l'interface DatabaseManagementMBean.
package com.jmxtest.mbeans;
public interface DatabaseManagementMBean {
String getDbUser();
void setDbUser(String user);
String getDbPassword();
void setDbPassword(String password);
String getDbDriver();
void setDbDriver(String dbDriver);
String getDbURL();
void setDbURL(String dbURL);
Long getDbCounter();
}
Notez que la dernière méthode getDbCounter n'a pas de méthode set correspondante ce qui signifie que l'attribut "dbCounter" est en lecture seule. Cela n'empêche pas d'implémenter une méthode setDbCounter dans l'implémentation. Cette dernière ne sera simplement pas visible.
L'implémentation de l'interface doit donc s'appeler DatabaseManagement (sans le MBean).
L'exposition de la classe pour JMX se fait de la manière suivante.
package com.jmxtest.mbeans;
public class DatabaseManagement implements DatabaseManagementMBean {
String dbDriver = "org.apache.derby.jdbc.ClientDriver";
String dbURL = "jdbc:derby://localhost:1527/testjmx";
String dbUser = "linus";
String dbPassword = "ChangeMe";
Integer dbCounter = 0;
public DatabaseManagement() {
}
public String getDbDriver() {
return dbDriver;
}
public String getDbPassword() {
return dbPassword;
}
public String getDbURL() {
return dbURL;
}
public String getDbUser() {
return dbUser;
}
public void setDbDriver(String dbDriver) {
this.dbDriver = dbDriver;
}
public void setDbPassword(String dbPassword) {
this.dbPassword = dbPassword;
}
public void setDbURL(String dbURL) {
this.dbURL = dbURL;
}
public void setDbUser(String dbUser) {
this.dbUser = dbUser;
}
public Integer getDbCounter() {
return dbCounter;
}
public void setDbCounter(Integer dbCounter) {
this.dbCounter = dbCounter;
}
}
L'exposition de la classe pour JMX se fait de la manière suivante.
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.jmxtest.mbeans.DatabaseManagement;
public class TestJMXProperties {
// MBeans
DatabaseManagement databaseConfig;
public TestJMXProperties() {
createMBeans();
}
//Register MBeans
private void createMBeans() {
try {
System.out.println("Creating MBeans");
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
databaseConfig = new DatabaseManagement();
ObjectName databaseName = new ObjectName("TestJMX:name=Database Management");
mbs.registerMBean(databaseConfig, databaseName);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
// Initialize the mbeans
public static void main(String[] args) throws InterruptedException {
TestJMXProperties test = new TestJMXProperties();
Integer counter = 0;
while (true) {
Thread.sleep(10000);
System.out.println("Waiting 10 seconds " + counter);
test.databaseConfig.setDbCounter(counter++);
}
}
}
Exécutez l'application avec les options suivantes :
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=1333
Les nouvelles propriétés peuvent être visualisées avec l'application JConsole. Sélectionnez l'application à superviser.
Avec Java 1.7, la sécurité a été renforcée. JConsole essaye d'abord de se connecter en mode SSL sécurisé. En ayant démarré avec les options indiquées précédemment, vous pouvez vous connecter en mode "Insecure connection".
Après la connexion, allez sur l'onglet MBean puis l'objet TestJMX. Vous verrez la liste des attributs définis dans l'application.
L'attribut dbCounter est en lecture seule. En double-cliquant sur la valeur, elle apparaitra sous forme de graphe qui peut ainsi être sauvegardé.
Vous pouvez également utiliser l'application JVisualVM après avoir rajouté le plugin VisualVM-MBeans.
L'affichage est similaire à JConsole et vous pouvez également profiler votre application.
Exposez des méthodes
Le principe d'exposition des opérations est le même que celui des propriétés. La méthode Java ne doit simplement pas commencer par "set" ou "get" pour être considérée comme une opération.Je vais maintenant définir une classe MainManagement avec son interface MainManagementMBean. Encore une fois, seul le nommage MBean permet d'indiquer qu'il s'agit de l'interface qui expose les méthodes.
L'interface MainManagementMBean contient les méthodes suivantes.
package com.jmxtest.mbeans;
public interface MainManagementMBean {
public void stop();
public void reload();
public void debug(boolean on);
public void save();
}
Je défini ici une implémentation de cette interface juste à titre d'exemple.
package com.jmxtest.mbeans;
// Main Management class
public class MainManagement implements MainManagementMBean {
public static volatile MainManagement _instance = null;
boolean debug = false;
// MBeans
DatabaseManagement databaseConfig; // Database MBean
MainManagement main; // Ini file content and operations
// Constructor that loads the ini file
public MainManagement(){
loadIniFile();
}
// Retrieves a unique instance of the class
public static MainManagement getInstance() {
if (_instance == null)
_instance = new MainManagement();
return _instance;
}
// Private method to load the configuration file
private void loadIniFile() {
System.out.println("Load configuration file");
databaseConfig = new DatabaseManagement();
debug = false;
}
// Private method to save the configuration file
private void saveIniFile() {
System.out.println("Saving ini file");
}
// Turns the application in debug mode.
// This has to be completed according to the debug framework used
public void debug(boolean on) {
System.out.println("Debug=" + on);
debug = on;
}
// Reloads the ini file
public void reload() {
loadIniFile();
}
// Exposed method to save the configuration file
public void save() {
saveIniFile();
}
// Exposed method to stop the application.
// Security must be put in place when this kind of methods are publicly exposed,
public void stop() {
System.out.println("Stop");
System.exit(0);
}
public DatabaseManagement getDatabaseConfig() {
return databaseConfig;
}
}
L'enregistrement du MBean se fait de la même manière que précédemment.
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.jmxtest.mbeans.MainManagement;
import com.jmxtest.mbeans.DatabaseManagement;
public class TestJMX {
// MBeans
DatabaseManagement databaseConfig;
MainManagement main;
public TestJMX() {
createMBeans();
}
//Register MBeans
private void createMBeans() {
try {
System.out.println("Creating MBeans");
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
main = MainManagement.getInstance();
ObjectName mainName = new ObjectName("TestJMX:name=TestJMX Management");
mbs.registerMBean(main, mainName);
databaseConfig = main.getDatabaseConfig();
ObjectName databaseName = new ObjectName("TestJMX:name=Database Management");
mbs.registerMBean(databaseConfig, databaseName);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
// Initialize the mbeans
public static void main(String[] args) throws InterruptedException {
TestJMX testJMX = new TestJMX();
Integer counter = 0;
while (true) {
Thread.sleep(10000);
System.out.println("Waiting 10 seconds");
testJMX.databaseConfig.setDbCounter(counter++);
}
}
}
Les méthodes ainsi exposées sont maintenant visibles dans JConsole et peuvent être appelées.
Voici donc comment avec quelques classes Java tout à fait standard et sans librairie additionnelle il est possible d'offrir une interface permettant de contrôler votre application à distance.
Commentaires
Enregistrer un commentaire