Exposer un MBean avec Spring dans WebLogic Server 8.1

La spécification liée aux extensions de management, plus connue sous le nom de JMX est l’un des plus ancienne du JCP. Elle porte le n°3. Cependant, elle n’a été validée qu’en Juillet 2000 et communément utilisée que très récemment. Il a fallut attendre les serveurs d’application J2EE 1.4 ou le JSE 5 pour voir cette technologie devenir massivement diffusée en version 1.2. Auparavant chaque éditeur proposait sa propre implémentation avec des versions incompatibles entre elles.

Le serveur d’application WebLogic 8.1 utilise massivement cette technologie pour permettre de configurer et monitorer les serveurs. La console d’administration n’est qu’une interface graphique à ces MBeans (Management Bean). Cependant il peut être intéressant d’exposer des MBean supplémentaires :

  • MBean type ‘Facade’ : Aggregation de MBeans existants pour unifier la gestion et limiter les appels
  • MBean type ‘Composite’ : Exposition de méthodes de haut niveau qui synthétisent un ensemble de MBean et de données techniques
  • MBean type ‘Applicatifs’ : Interface de gestion non plus au niveau technique (serveur d’application) mais fonctionelle

Voici donc les étapes à suivre et les pièges à éviter pour exposer un MBean avec le framework Spring 2.0 dans WebLogic 8.1. L’environnement de développement est Eclipse 3.2 avec WTP.

  • Créons un projet de type ‘Dynamic Web Project’ (nom WeblogicSpringMBean) avec le target runtime ‘Generic BEA WebLogic Server 8.1′
  • Ajoutons au répertoire WEB-INF/lib, les bibliothèque spring.jar et common-logging.jar.
  • Implémentons notre MBean de test (très simple): fr.xebia.management.InfoMBean

package fr.xebia.management;

public class InfoMBean {
	private String name;

	private int age;

	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Le framework Spring va nous permettre d’assembler et d’exposer ce MBean.

  • Créons dans le répertoire WEB-INF le fichier d’assemblage Spring applicationContext.xml
  • Déclarons le bean associé au MBean InfoMBean
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" 
     "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
 <bean id="infombean" class="fr.xebia.management.InfoMBean">
   <property name="name" value="xebia"></property>
   <property name="age" value="2"></property>
 </bean>
</beans> 
  • Déclarons le server de MBeans de WebLogic
<bean id="weblogicMBeanServer" 
   class="org.springframework.jmx.support.WebLogicJndiMBeanServerFactoryBean"/>
  • Déclarons le bean qui va permettre d’exposer le bean en tant que MBean (org.springframework.jmx.export.MBeanExporter)
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
	<property name="beans">
		<map>				
			<entry key="bean:name=testBean" value-ref="infombean" />
		</map>
	</property>
	<property name="server" ref="weblogicMBeanServer" />		
</bean>

Il reste à paramétrer le fichier web.xml pour permettre de charger cette configuration au démarrage du serveur. La servlet org.springframework.web.context.ContextLoaderServlet va se charger, au démarrage, d’assembler les beans à partir du fichier applicationContext.xml et d’exposer le MBean.

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_ID">
  <display-name>WebLogicSpringMBean</display-name>
  <servlet>
	<servlet-name>context</servlet-name>
	<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
	<load-on-startup>1</load-on-startup>		
  </servlet>
</web-app>

Si on se base sur les différentes documentations [1] c’est terminé.
Dans l’environnement Eclipse, démarrer le serveur et deployer l’application (La commande ‘Run On Server’ se charge des deux opérations).

Première Difficulté : Lors du déploiement de l’application WeblogicSpringMBean.war, le serveur répond :

javax.servlet.ServletException: Error creating bean with name 'weblogicMBeanServer' defined in ServletContext resource
[/WEB-INF/applicationContext.xml]: Invocation of init method failed;
nested exception is org.springframework.jmx.MBeanServerNotFoundException: Could not find WebLogic's MBeanHome object in JNDI;
nested exception is javax.naming.NoPermissionException: User does not have permission on weblogic.management.home to perform lookup operation.

Solution :il faut exécuter la servlet avec le rôle Admin

  • Editer le fichier web.xml pour déclarer le rôle admin
<web-app id="WebApp_ID">
	<display-name>WebLogicSpringMBean</display-name>
	<servlet>
		<servlet-name>context</servlet-name>
		<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
		<run-as>
			<role-name>admin</role-name>
		</run-as>
	</servlet>
	<security-role>
		<role-name>admin</role-name>
	</security-role>
</web-app>
  • Créer dans le répertoire WEB-INF un fichier weblogic.xml qui va associer le rôle admin avec le user weblogic
<weblogic-web-app>
	<security-role-assignment>
		<role-name>admin</role-name>
		<principal-name>weblogic</principal-name>		
	</security-role-assignment>
</weblogic-web-app>
  • Redéployer l’application

Ecrivons un client de test pour interroger notre MBean. Pour des soucis de commodités, nous allons le coder dans le même projet que notre MBean. Le code suivant permet retrouver tous les MBean dont le nom est ‘testBean’.

public static void main(String[] args) throws Exception {			
	Environment env = new Environment();
	env.setProviderUrl("t3://localhost:7001");
	env.setSecurityPrincipal("weblogic");
	env.setSecurityCredentials("weblogic");
	Context ctx = env.getInitialContext();
	
	System.out.println("Recherche du MBeanHome");
	MBeanHome home  = (MBeanHome) ctx.lookup(MBeanHome.LOCAL_JNDI_NAME);
	RemoteMBeanServer server = home.getMBeanServer();
	System.out.println("Recherche de tous les MBeans dont le nom est 'testBean' [*:*,name=testBean]");
	Set results = server.queryMBeans(new ObjectName("*:*,name=testBean"),
			null);
	for (Iterator iter = results.iterator(); iter.hasNext();) {
		ObjectInstance oi = (ObjectInstance) iter.next();
		ObjectName objectName = oi.getObjectName();
		System.out.println(objectName);			
	}
}

Sur la sortie console on obtient :

Recherche du MBeanHome
Recherche de tous les MBeans dont le nom est 'testBean' [*:*,name=testBean]
bean:name=testBean

Donc notre MBean est exposé sur le serveur WebLogic.

Rajoutons une ligne de code pour obtenir la valeur de l’attribut MBean (JMXClient2.java)

for (Iterator iter = results.iterator(); iter.hasNext();) {
  ObjectInstance oi = (ObjectInstance) iter.next();
  ObjectName objectName = oi.getObjectName();
  System.out.println(objectName);
  System.out.println(server.getAttribute(objectName, "Name"));
  System.out.println(server.getAttribute(objectName, "Age"));
}

Deuxième Difficulté : Sur l’appel de la méthode server.getAttribute(), le serveur lance une Exception :

javax.management.ReflectionException: The target object class class javax.management.modelmbean.ModelMBeanOperationInfo could not be found.

Cette exception indique un problème lié au code de WebLogic géré par le classloader système qui doit accéder à une classe chargée par le classloader de l’application Web. Le classloader d’un module J2EE (jar,war,ear) dérive du classloader système, et non l’inverse.

Solution : D’après le Bug Tracker de Spring [2], il faut, pour exposer notre MBean, utiliser une bibliothèque tierce packagée dans l’application Web: Le projet Apache Common Modeler [3]

  • Copions le fichier commons-modeler-2.0.jar dans le répertoire WEB-INF/lib
  • Dérivons la classe org.springframework.jmx.export.MBeanExporter et surchargeons la méthode createModelMBean()
package fr.xebia.management;

import javax.management.MBeanException;
import javax.management.modelmbean.ModelMBean;

import org.springframework.jmx.export.MBeanExporter;

public class CommonModelerBeanExporter extends MBeanExporter {
	protected ModelMBean createModelMBean() throws MBeanException {
		return new org.apache.commons.modeler.BaseModelMBean();
	}
}
  • Modifions l’assemblage Spring pour utiliser cette classe à la place MBeanExporter
<bean id="exporter" class="fr.xebia.management.CommonModelerBeanExporter">
	<property name="beans">
		<map>				
			<entry key="bean:name=testBean" value-ref="infombean" />
		</map>
	</property>
	<property name="server" ref="weblogicMBeanServer" />		
</bean>

Apres avoir redéployé l’application, le client de test JMXClient2.java permet d’afficher la valeur de l’attribut ‘Name’ et ‘Age’ du MBean

class test.xebia.management.JMXClient2
Recherche du MBeanHome
Recherche de tous les MBeans dont le nom est 'testBean' [*:*,name=testBean]
bean:name=testBean
2

Les valeurs affichées sont celles indiqués dans le fichier d’assemblage Spring.

Le client de test JMXClient3 modifie la valeur des attribut du MBean.

class test.xebia.management.JMXClient3
Recherche du MBeanHome
Recherche de tous les MBeans dont le nom est 'testBean' [*:*,name=testBean]
bean:name=testBean
Modification de l'attribut Name=J2EE
Modification de l'attribut Age=4

Si on relance le client de test JMXClient2, on doit obtenir les valeur J2EE et 4: L’instance du MBean est unique sur le server WebLogic.

class test.xebia.management.JMXClient2
Recherche du MBeanHome
Recherche de tous les MBeans dont le nom est 'testBean' [*:*,name=testBean]
bean:name=testBean
J2EE
4

Le projet Eclipse est disponible : weblogicspringmbean.zip. Il n’inclut pas le fichier spring.jar.

Avec le support en natif par la JVM, la technologie JMX va devenir de plus en plus présente dans les applications J2EE pour offrir une gestion à deux niveaux :

  • gestion technique : paramétrage de niveaux de log, accès à des statistiques (Pools de Connexion, Transaction, …)
  • gestion fonctionnelle : par exemple, l’application va pouvoir exposer elle-même des indicateurs à destinations des utilisateurs et pilotes fonctionnels. Aujourd’hui, ces indicateurs sont généralement calculés à grands coups de requêtes dans la base de données.

Un prochain post montrera comment accéder à ce bean en utilisant le langage WLST (WebLogic Scripting Tool).

Référence:

[1] Documentation Spring 2.0 JMX
[2] JIRA Spring
[3] Apache Common Modeler

Billets sur le même thème :

5 commentaires

  • Pour contourner le problème cité :
    javax.naming.NoPermissionException: User does not have permission on weblogic.management.home to perform lookup operation.

    une solution consiste à aller dans la console d’administration du domaine, sélectionner « View Domain-wide Security Settings » et cocher « Anonymous Admin Lookup Enabled »

  • Effectivement, c’est une solution mais le niveau de sécurité de l’ensemble du domaine est abaissé.
    Pour contourner le problème sans ouvrir cette brèche, on peut utiliser une API interne de weblogic:
    class weblogic.management.Admin
    méthode static
    Admin.getInstance().getMBeanHome().getMBeanServer()

    Pour l’utiliser dans la configuration Spring, le plus simple est de créer un POJO qui encapsule l’appel à la méthode statique.

  • Bonjour,

    Tout d’abord merci à l’auteur pour cet excellent article.
    C’est véritablement dans l’esprit J2EE de partager ses connaissances pour le bien de la communauté.

    J’ai récemment appris que l’on pouvait manager des ‘custom’ beans dans la console weblogic (intérêt évident pour des équipes de production).
    Je voulais savoir ou l’on peut se procurrer les 2 jars qui permettent d’activer cette interface dans la console weblo (console-ext-server.jar et console-ext-client.jar).

    Cordialement,

    Louis

  • @Louis Gueye
    Merci pour vos encouragements! Le partage de la connaissance est l’un des piliers de Xebia.

    Il est clair que l’exposition d’un MBean n’est qu’une partie: il faut généralement une interface graphique pour le piloter (JConsole étant très primitif). Cependant les extensions que vous mentionnez n’existe qu’à partir de la version 9 de Weblogic Server. [https://wls-console-extensions.projects.dev2dev.bea.com/]

Laisser un commentaire