Tutorial Annotation JDK 1.5: traitement au runtime

Technique > Annotation Java

4 - Traiter les annotation au runtime

Dans cet exemple, les annotations vont être utilisées pour décrire des autorisations sur des méthodes Java, et pour permettre de tracer l'appel de certaines méthodes.

1- Déclarer l'annotation CerbèreInfo

L'annotation CerbèreInfo contient un tableau d'utilisateurs autorisés à utiliser cette méthode, et un booléen permettant de tracer à la demande tous les appels à cette méthode. Dans cet exemple, les trace sont juste affichées à l'écran pour simplifier le code.

package com.btc.tutorial.cerbère;

import java.lang.annotation.*;

// L'annotation peut être utilisée au moment de l'execution
@Retention(RetentionPolicy.RUNTIME)

// L'annotation peut être placée sur une méthode
@Target(ElementType.METHOD)
public @interface CerbèreInfo { // Nom de l'utilisateur autorisé ou * pour tout le monde String[] autorise() default {"*"}; // true: les accés à cette méthodes sont écrits dans un fichier d'audit boolean audit() default false; }
Fichier: CerbèreInfo.java

2- Utiliser l'annotation CerberInfo sur une classe

Dans l'exemple suivant, nous créons une classe Java, en ajoutant l'annotation permettant de connaitre l'auteur du code:

package com.btc.tutorial.cerbère;


public class Entreprise {
    private String nom;


    // Tous le monde peut utiliser cette méthode, mais les accés sont enregistrés (audit)
    @CerbèreInfo (
            autorise={"*"},
            audit=true
    )
    public String getNom() {
        Cerbere.vérifier(this, "getNom");
        return nom;
    }

    // Seul admin peut utiliser cette méthode, et les accés sont enregistrés (audit)
    @CerbèreInfo (
            autorise={"admin", "bob"},
            audit=true
    )
    public void setNom(String nom) {
        Cerbere.vérifier(this, "setNom", String.class);
        this.nom = nom;
    }
}
Fichier: Company.java

3- Accéder aux annotations

La classe Cerbère permet de vérifier si l'utilisateur a ou non le droit de déclencher une méthode.
L'utilisateur est stockée par la classe Cerbère dans une String (simplifié pour l'exemple).

Regardons le code source:

package com.btc.tutorial.cerbère;

import java.lang.reflect.*;

public class Cerbère {

    // Le nom de l'utilisateur connecté (simplifié pour l'exemple)
    private static String _utilisateur;

    public static synchronized void setUtilisateur(String utilisateur) {
        _utilisateur = utilisateur;
    }

    public static String getUtilisateur() {
        return _utilisateur;
    }

    // Vérifier si l'utilisateur a le droit d'appeler cette méthode
    // Cette méthode utilise une nouveauté du JDK 1.5, les varargs permettant de passer
    // un nombre variable de paramètres.
    public static void vérifier(Object object, String methodName, Class... paramTypes) {

        try {
            // Créer un tableau contenant les types des paramètres de la méthode
            Class paramTypesArray[] = new Class[paramTypes.length];

            // Remplir le tableau à partir des parmaètres de la méthode
            int i = 0;

            // Ca aussi c'est une nouveauté du JDK 1.5!
            for (Class paramType : paramTypes)
                paramTypesArray[i++] = paramType;

            // Récupérer la méthode appelée.
            Method method = object.getClass().getMethod(methodName, paramTypesArray);

            // S'il n'y a pas d'annotation, autoriser l'appel
            if( !method.isAnnotationPresent(CerbèreInfo.class) ) return;

            // Récupérer l'annotation CerbèreInfo
            CerbèreInfo info = method.getAnnotation(CerbèreInfo.class);

            // Vérifier si l'utilisateur est autorisé à utiliser cette méthode
            if( vérifier(info.autorise(), getUtilisateur()) ) {

                // Si l'audit est activé pour cette méthode, ajouter l'appel de la méthode
                if( info.audit() ) log("[Cerbère] INFO: User " + getUtilisateur() + " granted to call " + methodName);
                return;
            }

        } catch (Exception e) {
            // Il y a un problème quelque part
            exception("[Cerbère] ERROR: while checking acces to " + methodName);
        }

        // L'utilisateur n'a pas le droit d'utiliser cette méthode
        exception("[Cerbère] ALERT: access to " + methodName + " denied to " + getUtilisateur());
    }

    // Vérifier si l'utilisateur fait partie des utilisateurs autorisés
    private static boolean vérifier(String[] autorisés, String utilisateur) {

        // Une nouveauté bien sympathique du JDK 1.5
        for(String autorisé: autorisés) {

            // Si "*", alors tous les utilisateurs peuvent déclencher cette méthode 
            if( autorisé.equals("*") ) return true;
            if( utilisateur.equals(autorisé) ) return true;
        }

        // L'utilisateur ne fait pas partie des utilisateurs autorisés
        return false;
    }

    private static void exception(String msg) {

        // Logger le problème
        log(msg);

        // Envoyer une exception (RuntimeException pour simplifier l'exemple)
        throw new RuntimeException(msg);
    }

    private static void log(String msg) {
        // Un peut minimaliste comme système d'audit, mais cela permet de simplifier l'exemple
        System.err.println("[Cerbère] " + msg);
    }
}
Fichier: Cerbère.java

Si vous connaissez AOP (Aspect Oriented Programming), vous pouvez imaginer la puissance de la solution AOP + Annotation: AOP vous permet de ne pas avoir à écrire Cerbère.vérifier(...) dans vore code, cet appel étant automatiquement ajouté par un outil AOP. Nous reviendrons sur ce sujet.

La page suivante vous présente l'annotation Todo et l'outil de traitement des annotations

(c) Business Technology Consulting 2005-2007