Une instance de cette interface est transmise à la méthode appelée dans l'intercepteur à chaque interception. Elle propose six méthodes particulières.

  • getContextData() : retourne une Map qui porte les données associées au contexte d'invocation.

  • getMethod() : retourne une référence sur la méthode interceptée.

  • getParameters() : retourne un tableau d' Object qui porte les paramètres passés à cette méthode.

  • getTarget() : retourne une référence sur l'instance de l'EJB intercepté.

  • setParameters(Object []) : l'appel à cette méthode permet de modifier les paramètres envoyés à la méthode interceptée.

  • proceed() : cette méthode doit être appelée pour continuer l'exécution normale du processus d'interception.

Comme on le voit, l'intercepteur a la main sur de nombreuses choses, notamment sur les valeurs des paramètres envoyées à la méthode interceptée, qu'il peut modifier.

La méthode proceed() a un statut particulier. C'est le fait de l'invoquer qui permet d'invoquer la méthode interceptée, ou l'intercepteur suivant s'il y en a un. Donc, l'intercepteur dans lequel on se trouve a le choix, de poursuivre le processus d'exécution nominal, et d'exécuter la méthode métier, ou de l'interrompre et donc d'empêcher son exécution.

Une méthode d'interception suit donc le modèle suivant.


Dans le cas où l'on décide de ne pas appeler la méthode finale, les quelques lignes de traitement permettent de construire un objet par défaut, de même type que le type de retour de la méthode interceptée. Plutôt que d'invoquer cette méthode, de récupérer l'objet qu'elle retourne et de le renvoyer à notre tour, on décide de retourner cet objet. Dans ce passage, on n'invoque donc pas la méthode métier de notre EJB.

Le client qui a invoqué cette méthode, lui, ne se rend compte de rien. Il reçoit en retour un objet par défaut, conforme aux contrats de cette méthode métier.

Montrons comment il est possible de créer un intercepteur qui code la logique de validation d'accès à une méthode. Prenons l'exemple de notre méthode createMarin(String). Nous allons créer un annotation @StringNotNull, qui, une fois posée sur notre méthode, permettra à un intercepteur de tester si le paramètre est nul ou pas, et s'il l'est, de ne pas appeler cette méthode.

On peut donc modifier le code de notre EJB MarinServiceImpl pour utiliser cette annotation.


Pour le moment, le comportement de notre EJB ne change pas, il faut encore définir un intercepteur dont le rôle sera d'analyser les paramètres des méthodes métier, de regarder si certains sont annotés par @StringNotNull, et d'appliquer la règle de non-nullité. Écrivons cet intercepteur.

Exemple 77. Intercepteur associé à @StringNotNull

public  class StringNotNullInterceptor {

     // méthode permettant de retourner une valeur par
     // défaut à partir d'un type
     private Object getDefaultValue(Class<?> type) {

         // cette méthode retourne true si le type 
         // correspond en fait à un type primitif Java
         if (type.isPrimitive()) {

             if (byte.class.equals(type)) {
                 return  0 ;
            }  else  if (short.class.equals(type)) {
                 return  0 ;
            }  else  if (int.class.equals(type)) {
                 return  0 ;
            }  // etc... ne sont pas traités : long, boolean, char, 
             // float, double
        }

         return null ;
    }

     @AroundInvoke
     private Object validateStringNotNull(InvocationContext context)  throws Exception {

        Method method = context.getMethod() ;

         // lecture des types des paramètres de la méthode interceptée
        Class<?>[] parameterTypes = method.getParameterTypes() ;
        
         for (int index =  0 ; index < parameterTypes.length ; index++) {
             // analyse des paramètres de type String
             if (String.class.equals(parameterTypes[index])) {
                 // lecture des annotations de ce paramètre
                Annotation[] annotations = method.getParameterAnnotations()[index] ;
                 for (Annotation annotation : annotations) {
                     // l'annotation StringNotNull existe-t-elle ?
                     if (StringNotNull.class.equals(annotation.annotationType())) {
                         // ici l'on regarde un paramètre de type String,
                         // annoté par StringNotNull
                        String stringParameter = (String)context.getParameters()[index] ;
                         if (stringParameter == null) {
                             // on a un paramètre de type String, annoté, et null
                             // on ne doit donc pas appeler la méthode
                            Object returnedObject = 
                                  getDefaultValue(method.getReturnType()) ;
                             return returnedObject ;
                        }  else {
                             // ici le paramètre est non nul, on continue donc
                             // à explorer les autres paramètres de la méthode
                        }
                    }
                }
            }
        }

         // si nous sommes arrivés ici, c'est qu'aucun paramètre de type String
         // annoté et nul n'a été trouvé, on peut donc appeler la méthode
         // interceptée
         return context.proceed() ;
    }
}

Comme on le voit, le code de cette classe est un peu complexe, et surtout s'appuie énormément sur le mécanisme de reflection qui permet de lire le contenu d'une méthode.

JPA & EJB
Retour au blog Java le soir
Cours & Tutoriaux

Table des matières

Introduction
1. Objet du mapping objet / relationnel
2. Un peu d'histoire
Un premier exemple
1. Introduction
2. Création de l'environnement technique
2.1. Introduction
2.2. Création de la base Derby
2.3. Création du projet NetBeans et d'une première entité
2.4. Structure d'un projet persistant
2.5. Une première classe persistante
2.6. Un premier fichier persistence.xml
3. Utilisation de ce premier exemple
3.1. Écriture du code d'utilisation
3.2. Exécution de notre premier exemple
3.3. Modification de la class Marin
3.4. Opérations CRUD
Mettre un jeu de classes en base
1. Introduction
2. Définition d'une entité JPA
2.1. Écriture de l'entité
2.2. Annotation de l'entité
2.3. Annotations des champs
2.4. Exemple d'utilisation
3. Opérations sur les entités
3.1. Introduction
3.2. Opération PERSIST
3.3. Opération REMOVE
3.4. Opération REFRESH
3.5. Opération DETACH
3.6. Opération MERGE
4. Mise en relation d'entités
4.1. Introduction
4.2. Relations unidirectionnelles et bidirectionnelles
4.3. Relation 1:1
4.4. Relation 1:p
4.5. Relation p:1
4.6. Relation n:p
4.7. Comportement cascade
4.8. Effacement des entités orphelines
5. Charger des entités et leurs relations
6. Objets inclus
6.1. Introduction
6.2. Déclaration d'un objet inclus
6.3. Utilisation d'objets inclus
6.4. Cas où l'objet inclus est nul
6.5. Renommer les colonnes incluses
6.6. Collections d'objets inclus
L'API Collection en base
1. Introduction
2. Enregistrer une collection d'entités
2.1. Enregistrement d'une collection simple
2.2. Enregistrement d'un Set
2.3. Enregistrement d'une List
3. Enregistrer une collection de types de base
4. Enregistrement d'une Map
4.1. Table de hachage de type (type de base, entité)
4.2. Cas où la clé est un champ de la valeur
4.3. Cas d'une table (entité, entité)
Héritage
1. Introduction
2. Enregistrement d'une hiérarchie de classes
2.1. Entité et super-classe non enregistrée
2.2. Position du problème
2.3. Trois façons de faire
3. Stratégie SINGLE_TABLE
3.1. Fonctionnement
3.2. Mise en place
3.3. Limitations
4. Stratégie JOINED
4.1. Fonctionnement
4.2. Mise en place
4.3. Limitations
5. Stratégie TABLE_PER_CLASS
5.1. Fonctionnement
5.2. Mise en place
5.3. Limitations
Requêtes
1. Introduction
2. Un premier exemple
2.1. Écriture d'une première requête
2.2. Exécution d'une première requête
2.3. Exécution d'une première requête d'agrégation
3. Définition de requêtes
3.1. Requêtes dynamiques
3.2. Requêtes paramétrées
3.3. Requêtes nommées
3.4. Requêtes natives
4. Exécution, analyse du résultat
4.1. Exécution d'une requête dynamique
4.2. Exécution d'une requête nommée
4.3. Analyse du résultat
4.4. Cas des résultats de grande taille
4.5. Remarques
5. Clause From
5.1. Définition des entités
5.2. Jointures dans la clause From
5.3. Remarque finale sur les jointures en JPQL
6. Clause Where
6.1. Variables et chemins dans une clause where
6.2. Expressions conditionnelles et opérateurs
6.3. Requêtes imbriquées
6.4. Opérateurs any, all et some
6.5. Expressions fonctionnelles
7. Clauses Group By et Having
8. Opérations Update et Delete
EJB
1. Introduction
2. Un premier exemple
2.1. Introduction
2.2. Installation dans Glassfish à l'aide de Netbeans
2.3. Création d'un premier EJB
2.4. Déploiement de notre premier EJB
2.5. Création d'un client
3. Mise en oeuvre du pattern session facade
3.1. Introduction
3.2. Modèle objet
3.3. Définition de l'unité de persistance
3.4. Assemblage de notre application
3.5. Assemblage et déploiement
3.6. Utilisation du client
4. Opération de persistance en façade
4.1. Introduction
4.2. Enrichissement du service
4.3. Création de la méthode findMarinById(long)
4.4. Ajout de la méthode findAllMarins()
4.5. Utilisation dans un code client
5. Types d'EJB
5.1. Introduction
5.2. Qu'est-ce qu'un EJB ?
5.3. Écriture d'un EJB session
5.4. Qu'est-ce qu'une méthode métier ?
5.5. EJB avec ou sans état
5.6. Gestion des transactions
5.7. Restrictions
6. Cycle de vie d'un EJB
6.1. Cas des EJB sans état
6.2. Cas des EJB avec état
6.3. Injection de dépendances
7. Transaction gérée par l'EJB
7.1. Introduction
7.2. Déclaration du mode transactionnel
7.3. Gestion de la transaction
7.4. Fonctionnement de la transaction
7.5. Cas des EJB avec état
8. Transaction gérée par le serveur
8.1. Introduction
8.2. Déclaration du mode transactionnel
8.3. Gestion de la transaction
8.4. Fonctionnement de la transaction
8.5. Remarques
9. Intercepteurs
9.1. Introduction
9.2. Aperçu général
9.3. Cycle de vie d'un intercepteur
9.4. Object InvocationContext
9.5. Interception d'un EJB ou d'une méthode métier
9.6. Exemple de mise en œuvre d'un intercepteur