Thématiques principales

Affichage des articles dont le libellé est annotations. Afficher tous les articles
Affichage des articles dont le libellé est annotations. Afficher tous les articles

samedi 9 mars 2019

Python : Les décorateurs

Bien nous voilà maintenant encore un nouveau sujet Python, les décorateurs ! Alors promis c’est le dernier! (ou presque) parce qu'après on s'intéressera à des aspects plutôt technologique de python plutôt que des subtilités du langages…

On va donc tâcher de faire court afin de ne pas lasser!

Les décorateurs

Vous connaissez le pattern décorateur ? et bien c’est un peu la même idée sauf que dans notre cas le but est de faire plutôt comme le pattern Proxy…

Quoi? Vous comprenez pas? Ok faisons un parallèle avec Java… Dans Java lorsque l’on dispose d’une classe comportant certains comportement mais que ces derniers ne suffisent pas, on peut employer ce que l’on appelle un proxy [pattern-prox].

Dans le principe le proxy va prendre en paramètre l’objet que l’on souhaite décorer et s’enregistrer auprès de ce dernier afin de déclencher l’une de ses méthodes en lieu et place de l’objet en lui même. Alors bien sûr, souvent on joue quand même la méthode de l'objet mais celui ci on ajoute quelques comportement supplémentaire, on dit que l’on va décorer…

Bien en python c’est un peu la même idée mais avec des fonctions.

Ainsi un décorateur est une fonction prenant en paramètre une autre fonction (celle ci est dite décorée). Le déclarant est exécuté sur la fonction dès sa déclaration et permet donc d’agir sur celle ci.

dimanche 3 mars 2019

OSGI : Les annotations

Nous avons vu comment déclarer un Activator/Factory/Tracker [ex-karaf], [uttu-felix] pour construire des bundles OSGI, ensuite nous avons vu comment tirer parti de la philosophie Spring pour alléger la définition des beans, des services  et leur utilisations avec SpringDM et Blueprint [bp-karaf], [uttu-bp]; dans ce dernier article (où presque sur OSGI), je vous propose de nous intéresser à une dernière façon de faire des bundles : en utilisant des annotations [scr-annot].

Bon la question ici n’est pas de faire un laïus sur les annotations, j’en ai déjà parlé dans un article [uttu-annot], donc on va aller directement dans le vif du sujet parce que l’on se doute bien que pour remplacer nos Activator and coe et/ou les description xml, il va nous falloir quelques concepts de base.

Les annotations

Alors en fait si vous ne l’aviez pas encore vraiment compris, avec OSGI, finalement il n’y à pas forcément beaucoup de concepts à mettre en oeuvre…. en effet finalement, on à des beans qui implémentent des services et qui vont être injecté dans d’autres beans…

Du coup dans notre contexte, on va rencontrer les annotations suivantes:
  • @Component: permet de déclarer un bean
  • @Activate/@Deactivate: permet de spécifier une méthode gérant une initialisation spécifique (où inversement pour l’annotation @Deactivate)
  • @Référence: permet de spécifier une donnée membre dans laquelle devra être injectée un autre bean (spécifié par @Component)

samedi 18 août 2018

JEE : CDI partie 2 : les patterns

Nous revoilà avec CDI, nous avions vu les principes de base de l'injection, son utilisation dans une factory et ensuite comment l'utiliser avec des servlets et des JSP. Aujourd'hui nous terminerons le sujet en traitant des implémentations de CDI des patterns observateur, décorateur (ou façade) et InvocationHandler.

Les patterns

Nous avons jusque la vu les fonctionnalités les plus importantes du framework CDI mais ca serait passer a coté d'autres fonctions bien pratiques que de s’arrêter ici. En effet comme énoncé plus haut, CDI fourni des versions adaptées à l'injection de certains designs patterns.

Observateur

Voyons par exemple le pattern observateur [7] Je ne reviens pas sur ce pattern déjà décrit dans le blog mais il s'agit d'un modèle de communication réactif entre deux composants se notifiant par le biais d'un système d’événements.

Dans le framework CDI, ce pattern se traduit par l'utilisation de la classe générique Event<T> prenant en type l'information à transmettre (le vrai événement en fait) et l'annotation @Observes se chargeant de taguer le paramètre de type T d'une méthode devant recevoir l’événement, l'appel à celle ci se faisant alors automatiquement. Voyons cela dans le concret avec un exemple.

Considérons simplement que deux beans doivent communiquer entre eux (on va s'appuyer sur l'article précédent sur CDI [14]). On va considérer que CDIModelShortScope veuille prévenir CDIModel qu'une requête utilisateur vient d’être faite (en gros que l'objet vient de se creer et d'etre detruit)

On va donc modifier ce premier de façon a qu'il permette l’émission de messages en utilisant Event et en utilisant la méthode fire

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Named
@RequestScoped
public class CDIModelShortScope {

 @Inject
 private Event<String> event;
 
 @PostConstruct
 public void build()
 {
  System.out.println("Construction de CDIModelShortScope");
  event.fire("Une nouvelle conexion");
 }
 
 @PreDestroy
 public void destroy()
 {
  System.out.println("Destruction de CDIModelShortScope");
  event.fire("Fin de vie CDIModelShortScope");
 }
 
}
On ajoute dans la classe CDIModel, le moyen d’écouter les messages: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Named("model")
@ApplicationScoped
public class CDIModel {

 private String dataModel="les données du model";

 public void updateOnConnect(@Observes String event)
 {
  System.out.println(event);
 }
 
....
 
}

Le comportement resultant produisant bien dans les log : 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[#|2018-08-14T21:27:18.493+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838493;_LevelValue=800;|
  Construction de CDIModel|#]

[#|2018-08-14T21:27:18.502+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838502;_LevelValue=800;|
  Construction de CDIModelShortScope|#]

[#|2018-08-14T21:27:18.503+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838503;_LevelValue=800;|
  Une nouvelle conexion|#]

[#|2018-08-14T21:27:18.505+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838505;_LevelValue=800;|
  Destruction de CDIModelShortScope|#]

[#|2018-08-14T21:27:18.505+0200|INFO|glassfish 5.0||_ThreadID=27;_ThreadName=Thread-8;_TimeMillis=1534238838505;_LevelValue=800;|
  Fin de vie CDIModelShortScope|#]

[#|2018-08-14T21:27:47.287+0200|INFO|glassfish 5.0||_ThreadID=132;_ThreadName=Thread-8;_TimeMillis=1534238867287;_LevelValue=800;|
  Destruction de CDIModel|#]

Plutôt efficace et pas cher... non? Passons au décorateur.

Le Decorateur [6] 

Je vous avais parler de pattern avec un "s" et bien CDI fourni aussi des implémentations pour quelques autres patterns, entre autre les patterns façade (ou décorateur) et invocationhandler. Ces deux patterns sont assez proche en fonctionnement, la différence étant dans la nature de la dépendance que l'on crée entre le bean décoré et l'objet décorateur ou objet intercepteur.

Regardons cela par un exemple:

Considérons un bean MyObject contenant une donnée membre "nom" de type String caracterisant l'identité de l'objet. Ce nom est recuperable via une méthode getName().

Nous souhaiterions alors une fonctionnalité nous permettant de façon transparente renvoyer le hash du nom a la place. (Par soucis de simplicité pour l'exemple on utilisera les annotations @Named et @ApplicationContext).

On va dans un premier temps proposer une solution avec un décorateur. Pour cela, il nous faut construire une classe ayant le même profil de classe que le bean décoré. On va définir une API commune sous la forme d'une interface.

Enfin définissons le décorateur: Celui se défini sous la forme d'une classe abstraite implémentant la même interface que le bean en utilisant les annotations @Delegate et @Decorator (et @Priority qui permet de donner un ordre de prise en charge des beans si plusieurs décorateurs sont a l'oeuvre sinon cela est équivalent a définir le décorateur dans le beans.xml).

Ainsi le bean prendra la forme suivante:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Named("myObject")
@ApplicationScoped
public class MyObject implements INamed {

 private String name="defaultName";

 public String getName() {
  return name;
 }
}
et le décorateur: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Priority(value = 0)
@Decorator
public abstract class MyObjectDecorator implements INamed{

 @Inject
 @Delegate
 private INamed myObject;
 
 @Override
 public String getName() {
  return String.valueOf(myObject.getName().hashCode());
 }

}

La JSP affichant et utilisant la méthode getName nous renverra alors:
1
myObject Name: -437142420

L'InvocationHandler [8]

Apres avoir traité la question du décorateur regardons la solution alternative qu'est l'utilisation de l'intercepteur (basé sur le pattern invocation handler que nous avons traité dans [8]).

Ce pattern est un pattern permettant de positionner un objet entre le client faisant l'appel d'une méthode et la méthode elle même. En comparaison avec le décorateur, on pourrait se dire, "ok bon ca fait la meme chose" mais l'invocation handler a plutôt pour idée de modifier le comportement de la méthode d'une classe pour toutes ses instances. Un décorateur n'a pas cet objectif : il ne va pas se charger de toutes les instances de la classe, il peut éventuellement ne décorer que des objets spécifiques selon le besoin.

On pourrait donc en quelques sortes considérer que le décorateur a pour rôle de modifier le comportement en y ajoutant des éléments fonctionnel selon le cas alors que l'invocation handler aura un impact plus global et avec une orientation plus technique.

Voyons maintenant comment utiliser l'invocation Handler avec CDI.

Pour l'exemple imaginons que nous souhaitons compter le nombre de caractère du nom de l’objet que nous utilisions précédemment et l'affecter a une donnée membre dédiée.

Pour cela il nous faut tout d'abord construire une annotation maison permettant de tisser un lien entre le bean et notre futur intercepteur. Cette annotation devra alors porter l'annotation @InterceptorBinding. Appelons la CompteurAnnot:



1
2
3
4
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface CompteurAnnot {}
On place ensuite cette annotation sur la méthode du bean qu'il s'agit d'intercepter


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@Named("myObject")
@ApplicationScoped
public class MyObject implements INamed {

 private String name="defaultName";
 
 private String nameSize;

 @CompteurAnnot
 public String getName() {
  return name;
 }
 
 public String getNameSize() {
  return nameSize;
 }

 public void setNameSize(String nameSize) {
  this.nameSize = nameSize;
 }
}
On va enfin créer notre intercepteur avec une nouvelle classe Compteur avec les Annotations @Interceptor et @AroundInvoke. Le premier est placé sur la classe et le deuxième sur la méthode qui devra être appeler a la place de celle du bean et réaliser le comptage. On y ajoutera notre annotation, afin que CDI sache faire le lien entre l'intercepteur et le bean associé.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Priority(value = 0)
@CompteurAnnot
@Interceptor
public class Compteur {

 @Inject
 private MyObject myObject;
 
 @AroundInvoke
 public Object invoq(InvocationContext context) throws Exception {
  String result = (String) context.proceed();
  System.out.println("YES: on a invoqué "+result);
  myObject.setNameSize(String.valueOf((result.length())));
  return result;
 }

}
Du coup en ajoutant l'appel a cette méthode getSizeName dans la JSP, celle ci nous donne maintenant la taille de la chaîne, l’intercepteur a donc bien fait son travail.


1
myObject Name: -437142420, 10

Conclusion

Nous voila au bout de cet article sur CDI. Nous n'avons pas absolument tout traité mais globalement on en a fait un tour d'horizon plutôt large techniquement nous permettant de suffisamment comprendre l’intérêt de profiter des fonctionnalités de CDI et surtout de voir son role pivot dans les applications à base JEE. Ainsi a l'image de Spring, CDI est maintenant un incontournable.

Références

[1] https://un-est-tout-et-tout-est-un.blogspot.com/2018/05/iod-linjection-de-dependances.html
[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi
[14] http://un-est-tout-et-tout-est-un.blogspot.com/2018/08/jee-cdi-partie-1-linjection.html

mercredi 15 août 2018

JEE : CDI partie 1 : l'injection

Apres un mois de juillet avec de l'IA, nous revoilà avec du Java, en particulier JEE. Cet article intervient dans la ligné de l'article [1] ou nous avions présenté les principes de l'injection de dépendance mais étions passé au dessus des implémentations possibles. Nous traiterons donc en toute logique d'une des implémentations majeur existante : CDI (l'autre étant Spring mais nous le traiterons dans un article dédié).

Introduction

CDI, ou Context and Dependency Injection, est donc un framework d'injection de dépendances évoluant dans l'environnement JEE. Comme d'habitude avec les technologie JEE, CDI a été élaboré après que le framework concurrent ici Spring qui en a posé les bases. Ainsi il s'appuie sur la formalisation apportée par les  JSR 299 et 346 [2], [3] a partir des années 2009 et fut décliné selon differentes versions telles que:
  • v1 en 2009 avec JEE 6 
  • v1.1 en 2013
  • v1.2 en 2014
  • v2 en 2017 dans JEE 8 [10]

Standalone mode

La plupart des concepts de base de CDI ont été élaboré dans ses premières versions et en fait sa dernière évolution n'apporte surtout que la possibilité de faire évoluer le framework en dehors d'un conteneur d'application en permettant d'instancier le framework directement dans un classique main comme suit:

1
2
3
 public static void main(String[] args) {
  CDI<Object> container = new Weld().initialize();
 }

en utilisant la version 3 de Weld [4] et en le declarant dans le pom du projet:

1
2
3
4
5
<dependency>
 <groupId>org.jboss.weld.se</groupId>
 <artifactId>weld-se-core</artifactId>
 <version>3.0.5.Final</version>
</dependency>

Pourtant cette utilisation en standalone n'est pas forcement préconisé et est seulement à utiliser que lorsque l'on souhaite construire son propre conteneur d'application Java (nous reviendrons sur cette possibilité quand nous aborderons OSGI, bientot)

Standard mode

En l’état, le mieux que l'on puisse faire, c'est se référer à la spécification JEE en utilisant la dépendance suivante:

1
2
3
4
5
6
<dependency>
 <groupId>javax</groupId>
 <artifactId>javaee-api</artifactId>
 <version>8.0</version>
 <scope>provided</scope>
</dependency>

Ainsi l'application que l'on produira (sous la forme d'un War ou d'un EAR si l'on utilise des EJB) s'appuiera sur les implémentations java fournies par le container d'applications.

Pour traiter de CDI, n'importe quel container supportant une version 1.2 de CDI fera l'affaire (idéalement appuyez vous sur Glassfish [5] qui reste l’implémentation JEE de référence)

L'injection

Retournons a CDI et ce que le framework permet de faire. CDI comme son nom l’évoque traite de l'injection de dépendances. Globalement ceci passe par l'utilisation de beans et de l'annotation @Inject.

1
2
@Inject
private ServletConf servletConf;

On ne peut pas plus simple pour un framework! ou du moins pour son utilisation en première approche. Cependant ce n'est pas tout, car CDI fourni de nombreuses autres fonctionnalités techniques pour la conception logicielle en s'appuyant sur différents Design Pattern tel que:
  • Le Decorateur [6]
  • L'Observateur [7]
  • L'InvocationHandler [8]
  • La Factory [9]
Ou entre autre la possibilité de spécifier au moteur d'injection d'utiliser des alternatives (avec l'annotation du même nom @Alternative) des spécialisations ou de faire de la résolution dynamique à l'aide de la classe Instance<T>

Nous nous limiterons aux patterns, les annotations cités ci-dessus étant assez immédiates a utiliser.

La factory [11]

Apres cet exemple simple, considérons par exemple que nous ne souhaitons plus simplement injecter un bean quelconque mais injecter un bean de notre propre composition. Comment faire? Et bien la va intervenir une annotation @Produces que l'on va associer a une ou plusieurs méthodes qui seront désignées comme les constructeurs des beans à injecter.

L'interet de l'approche est double, d'une part elle permet d'associer la création d'un bean a injecter avec un comportement et d'autre part de permettre a des objets qui ne sont pas des beans [12] a être quand même injectable :

1
2
3
4
5
@Produces 
public CDIModel createCDIModel()
{
 return new CDIModel();
}

Alors est ce que ça marche? Et bien non!! Arf pourquoi ?? C’était si simple, ça aurait du! Et bien simplement que le moteur CDI va avoir un conflit d'injection, il se base sur les signature de type pour trouver quelque chose a injecter et la, il en trouve deux : la classe CDIModel elle même et la méthode createCDIModel qui retourne un CDIModel.

Pour s'en sortir, il nous faut un moyen de discrimination. Pour cela, on va utiliser l'annotation @Qualifier qui va nous permettre de construire des annotations utilisables a la fois sur les éléments sources et sur les éléments cibles, la ou l'on souhaite réaliser l'injection.

On va donc créer une annotation maison pour discriminer les beans construis avec la factory.

1
2
3
4
5
6
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface FromFactory {

}

On l'ajoute sur la signature de la méthode et sur l’élément ou l'injecter et la oh miracle ça fonctionne (ici par exemple on injecte avec les deux approches dans une jsp)

1
2
@Produces @FromFactory
public CDIModel createCDIModel()

Sans oublier d'ajouter l'annotation @Default sur notre classe CDIModel de façon a ce que par défaut le moteur d'injection n'utilise pas la factory. Il ne le fera que si on lui dit explicitement.

Enfin, on utilise les deux modes d'injection:

1
2
3
4
5
6
@Inject
private CDIModel model;

@Inject
@FromFactory
private CDIModel model2;

donnant

1
2
3
Model CDI: les données du model

Model 2 CDI: les données du model From Factory

Named

Pour faire plus simple, et ne pas forcement avoir a construire des annotations, CDI fourni une annotation dédiée à la discrimination des beans: @Named qui permet de donner un nom a l’élément producteur du bean et de faciliter le binding sur l’élément où l'injecter. L’intérêt de cette annotation est qu elle permet de construire des beans spécifiques utilisables directement dans les JSP (même si l'on a pas encore parler de cette techno, cela va permettre d'utiliser des beans dans des éléments de pseudo code déclarés dans le code html, nous y reviendrons)

Ainsi par exemple, on va pourvoir ajouter deux @Named, l'un sur la classe:

1
2
3
4
@Named("model")
public class CDIModel {
...  
}

et l'autre sur la factory

1
2
3
4
5
6
7
@Produces @Named("model2")
public CDIModel createCDIModel()
{
 CDIModel m=new CDIModel();
 m.setDataModel(m.getDataModel()+" From Factory");
 return m;
}

les deux permettant de ne pas avoir a écrire le code suivant dans notre servlet ni même de spécifier qu'il faut les injecter:

1
2
3
4
5
6
7
8
if(model!=null)
 request.setAttribute("model", model);

if(model2!=null)
{
 model2.setDataModel(model2.getDataModel()+" From Factory");
 request.setAttribute("model2", model2);
})

Les scopes

Puisque nous parlons de JSP, profitons en pour nous intéresser au cycle de vie des beans. C'est quoi le lien? Bien entendu il s'agit de la durée de vie du bean selon le type de requête qu'un utilisateur est amener a faire sur les differentes pages de notre application (on parle d'application JEE quoi...). Entre autre on doit se demander si notre bean doit vivre juste le temps d'une requete (on utilisera alors l'annotation @RequestScoped), le temps de plusieurs requêtes (@SessionScoped) ou carrément le temps de toute la durée de vie de notre application (@ApplicationScoped).

Ainsi selon le scope choisi, un bean ne vivra pas le même temps... cela signifie aussi que celui ci sera recréé autant de fois que nécessaire, selon les demandes. Donc resumons:

  • Avec un scope application, le bean ne sera donc créer qu'une fois et ne sera a priori pas détruit (sauf lors de l’arrêt de l'applicatif). 
  • Le scope requete provoque a l'inverse la création du bean a chaque requête http et détruite a l'issu de la réponse. 
  • Le scope session est un intermédiaire où le bean est créé a la connexion de l'utilisateur et a l'aide d'information de session permet de maintenir le bean tant que cet utilisateur utilise l'application.

Pour tester ces scopes nous allons aller un peu plus loin dans la gestion du cycle de vie du bean. En effet, on a bien compris qu'un bean a une durée de vie plus ou moins longue mais on a pas parler de ce qui se passe lors de sa naissance ou de sa mort. Bien sur nous avons évoqué l'utilisation de l'annotation @Produces permettant de réaliser une pseudo initialisation du bean mais en fait l'utilisation de @Produces a surtout pour vocation d’initialiser un contexte de construction du bean et non le bean lui-même.

Ainsi donc, pour ce qui concerne la naissance et la mort d'un bean, il existe deux annotations spécifiques @PostConstruct et @PreDestroy permettant de preciser deux méthodes à exécuter à l'instar d'un constructeur et d'un destructeur.

Pouvoir exécuter un destructeur, on comprend vite que ça permet d’éventuellement désallouer des ressources. Par contre, on peut discuter de la pertinence d'un pseudo constructeur par rapport au vrai constructeur de la classe. En effet celui ci a pour objet justement d'initialiser l'objet! Alors pourquoi un @PostConstrut? Simplement pour permettre au bean d'exister complètement avant de réaliser une initialisation quelconque ceci permettant donc la prise en compte de la présence d’éventuelles autres injections de bean dans le bean que nous sommes en train de construire! Sans cela, point de salut!

Donc voila un petit exemple pour illustrer tout cela et voir comment fonctionne nos @ResquestScope et @ApplicationScope (on ne traitera pas @SessionScope, celui ci étant plus long a mettre en oeuvre et n'amenant qu'assez peu d'infos supplémentaires sur le fonctionnement de CDI)

Dans un premier temps, on va tout d'abord créer deux nouveaux bean pour lesquels nous définirons les méthodes @PostConstruct et @PreDestroy.

A ces deux classes on va ajouter a l'un @ApplicationScope et a la seconde @RequestScope comme suit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@ApplicationScoped
public class CDIModel {

 private String dataModel="les données du model";
 
 @PostConstruct
 public void build()
 {
  System.out.println("Construction de CDIModel");
 }
 
 @PreDestroy
 public void destroy()
 {
  System.out.println("Destruction de CDIModel");
 }
...
}
et


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Named
@RequestScoped
public class CDIModelShortScope {

 @PostConstruct
 public void build()
 {
  System.out.println("Construction de CDIModelShortScope");
 }
 
 @PreDestroy
 public void destroy()
 {
  System.out.println("Destruction de CDIModelShortScope");
 }
 
}

On réalise finalement les actions suivantes
  • On build et on construit notre EAR.
  • On charge celui ci dans notre container d'application preferé
  • On charge la page et on fait plusieurs raffraichissement.
  • On fini par decharger l'EAR
On jette un œil aux logs :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[2018-08-13T23:42:14.969+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196534969] [levelValue: 800] [[
  Construction de CDIModel]]

[2018-08-13T23:42:14.986+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196534986] [levelValue: 800] [[
  Construction de CDIModelShortScope]]

[2018-08-13T23:42:15.002+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=31 _ThreadName=Thread-8] [timeMillis: 1534196535002] [levelValue: 800] [[
  Destruction de CDIModelShortScope]]

[2018-08-13T23:43:31.005+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611005] [levelValue: 800] [[
  org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]]

[2018-08-13T23:43:31.013+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611013] [levelValue: 800] [[
  Construction de CDIModelShortScope]]

[2018-08-13T23:43:31.014+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1534196611014] [levelValue: 800] [[
  Destruction de CDIModelShortScope]]

[2018-08-13T23:43:32.079+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612079] [levelValue: 800] [[
  org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]]

[2018-08-13T23:43:32.081+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612081] [levelValue: 800] [[
  Construction de CDIModelShortScope]]

[2018-08-13T23:43:32.081+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=34 _ThreadName=Thread-8] [timeMillis: 1534196612081] [levelValue: 800] [[
  Destruction de CDIModelShortScope]]

[2018-08-13T23:43:33.420+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613420] [levelValue: 800] [[
  org.tc.jee.essai.prod.web.cdi.bean.ServletConf@3dc05bc]]

[2018-08-13T23:43:33.422+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613422] [levelValue: 800] [[
  Construction de CDIModelShortScope]]

[2018-08-13T23:43:33.423+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=30 _ThreadName=Thread-8] [timeMillis: 1534196613423] [levelValue: 800] [[
  Destruction de CDIModelShortScope]]

[2018-08-13T23:43:42.998+0200] [glassfish 5.0] [INFO] [] [] [tid: _ThreadID=48 _ThreadName=Thread-8] [timeMillis: 1534196622998] [levelValue: 800] [[
  Destruction de CDIModel]]
Effectivement la on se rend compte que le bean ayant le scope de applicatif n'est créer qu'au premier chargement de la page tandis que le scope associé a la requête et créer et détruit quasiment a la volé lors de chaque rafraîchissement.

Conclusion

Bien sur nous n'avons pas tout vu il reste les patterns avec CDI! mais cet article est deja assez long et vous propose de revenir d'ici quelques jours pour terminer ce sujet.

Références

[1] https://un-est-tout-et-tout-est-un.blogspot.com/2018/05/iod-linjection-de-dependances.html
[2] https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html
[3] http://openwebbeans.apache.org/
[4] http://weld.cdi-spec.org/
[5] https://javaee.github.io/glassfish/
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-decorateur.html
[7] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[8] https://un-est-tout-et-tout-est-un.blogspot.com/2018/04/invocationhandler.html
[9] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html
[10] http://www.cdi-spec.org/news/2017/05/15/CDI_2_is_released/
[11] https://readlearncode.com/java-ee/introduction-to-cdi-producer-methods/
[12] https://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html
[13] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi

lundi 2 avril 2018

InvocationHandler

Nous avions parlé des annotations dans un articles precedents mais nous nous etions arreter au traitements des annotations utilisé dans le code ou dans les class mais pas celles utiliser en runtime.

Je vous propose dans cet article de réemployer l’exemple traité en gardant l’annotation Relation mais de la compléter par exemple d’une annotation Tuple qui permet d'insérer des données dans la table construite par l’annotation Relation. (Au passage on adaptera un peu cette dernière de façon à ce que la Relation ait les attribut qui vont bien…)

Commençons par l’ajout de l’attribut dans notre précédente annotation, au passage on la passe en mode RUNTIME:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Relation {
 
 String value();
}


Ensuite on va adapter le processeur. On va externaliser la partie en interface avec la BD dans une classe utilitaire permettant de faire la construction de la table avec l’attribut qui va bien:


public void createRelation(Element e) {
  Relation relation = e.getAnnotation(Relation.class);
  String rqt = this.CREATE + e.getSimpleName().toString() + "( " + relation.value() + ATTRIBUT + ");";
  System.out.println("Requete :" + rqt);
  try {
   PreparedStatement statement = DriverManager.getConnection(DB_URL, USER, PASS).prepareStatement(rqt);
   System.out.println("Mon element : " + e.getSimpleName().toString());
   statement.executeQuery();
  } catch (SQLException e1) {
   e1.printStackTrace();
  }
 }

Voila, normalement on aura un attribut name dans notre table lors du build.

Maintenant on veut qu'à l'exécution, pour chaque objet de type Helloworld on insere dans la table le nom passé en parametre du setter. .

Pour cela on va creer l’annotation AddValue (ok c’est pas original) de façon a pouvoir la placer sur cette methode:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AddValue {

}


Ainsi on l’utilisera ainsi:


@Relation(value = "name")
public class HelloWorld {
 private String name;
 
 @AddValue
 public void setName(String name)
 {
  this.name=name;
 }
}


Mais me direz vous, cela ne permet pas vraiment de mapper un quelconque processus d’insertion en base de la donnée! Effectivement car il manque la partie processeur de cette annotation.

Le problème ici c’est que l’annotation doit être traité pendant l'exécution. Alors comment faire?

Et bien tout simplement en utilisant deux patterns, la factory et le pattern proxy. Le premier va nous permettre de masquer l’utilisation du second qui sera couplé à l’invocation handler! Le voila celui la!

Alors avant cela voici ce que nous espérons pouvoir faire:


IHelloWorld o=MyAnnotFactory.mapObject(IHelloWorld.class,HelloWorld.class);
o.setName("Thomas");


Et ceci doit nous permettre de l'insertion de Thomas dans la base lors de l’affectation. Pour cela, la factory va utiliser une interface et le prototype de la classe pour construire une instance qui sera en réalité un proxy.


 public static  T mapObject(Class i,Class t) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
 {
  T proxy=(T) Proxy.newProxyInstance(MyAnnotFactory.class.getClassLoader(),new Class[]{i}, new MyInvocationHandler(t));
  return proxy;
 }


Ainsi on utilise l’api Java Proxy permettant de construire ce proxy a partir de d’une interface identifiant au proxy quels sont les méthodes a wrapper et pour lesquelles, le proxy devra appeler l’InvocationHandler à la rescousse. Voyons comment, ce n’est pas tres compliqué:


public class MyInvocationHandler implements InvocationHandler {

 private Object object;

 public MyInvocationHandler(Class t) {
  try {
   this.object = t.newInstance();
  } catch (InstantiationException | IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 @Override
 public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
  System.out.println("Methode :" + method.getName());
  Method m=object.getClass().getMethod(method.getName(),method.getParameterTypes());
  for (Annotation annot : m.getAnnotations()) {
   System.out.println(annot); 
   if (annot.annotationType().equals(AddValue.class)) {
    System.out.println("je pousse en base");
    AnnotDBUtils.getInstance().pushData(object,arg2);
    return object;
   }
  }
  return method.invoke(object, arg2);
 }
}


Alors tout d’abord, le proxy doit continuer à faire croire que l’on manipule l’objet en question. (la factory etant la pour le tour de passe passe…) Du coup, a peine appelé, l’invocation handler va se charger de se construire un objet du type masqué par le proxy afin de l’utiliser lors de l'appelle effectif des méthodes sur le proxy.



Ceci va permettre de tromper l’utilisateur mais en plus cela va nous permettre de compléter ce comportement. Ainsi le contenu de la méthode invoke décrit la validation de la présence de l’annotation AddValue qui lorsqu’elle est présente va appeler une méthode push Data de notre utilitaire de BD que voici:


 public void pushData(Object object, Object[] arg2) {
  Relation relation = object.getClass().getAnnotation(Relation.class);
  String rqt = this.INSERT + "\"" + object.getClass().getSimpleName().toLowerCase() + "\" (" + relation.value()
    + ")" + VALUES + "('" + arg2[0] + "');";
  System.out.println("Requete :" + rqt);
  try {
   PreparedStatement statement = DriverManager.getConnection(DB_URL, USER, PASS).prepareStatement(rqt);
   statement.executeUpdate();
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }


Voilà alors bien sur a chaque appele de la méthode setName, l’Invocation Handler va tenter de pousser la nouvelle donnée en BD sans chercher à savoir si c’est cohérent logique ou autre. Pour aller plus loin, il faudrait donner des moyens de vérification si la donnée n’est pas déjà en base ou ajouter un attribut servant de clef primaire…. mais la ca complique beaucoup les chose, et bon on va pas reimplementer JPA… même si l’idée est la même!

Du coup on va s'arrêter là mais j’imagine que vous avez compris l'intérêt de ce pattern surtout lorsqu’il est couplé aux annotations!

Références:

[1] http://alain-defrance.developpez.com/articles/Java/J2SE/implementation-dynamique-java-proxy-invocationhandler/
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-proxy.html
[3] http://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html

lundi 12 mars 2018

Les annotations


Nous avions parlé des annotations lorsque nous avions traité des evolutions Java 5 [1]. Aujourd’hui je vous propose de nous intéresser à en faire quelques unes et de réaliser le ou les processeurs d’annotations qui vont bien pour les traiter dans les contextes de la compilation puis at runtime. Nous tâcherons de traiter les différents cas que nous pourrions rencontrer en définissant des annotations sur des cibles variées.

Tout d’abord, premier point, une annotation c’est quoi? Alors pour présenter cela, je vais reprendre l’exemple que nous avions vu alors dans [1]:

@SuppressWarnings(value = { "static-method" })
public <S> S functionGen(S s)
{
 return s;
}

Comme vous l’avez deviné, l’annotation ici est évidemment SuppressWarnings. Elle est identifiable via un @ et prend ici une liste de paramètres réduits à un seul. Le détail nous importe peu, ce qui est intéressant est l’information que l’annotation va transporter.

Une annotation a un nom, potentiellement des variables, et si ce n’est pas visible ici, possèdes des informations sur son contexte d’utilisation, et de sa porté. Ces deux derniers éléments sont positionnés lors de la définition de l’annotation.

Son contexte d’utilisation permet de préciser où celle ci pourra être utilisé. Ici dans l’exemple, l’annotation est positionné sur la méthode mais celle-ci peut être positionné sur une classe, une donnée membre ou même un package, tous ces éléments n'étant pas en exclusion mutuelle. Potentiellement une même annotation peut être utilisé sur plusieurs type de structures différentes.

De façon complète, il est possible de positionner une annotation (sauf définit contrairement) sur:

  • les classes 
  • les méthodes 
  • les interfaces 
  • les variables 
  • les packages


Au delà du cadre de son utilisation, la porté a par contre plus d’importance sur l’utilisation fonctionnelle de l’annotation. En effet, la porté définit à quel moment l’annotation doit être traitée comme une information utile au programme java. Celle ci peut être soit intégré qu’au code source (et ne sera plus visible ensuite, on utilise ce type d’annotation essentiellement pour donner de l’information au développeur ou aux analyseurs de code), soit intégré au code binaire (permettant au compilateur de réaliser des tâches supplémentaires) soit intégrée pour être traité au runtime afin de permettre d'adjonction de comportement dynamique (beaucoup de framework utilise aujourd’hui ce type d’API)

Bien sur ces différents cas d’utilisation des annotations nécessitent des mécanismes et des traitements différents. Autant on comprend vite comment va fonctionner les traitements d’annotations associé au code source qui auront surtout un rôle informatif. Par contre lorsque les annotations sont traité lors de la phase de compilation, il sera nécessaire d’utiliser le mécanisme des processeurs d’annotations (l’API Pluggable Annotation Processor). Enfin lorsque celles ci sont définies pour être traité at runtime, il faudra user de l’introspection et de certains type de pattern afin de profiter intelligemment des possibilités des annotations.

Mais avant de pouvoir traiter les annotations, je vous propose de reprendre tous ces points en définissant notre propre annotation.

Prenons d’abord l’exemple d’une annotation fournissant un lien documentaire explicitant la référence fonctionnelle ou technique amont. Pour la définir, nous allons créer une pseudo interface en utilisant le caractère @ avant le mot clef interface. Nous y déclarent une propriété document et une propriété chapitre et une propriété page.

Ces informations portent le côté fonctionnel, il faut maintenant définir le côté technique pour spécifier le contexte d’utilisation et de traitement.

Pour cela il faut utiliser des méta-annotations (une annotation pour annotations):

  • @Documented : Permet de préciser la présence de l’annotation dans la java doc de l'élément portant celle ci 
  • @Inherit : Permet de transmettre les propriétés informationnelles de l’annotation aux classes filles de l'élément portant celle-ci 
  • @Retention : précise le niveau de traitement (source, bitcode ou runtime) 
  • @Target: précise la ou les cibles sur lesquels l’annotation peut être apposé.


Dans le contexte très basique de cet exemple, nous nous limiterons a simplement a specifier la Retention a source.

@Retention(RetentionPolicy.SOURCE)
public @interface Reference
{
 String document();
 String chapitre();
 int page();
}

Voilà dans nos classes nous pourrons alors par exemple préciser à quelle partie de la documentation de la spécification cliente les classes font références:

@Reference(document=”docAmont.doc”,chapitre=”Service Client”,page=44)
public class ServiceClient
{
 public void service();
}

Imaginons maintenant une nouvelle annotation nécessitant un traitement plus profond à effectuer lors de la compilation. Par exemple une annotation @Relation qui associe a une classe permettra la production d’une table du même nom dans une bd (pour faire simple, nous passerons sur la production des attributs de relation mais on imagine facilement qu’il faudrait faire une annotation pour les données membres de la classe)

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Relation
{
}

Ainsi, sur une classe de notre application, nous placerons tout simplement notre annotation ainsi:

@Relation
public class HelloWorld
{
}

Alors bien sur si ceci nous permet de sémantiquement faire un lien entre notre classe et la relation d’une BD, il nous faut procéder à la mise en oeuvre d’un Processeur d’Annotation dont le but sera de récupérer toutes les classes postant celle-ci et exécutera un CREATE TABLE en sql sur la BD en question.

Pour réaliser un processeur d’annotation, il n’y a rien de complexe, il suffit de produire un simple artifact maven de type jar dans lequel nous trouverons:

  • une classe RelationProcessor dérivant la classe AbstractProcessor portant les annotations SupportedAnnotationTypes (précisant quelles annotations nous traitons) et SupportedSourceVersion (précisant dans quel version java la compilation aura lieu) 
  • un fichier de conf dans le répertoire META-INF/services nommé javax.annotation.processing.Processor et contenant le nom complet de notre classe processor.

A noter qu’il sera nécessaire de désactiver l’utilisation des processeurs d’annotations pour le packaging de notre propre processeur (sinon celui ci tentera se traiter lui même), il s’agit de l’option de configuration -proc:none du maven-compiler-plugin. Ensuite il suffira de faire une dépendance à la compilation pour que notre logiciel, lors de sa compile trouve par inspection le fichier de conf de notre jar contenant le processeur et l'exécute.

Voyons comment cela se presente:

Tout d’abord nous allons definir un pom module pour plus de simplicité:

   <modules>
    <module>tc-annotation</module>
    <module>tc-processor</module>
    <module>tc-helloworld</module>
   </modules>


Le premier module contient nos annotation, le second contiendra le processeur et le troisieme sera notre classe helloworld

Le module d’annotation n’a rien de special, c’est un projet maven classique produisant un jar (donc l’utilisation du plugin maven-compiler-plugin est largement suffisant) et contenant la définition de notre annotation:

package annot;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Relation {}



Le module processor est un peu plus complexe, comme nous l’avons vu il faut definir une classe heritant de la classe AbstractProcessor. Cette classe va recuperer l’ensemble des elements portant notre annotation et envera une requete en BD pour creer la table associée.

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({ "annot.Relation" })
public class HWProcessor extends AbstractProcessor {

    public static final String JDBC_DRIVER = "org.postgresql.Driver";
    public static final String DB_URL = "jdbc:postgresql://localhost:5432/test";
    public static final String USER = "postgres";
    public static final String PASS = "";
    
    public static String CREATE="CREATE TABLE ";

    @Override
    public boolean process(Set arg0, RoundEnvironment arg1) {
        System.out.println("HWProcessor running");
        Connection conn =null;
        try {
            Class.forName(JDBC_DRIVER);
            conn = this.getConnection();
        
        for (Element e : arg1.getRootElements()) {
            if (e.getAnnotation(Relation.class) != null) {
                this.createRelation(e,conn);
            }
        }
        } catch ( SQLException | ClassNotFoundException e1) {
            System.out.println(e1);
            return false;
        }
        return true;
    }
    
    public void createRelation(Element e,Connection conn) throws SQLException
    {
        PreparedStatement statement=conn.prepareStatement(this.CREATE+ e.getSimpleName().toString()+"();");
        System.out.println("Mon element : "+e.getSimpleName().toString());
        statement.executeQuery();
    }
    
    public Connection getConnection() throws SQLException {
        return DriverManager.getConnection(DB_URL, USER, PASS);
       }

}


a noter de ne pas oublier d’ajouter le fichier javax.annotation.processing.Processor

au niveau du build maven, il vous faudra ajouter la ligne -proc:none a la configuration du plugin maven-compiler-plugin. Ne pas oublié non plus de bien sur ajouter les dependances a l’artifact contenant l’annotation et ici en plus a postgres puisqu’il s’agit de la BD que nous utilisons.

 <build>
  <plugins>
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-compiler-plugin</artifactid>
    <version>3.7.0</version>
    <configuration>
     <verbose>true</verbose>
     <source></source>1.8
     <target>1.8</target>
     <compilerargument>-proc:none</compilerargument>
    </configuration>
   </plugin>
  </plugins>
 </build>

 <dependencies>
  <dependency>
   <groupid>org.tc.test</groupid>
   <artifactid>tc-annotation</artifactid>
   <version>0.1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
   <groupid>postgresql</groupid>
   <artifactid>postgresql</artifactid>
   <version>9.1-901-1.jdbc4</version>
  </dependency>
 </dependencies>


Enfin il reste a utiliser notre annotation dans notre projet. Donc evidement on depose l’annotation sur la classe:

import annot.Relation;

@Relation
public class HelloWorld {}


Enfin il faudra bien sur configurer notre build. Alors soit on precise dans le plugin maven-compiler-plugin que l’on va utiliser un artifact contenant un processeur d’annotation en eventuellement precisant le processeur :

   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-compiler-plugin</artifactid>
    <version>3.7.0</version>
    <configuration>
     <verbose>true</verbose>
     <source></source>1.8
     <target>1.8</target>
     <testsource>1.8</testsource>
     <testtarget>1.8</testtarget>
     <annotationprocessorpaths>
      <path>
       <groupid>org.tc.test</groupid>
       <artifactid>tc-processor</artifactid>
       <version>0.1.0-SNAPSHOT</version>
      </path>
     </annotationprocessorpaths>
     <!-- Soit les deux lignes suivantes soit le fichier dans le repertoire 
      services du jar contenant le processor -->
     <annotationprocessors>
      <annotationprocessor>proc.HWProcessor</annotationprocessor>
     </annotationprocessors>
     <!-- Ne pas faire de fork quand on utilise un processeur -->
     <!-- <fork>true</fork> -->
    </configuration>
   </plugin>



Mais cela, on peut aussi se reposer sur la capacité du jdk a decouvrir le processeur et ne declarer l’artifact que comme une dependance de compilation et ne pas mettre les champs annotationProcessors et annotationProcessorPaths (simplifiant largement la complexité du pom au passage). on prefere donc:

<dependencies>
  <dependency>
   <groupid>org.tc.test</groupid>
   <artifactid>tc-annotation</artifactid>
   <version>0.1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
   <groupid>org.tc.test</groupid>
   <artifactid>tc-processor</artifactid>
   <version>0.1.0-SNAPSHOT</version>
   <scope>compile</scope>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-compiler-plugin</artifactid>
    <version>3.7.0</version>
    <configuration>
     <verbose>true</verbose>
     <source></source>1.8
     <target>1.8</target>
    </configuration>
   </plugin>
  </plugins>
 </build>



Voila il ne reste plus qu’a builder notre module:

mvn clean install


ok ca marche:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] tc-annotation-0.1.0-SNAPSHOT ....................... SUCCESS [  4.078 s]
[INFO] tc-processor-0.1.0-SNAPSHOT ........................ SUCCESS [  1.929 s]
[INFO] tc-helloworld-0.1.0-SNAPSHOT ....................... SUCCESS [  1.223 s]
[INFO] tc-annotation-module-0.1.0-SNAPSHOT ................ SUCCESS [  0.038 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------



Voyons voir les log de notre processeur:

HWProcessor running
Mon element : HelloWorld
org.postgresql.util.PSQLException: Aucun résultat retourné par la requête.


Ok donc ca a l’air de s’etre bien passé vérifions la BD:

 psql -U postgres -w -d test -c "SELECT table_name from information_schema.tables where table_name='helloworld';"
 table_name
------------
 helloworld
(1 ligne)


Bonne nouvelle notre table est la dans le schéma!!

Bien voila, nous venons de passer en revu les deux ca d’utilisation que sont les annotations sur les sources et sur les classes. Leur objectifs ne sont clairement pas les même et si le premier cas ne nécessite pas beaucoup de travail, on peut voir qu’il faudra en fournir un peu plus sur le second en élaborant des processeur d’annotations.

Maintenant il reste un dernier cas d’utilisation, les annotations utilisés at runtime. Cependant désolé mais je ne traiterai pas de leur utilisation dans cet article. En effet, d’une part car cela relève globalement de la même approche introspective que pour la conception d’un processeur mais en employant plutôt quelques patterns bien senti comme l’Invocation Handler. Ce dernier est d’ailleur l’objet d’un prochain article et nous en profiterons alors pour jouer avec les dites annotations at runtime et nous verrons que le gros de travail sur le sujet a globalement été réalisé ici.

Références :

[1] Evolution Java 5