mardi 30 octobre 2007

Utilisation des JavaAgents

En étudiant le fonctionnement de KODO en standalone, j’ai été amené à utiliser un concept qui m’était inconnu : les JavaAgents. C'est une nouveauté de Java 5 pour laquelle on trouve très peu de documentation. Je vous propose donc d'expliquer simplement son utilisation.

Définition

Un JavaAgent est une librairie « pluggable » qui tourne dans une JVM embarqué (celle du programme exécuté) et qui intercepte les chargements de classes. Cela permet donc de suivre le processus de chargement des classes d’un programme java et, si nécessaire, de modifier à chaud le comportement de la classe (mécanisme d’intercession).

Concrètement un JavaAgent se présente sous la forme d’un simple JAR contenant une classe implémentant une méthode :

public static void premain(String agentArgs, Instrumentation inst)

Le premier argument de cette méthode est un String représentant les arguments à passer à l’agent (à parser sois même avec pipe, délimiteur,…). Le deuxième est l’instance d’Instrumentalisation. C’est grâce à cette classe que l’on ajoute les transformers voulus.

ClassTransformer

Les classes implémentant l’interface ClassFileTransformer sont créées pour effectuer des modifications dans le bytecode de classes existantes. Leur implémentation est très simple puisqu’il suffit d’implémenter l’unique méthode de l’interface ClassFileTransformer.

Création d’un agent java simple

Dans cet article je vous propose de créer un agent java très simple. Il aura pour but l’affichage dans la sortir standard des classes qui sont chargée par le programme. Pour arriver à notre objectif nous aurons besoin de créer l’agent, son ClassFileTransformer, et d’extraire le tout dans un JAR correctement formaté.

Dans un nouveau projet java créez une classe myJavaAgent comme ceci :

package fr.demo.testAgent;

import java.lang.instrument.Instrumentation;
import
fr.demo.testTransformer.TestClassTransformer;

public class myJavaAgent {
public static void premain(String agentArgs, Instrumentation inst)

{
inst.addTransformer(
new TestClassTransformer());
}
}


La seule opération de l’agent est d’ajouter un classtransformer à l’instance d’instrumentalisation de la JVM du programme executé.


Code du class transformer :

package fr.demo.testAgent;
import
java.lang.instrument.ClassFileTransformer;

import java.lang.instrument.IllegalClassFormatException;

import java.security.ProtectionDomain;

public class EzClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className,

Class classBeingRedefined, ProtectionDomain protectionDomain,

byte[] classfileBuffer) throws IllegalClassFormatException {

System.out.println("Je charge la classe :" + className);

return classfileBuffer;

}

}

Comme vu précédement, l’interface ClassFileTransformer se résume en une méthode à implémenter : transform. Cette méthode passe en paramètre la classe avant qu’elle soit chargée par la JVM. Elle offre la possibilité de modifier le comportement de la classe en ayant comme paramètre de retour un tableau de byte correspondant à la classe compilée. Pour l’instant nous nous contenterons simplement d’afficher que la classe est chargée en renvoyant directement la classe passée en paramètre sans modification.


Création du JAR

Un java agent se présentant sous la forme de JAR, il nous faut donc créer un JAR contenant l’agent java et le classfiletransformer (via éclipse, click droit sur projet, export, java archive). Dans le fichier META-INF/MANIFEST.MF du jar il faut ajouter l’entrée :

Premain-Class: fr.demo.testAgent.myJavaAgent

Cette entrée permet de définir à la JVM la classe du JAR jouant le rôle de l'agent (bootstrap class).

Une fois le JAR crée il ne nous reste plus qu’à le tester. Pour ce faire prenez une application java de votre choix (console, swing…) et éxécutez la simplement en ajoutant l’argument (via éclipse « open run dialog » et « arguments ») :

-javaagent:lienversjar .

Conclusion

Les AgentsJava sont très utiles pour ajouter des crochets au chargement des classes de la JVM. Certes l’utilisation que nous en avons fait dans cet article est somme toute limitée, mais les classfiletransformer offrent des possibilitée que vous n’osez même pas imaginer! Je proposerais prochainement un article montrant ce méchanisme couplé avec BCEL pour modifier a chaud les bytecode des classes : une façon de voir cette technologie d’un aspect(J?) différent (comprendra qui pourra).

Aucun commentaire: