Emmanuel PIERRE - Tutoriels et Astuces en Java

Aller au contenu | Aller au menu | Aller à la recherche

lundi 21 juillet 2008

Superviser une application java avec Spring et JMX (2)

Détails :
Dans le premier tutoriel Superviser une application java avec Spring et JMX (1), nous avons exposé avec Spring un MBean permettant de ré-initialiser un service de configuration sans redémarrer le serveur.
Cependant par défaut Spring expose toutes les propriétés et méthodes publiques du bean. Entre autres la méthode

public void setConfigurationService(ConfigurationService configurationService) { }

Or celle-ci ne doit pas être visible dans la JConsole. Mais elle est nécessaire pour effectuer l'injection du service de configuration.
Nous allons donc utiliser des annotations JDK 5.0 pour restreindre les attributs et méthodes exposées. D'autres méthodes peuvent être utilisées. Pour plus de renseignements, voir Controlling the management interface of your beans


(Les sources du projet sont disponibles en annexe)

1- Modification de l'implémentation du MBean

@ManagedResource(
	objectName="bean:name=configurationServiceMBean2", 
	description="MBean permettant de manager le service de configuration", 
	log=true,
	logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate", 
	persistPeriod=200,
	persistLocation="foo", persistName="bar")
public class ConfigurationServiceMBeanImpl implements ConfigurationServiceMBean {
 
	private static Log logger = LogFactory.getLog(ConfigurationServiceMBeanImpl.class);
 
	private ConfigurationService configurationService;
 
	@ManagedOperation(description="Re-initialise le service de configuration")
	public void reInitConfigurationService() {
		logger.info("Debut de la re-initialisation du service de configuration");
		configurationService.init();
		logger.info("Fin de la re-initialisation du service de configuration");
	}
 
	/**
	 * @param configurationService the configurationService to set
	 */
	public void setConfigurationService(ConfigurationService configurationService) {
		this.configurationService = configurationService;
	}
 
}

L'annotation @ManagedOperation permet de spécifier que l'on veut exposer la méthode public void reInitConfigurationService().
La méthode setConfigurationService ne doit pas être exposée. Donc on n'ajoute aucune annotation.
Pour exposer une propriétée, via les getters et setters, on utilisera l'annotation @ManagedAttribute.

2- Modification du fichier applicationContext.xml pour prendre en compte les annotations
La déclaration du MBean est la même :

<!-- MBean permettant d'administrer le Service de configuration -->
<bean id="configurationServiceMBean" class="fr.minimarmotte.projects.mbean.impl.ConfigurationServiceMBeanImpl">
	<property name="configurationService" ref="configurationService" />
</bean>

Le reste doit être modifié.

On va tout d'abord spécifier quelles méta data nous utilisons dans nos classes. Nous avons choisis les annotations JDK 5.0 :

<!-- Meta data utilisées dans les classes : annotations JDK 5.0 -->
<bean id="jmxAttributeSource"
          class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

Nous déclarons ensuite un assembleur qui va créer une interface à partir de la classe, en utilisant les annotations (pour afficher uniquement les méthodes et attributs que l'on veut exposer) :

<!-- Assembleur qui va créer une interface de management en utilisant les meta data -->
<bean id="assembler"
	class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
	<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>

Puis on définit la stratégie permettant de récupérer le nom du bean à exposer :

<!-- Méthode pour determiner le nom du MBean a exposer : on utilise les annotations -->
<bean id="namingStrategy"
	class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
	<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>

Dans notre cas, on spécifie que le nom du MBean est défini à l'aide des annotations par l'attribut objectName="bean:name=configurationServiceMBean2".

Enfin on déclare notre exporter qui va exposer notre MBean:

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
	<property name="assembler" ref="assembler"/>
	<property name="namingStrategy" ref="namingStrategy"/>
	<property name="autodetect" value="true"/>
</bean>

En mettant la propriété autodetect à true, on indique que l'on souhaite exposer tous les MBeans annotés avec l'annotation @ManagedResource (au lieu d'utiliser une liste de MBean).

3- Test des modifications
On redémarre le serveur. Dans les logs on constate que le nom du MBean exposé est bien configurationServiceMBean2, défini grâce aux annotations.

INFO: Located managed bean 'configurationServiceMBean': registering with JMX server as MBean [bean:name=configurationServiceMBean2]

On lance JConsole et on se connecte. On trouve le MBean configurationServiceMBean2. Et contrairement au premier tutoriel, seule la méthode reInitConfigurationService() est disponible. Les attributs et autres méthodes ne sont pas accéssibles !

Superviser une application java avec Spring et JMX (1)

Détails :
Dans les tutoriels Créer un service de configuration avec Spring 1,2 et 3, nous avons créé un service qui permet d'initialiser des propriétés de configuration à partir d'un fichier .properties. Ces propriétés peuvent alors être utilisées dans toutes les servlets.
Si une des propriétés du fichier .properties est changée, il faut redémarrer le serveur pour qu'elle soit prise en compte, ce qui est un gros inconvénient dans un environnement de production.
Pour palier à ce problème, nous allons créer un bean de management : MBean et l'enregistrer sur le serveur. La console JConsole, fournie avec la jdk 1.5, va nous permettre d'accéder à ce MBean et d'en exécuter certaines méthodes et entre autre la méthode permettant de réinitialiser le service de configuration, sans redémarrer le serveur.

Définition JMX : Java Management eXtension ou JMX est une API java permettant de surveiller une application java existante. Cette technologie permet également d’intéragir sur l’application en cours d’exécution.

Projet réalisé avec :
Eclipse Europa 3.3.1.1
Apache Tomcat 5.5.26
Plugin Maven pour Eclipse http://m2eclipse.codehaus.org/
Java JDK 1.5
Spring 2.5.5


(Les sources du projet sont disponibles en annexe)

1- Création du MBean et de son interface
Nous souhaitons créer un bean de management dont le seul rôle est de permettre la réinitialisation du service de configuration ConfigurationService. Nous créons donc une interface très simple :

public interface ConfigurationServiceMBean {
 
	/**
	 * Ré-initialise le service de configuration.
	 */
	void reInitConfigurationService();
}

Puis nous créons une implémentation de ce service :

public class ConfigurationServiceMBeanImpl implements ConfigurationServiceMBean {
 
	private static Log logger = LogFactory.getLog(ConfigurationServiceMBeanImpl.class);
 
	private ConfigurationService configurationService;
 
	public void reInitConfigurationService() {
		logger.info("Debut de la re-initialisation du service de configuration");
		configurationService.init();
		logger.info("Fin de la re-initialisation du service de configuration");
	}
 
	/**
	 * @param configurationService the configurationService to set
	 */
	public void setConfigurationService(ConfigurationService configurationService) {
		this.configurationService = configurationService;
	}
 
}



2- Configuration du fichier applicationContext.xml pour exposer notre MBean

Déclaration du MBean avec injection du service de configuration :

<bean id="configurationServiceMBean" class="fr.minimarmotte.projects.mbean.impl.ConfigurationServiceMBeanImpl">
	<property name="configurationService" ref="configurationService" />
</bean>

La configuration suivante permet de localiser un serveur existant et de s'y connecter (ici on utilise un serveur tomcat) :

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
     	<property name="locateExistingServerIfPossible" value="true"/>
</bean>

Enfin notre MBean va devoir signaler sa présence au MBeanServer. On utilise pour cela un MBeanExporter, pour lequel on spécifie la liste des MBean à exposer sur un MBeanServer en particulier :

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


Pour plus de détails, se référer à Spring 2.5 reference chapter 20 JMX.

3- Configuration de la JVM et démarrage du serveur
Afin de pouvoir utiliser la JConsole, nous devons tout d'abord ouvrir le connecteur JMX de la JVM. On va donc passer quelques options à la JVM. Dans Eclipse, on fait un clique droit sur le serveur tomcat, Open. Puis on clique sur le lien Open Launch Configuration : Eclipse overview serveur

On clique sur l'onglet Arguments et dans la partie VM arguments, on rajoute l'option suivante : -Dcom.sun.management.jmxremote Eclipse launch configuration properties

En local (JConsole et serveur sur la même machine), cette option suffit. Mais il est possible d'administrer un serveur à distance. Dans ce cas d'autres paramètres doivent être ajoutés. Pour plus d'informations, voir la doc de sun. Remote Monitoring and Management.

Il nous reste à démarrer le serveur et constater dans les logs que notre MBean a bien été enregistré :

21 juil. 2008 12:09:30 org.springframework.jmx.export.MBeanExporter afterPropertiesSet
INFO: Registering beans for JMX exposure on startup
21 juil. 2008 12:09:30 org.springframework.jmx.export.MBeanExporter registerBeanInstance
INFO: Located managed bean 'bean:name=configurationServiceMBean': registering with JMX server as MBean [bean:name=configurationServiceMBean]


4- Accès au MBean dans la JConsole
Sous windows, dans le dossier d'installation de la jdk, aller dans le dossier bin et lancer jconsole.exe. JConsole connexion On clique sur connect.

Un certains nombres d'onglets sont disponibles. Ils offrent diverses fonctionnalités de supervision et d'administration. Pour plus de détails, voir l'article de Sun : Using JConsole to Monitor Applications.

Aller sur l'onglet MBeans, puis rechercher le MBean que nous avons enregistré : bean/congigurationServiceMBean. JConsole mbean configuration service 1

Dans les logs on peut voir l'erreur suivante :

21 juil. 2008 13:45:30 RequiredModelMBean getAttributes(String[])
GRAVE: Failed to get "ConfigurationService": javax.management.AttributeNotFoundException: getAttribute failed: ConfigurationService is not readable 

Ne pas en tenir compte pour le moment...

Pour ré-initialiser le service de configuration, aller dans l'onglet Operations et cliquer sur le bouton reInitConfigurationService. Un message de succès apparaît. Et on peut voir dans les logs :

21 juil. 2008 13:52:11 fr.minimarmotte.projects.mbean.impl.ConfigurationServiceMBeanImpl reInitConfigurationService
INFO: Debut de la re-initialisation du service de configuration
21 juil. 2008 13:52:11 fr.minimarmotte.projects.mbean.impl.ConfigurationServiceMBeanImpl reInitConfigurationService
INFO: Fin de la re-initialisation du service de configuration

indiquant que notre manipulation a bien eu un impact sur l'application en cours, sans pour autant redémarrer le serveur.

Pour vérifier que la ré-initialisation fonctionne correctement, aller sur la page http://127.0.0.1:8080/mp-spring-jmx-1/afficher_messages
La valeur correspond à la clé "cle.test.resource" est affichée.
Nous allons changer cette propriété dans le fichier C:/temp/configurationService.properties :
Avant : cle.test.resource = il y a bien une resource
Après : cle.test.resource = clé changée
Si on ré-affiche la page http://127.0.0.1:8080/mp-spring-jmx-1/afficher_messages, on voit qu'aucun changement n'a été pris en compte. Pour que les changements soient pris en compte, soit on redémarre le serveur, soit on réinitialise le service de configuration grâce à la JConsole. On re-clique donc sur le bouton reInitConfigurationService.
Et on ré-affiche la page http://127.0.0.1:8080/mp-spring-jmx-1/afficher_messages On s'aperçoit alors que la valeur correspondant à la clé "cle.test.resource" a bien été modifiée !!!