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).

vendredi 26 octobre 2007

Bean Entity StandAlone recherche CRUD générique

Petite classe java utile rédigée par mes soins qui vous permettra de ne pas perdre de temps avec vos opérations de CRUD (Create Read Update Delete ) pour les entityBeans Standalone.

Télécharger la classe

Pour l'utiliser il suffit simplement d'hériter de cette classe et d'ajouter un contructeur. Exemple avec une DAO d'un objet "Secteur" :

public class SecteurDAO extends GenericDAO {


public SecteurDAO(String entityManagerName)
{
super(entityManagerName,Secteur.class);

}
}
Bean Entity

Les beans entity doivent posséder des namedqueries pour définir comment les sélectionner. Exemple :
@Entity
@NamedQueries(value ={
@NamedQuery(query="Select a from Agence a where a.id = ?1"
,name="SelectByIdAgence") ,
@NamedQuery(query="Select a from Agence a"
,name="SelectAllAgence")})
public class Agence implements Serializable {
Il est important de garder le même nom dans les NamedQueries :SelectById et SelectAll + nom de la classe. On ajoute le suffixe de la classe car on ne peut pas avoir deux mêmes noms pour des requêtes au sein d'une même unité de persistance : Merci KODO ...

Limitations
Cette classe ne fonctionne que pour les tables ayant une clé primaire non composée (je ferai plus tard une RTB "Release to Bloggers" en ajoutant un nombre de paramètres variable pour la sélection).