Thématiques principales

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

vendredi 28 décembre 2018

OSGI : Concepts généraux


Aujourd’hui nous allons parler d’un framework qui me tient à coeur: OSGI. Cela faisait un moment que je voulais écrire un article sur celui ci mais beaucoup d’autre sujet, se sont trouvé être plus intéressant sur le moment.

À vrai dire, OSGI est clairement très intéressant mais l’utilisant depuis déjà près de 8 ans, malgré l'intérêt qu’il a intrinsèquement, il ne porte plus pour moi ce goût de nouveau que peuvent avoir les sujets comme l’IA... , c’est évident.

Pourtant voilà, je me suis dit, il est temps de faire le point sur le sujet et de le mettre un peu plus sous les projecteurs, car mine de rien, quasiment tout le monde l’a un jour utiliser, mais très peu de gens le savent!

Mais repartons du début!

Historique

OSGI [OSGI], [wikipedia] ou Open Service Gateway Initiative est un framework de gestion de composants et de services. Initialement prévu pour le monde de l’embarqué (si si ^^), il a été utilisé par des constructeur comme Volvo, BMW ou encore Cisco et s’emploie dans les produits logiciels à destination du monde de la domotique et des systèmes de sécurités

Nous le verrons plus en détail mais pourquoi OSGI? parce que ce framework est léger (en dehors de la JVM) et permet le chargement et le déchargement de ses librairies à chaud tout en facilitant la manipulation de ces dernières dans des versions différentes simultanément.

Issu de la JSR 8 [JSR8], OSGI a été créé dans les années 2000 et a évolué régulièrement. Ainsi aujourd’hui la spécification OSGI en est à la version 7 [OSGI-V7] avec une implémentation de référence fourni par l’Alliance OSGI qui ne doit pas être considéré comme une version de production mais “juste de références”. Ainsi différentes versions open sources ont été implémenté pour répondre à des besoins plus techniques et se sont accaparé ces différentes versions des spécifications comme equinox [equinox], knoplerfish [knoplerfish], concierge [concierge] ou encore felix [felix].

Aujourd’hui la spécification 7 de OSGI forme un pavé de plus de 1000 pages de documentation [OSGI-V7-CORE] et il nous est impossible d’en faire un résumé exhaustif ici cependant dans cet article nous tâcherons de passer en revu les concepts pivots de OSGI pour en comprendre les mécanismes clefs puis nous nous intéresserons aux différentes implémentations ainsi que leur intégrations dans les frameworks plus classique (mais qu’on sait pas que OSGI est dedans…. O_o)

OSGI concepts généraux

OSGI est donc un framework Java. Conçu il y a près de 18 ans, celui ci est un peu partout sans que personne ne le sache vraiment. On le trouve dans la plupart de nos serveurs d’applications JEE, jusque dans nos IDE comme eclipse qui à fondé sa architecture dessus… si si…

Bien sûr ce n’est pas parce que quelque chose est utilisé partout que forcément c’est bien…. c’est vrai on le voit tous les jours avec le framework Spring! non blague à part (ou pas), OSGI est partout mais pour de bonnes raisons et c’est justement sa discrétion qui en fait sa force et sa pertinence.

En effet, OSGI est un framework amenant des moyen technique de mise en oeuvre logicielle mais aussi des moyens conceptuelles, apportant avec lui quelques paradigme de modélisation simple mais efficace laissant libre le développeur de faire ce qu’il veut tout en lui garantissant plus de souplesse à son application.

Il fournit pour cela une implémentation à deux paradigmes importants en développement logiciel:

  • la modularité qui permet la gestion et l’articulation logique des éléments de l’application
  • les services qui fournissent les moyens de donner une opérationnalitée à ces éléments en leur donnant du sens.

La modularité dans OSGI

Sans aller trop loin dans le débat de la modularité [serv-side], on rappellera quelques principe de façon à poser ce à quoi nous attendre dans OSGI

La modularité est une approche de conception permettant la séparation des préoccupations, que celles-ci soient fonctionnelle, ou technique.

Bien sur il existe différents degrés de modularité et celle-ci s’appliquant sur la solution logicielle à des niveaux très variés.

Il est ainsi possible de concevoir de façon modulaire tant au niveau de l’architecture système que de l'architecture logicielle. Quel que soit l’objectif métier, le but est de permettre de scinder les problématiques et amener du découplage facilitant ainsi: la réalisation, le test, la compréhension, la maintenance, la mise à jour, etc...



Pour mettre en oeuvre de la modularité dans une architecture, il existe divers solutions et implémentations: certains design pattern comme l’observateur [observ], l’association du pattern proxy et factory [invochand] ou encore l’utilisation de contrat de services avec des interfaces et des API comme illustré ci dessus.

OSGI propose une autre alternative en se situant à la croisé des chemins de ces approches. Nous en avions parlé dans l’article [whriteboard]. En résumé, la modularité consiste en la construction de composants interdépendants via des interfaces (API). Des lors, l’utilisation d’un module par un autre passe par une api et masque donc l’identité du composant réellement utilisé, c’est le principe du masquage de l'implémentation permettant ainsi découplage, réutilisation, capacité à être testé etc…

Cependant, on constate une limite a l’utilisation des contrats de service et des API : il faut etre capable de re-associer les composants interdépendants [serv-side]. Il faut pour cela utiliser des patterns comme la Factory et au final déplacer le couplage vers la factory [whiteboard].


Alors bien sur il existe d’autres approches pour répondre à cette problématique avec les framework d’IoC comme Spring (implémentant le pattern Hollywood [hollywood]) utilisant des xml ou des annotations. Pourtant ces solutions sont assez peu satisfaisantes car elles confient les détails d'implémentations à un acteur tiers qui aura la charge de faire communiquer les composants entre eux sur la base d’une description donné statiquement. Finalement cela re-créera un couplage car si en effet dans Spring (par exemple) on spécifie la dépendance d’un composant via une API et des interfaces, il faudra lui spécifier un à moment l'implémentation qui y répondra et cela de façon statique par configuration.

Le pattern Broker

OSGI fournit une alternative en utilisant le pattern Broker [broker] permettant, au travers d’un registre de bundle (nom des composants ou modules OSGI) et de service, de déporter le couplage en dehors de la préoccupation des composants mis en interaction.

Il s’agit avant tout d’un pattern d’architecture (non d'implémentation) qui classiquement s’utilise dans le cadre de service de messagerie comme JMS mais qui ici se focalise sur la mise en collaboration des bundles entre eux, à la demande, et ce géré par le registre.

Dans sa mise en oeuvre, un ensemble de composants (les bundles) vont s’enregistrer auprès du registre comme fournisseur de services liés à différents contrats d’interfaces. Ensuite, par l'intermédiaire du registre, ils seront appelé par d’autres bundle ayant besoin des services remplissant les contrats d’interfaces souhaités.

Dans ce principe, aucun bundle ne se connait directement, tout passe par le registre qui aura à gérer l’ensemble des services offerts et les clients qu’il faudra rediriger vers les bonnes implémentations.


Les services

OSGI est une implémentation du pattern Broker. Il s’agit donc en réalité d’un framework permettant la conception logicielle de façon modulaire mais et surtout de construire des applications selon une logique orienté service.

Ainsi comme nous venons de le voir la modularité va nous amener à séparer les préoccupations, les services vont en plus nous amener à réfléchir nos bundles selon le rôle qu’ils ont à mener dans notre application de façon à en exposer juste et seulement leur valeur ajouté.

L’idée est la encore et toujours la modularité, mais en ayant une forte décomposition, on maximise alors le re-use car un service implémentant un contrat de service pourra être mis à disposition par le registre à tous les bundles en ayant le besoin.

Vous allez dire mais ça Spring le fait déjà au final le registre, c’est le fichier xml de Spring.

Effectivement mais OSGI va plus loin:

  • D’une part, l’utilisation du registre va autoriser un bundle à consommer un service à la demande et de s’en défaire, le couplage entre les bundles est dynamique et non permanent.
  • La conséquence du point précédent que nous détaillerons plus loin dans l’article est que cela permet la mise en ligne des bundles sans que ces derniers n’aient eu le besoin de résoudre à leur chargement toute leur dépendance et service.
  • Ceci nous amènera alors à la possibilité de gérer des versions concurrente d’un même service pour faciliter leur mise à jour à chaud.

Avec OSGI, la gestion des services par le registre des bundles nous laisse entrevoir quelques fonctionnalités dont nous n’avons pas vraiment l’habitude. Dans un projet classique, généralement nous pensons notre application comme un tout unique dont les éléments, librairies et services, devront évoluer conjointement. Mais avec OSGI, cette vision limitative devient caduc et il faudra penser l’application comme un système en perpétuel mutation dont les éléments nouveau côtoient des éléments anciens et dont les services seront utilisés selon le besoin.

Gestion des dépendances

Pour permettre des mécanismes de gestion à chaud des bundles et de la consommations dynamiques des services, OSGI va donc fournir un certain nombre de concepts focalisé sur une gestion un peu particulière des dépendances.

Cette gestion des dépendances va s’exprimer selon deux types [10min]:

  • les dépendances statiques
  • les dépendances dynamiques

Statiquement

La gestion statique des dépendances dans OSGI est assez similaire à la gestion des dépendances Java réalisé avec le classpath à ceci prêt que celui ci est plus stricte car embarque avec les lui le detail des versions impliqué dans la dépendance.

Cela paraît dans un premier temps très contraignant cependant cela permet en fait de faire vivre au sein du framework deux versions distinctes d’une même dépendance, laissant alors au composant qui en ont besoin d’utiliser celle avec laquelle il sera le plus en phase.

Ce type de dépendance se déclarent au sein du manifest java décoré et adapté au formalisme OSGI (nous verrons cela plus en détails dans le chapitre sur les bundles).

Ainsi les bundles lorsqu’ils seront chargé par le framework OSGI (on verra plus tard mais il s’agit de la phase d’installation) devront trouver dans leur classpath ou dans les classloader du framework les dépendances apportées par les autres bundles dans les versions attendues


Dynamiquement

Une fois installé, un bundle apporte un ensemble de dépendance statique, cependant, pour créer un minimum de couplage, il est préférable de s’appuyer autant que faire se peut sur le pattern broker.

Ainsi pour cela le framework OSGI nous amene à construire des bundles définissant des contrats des services ne contenant que des informations spécifiques aux contrats de services.

Ainsi ces contrats seront ensuite implémentés par un autre bundle qui aura la charge de l'implémentation du contrat de service. Ainsi, ce bundle s'enregistrer auprès du registre et lorsqu’un autre bundle demandera au registre OSGI un service conforme au contrat de service défini dans le bundle contrat alors le registre fournit l'implémentation.

L'intérêt de cette approche est qu’il est possible alors de proposer divers implémentation d’un même contrat de service.


Conclusion

Nous avons dans les grandes lignes les principes généraux de OSGI, son paradigme de modélisation modulaire et orienté service.

L'intérêt de son utilisation est double, d’une part de fournir une solution réelle de couplage des modules mais aussi la capacité de faire évoluer une application dynamiquement tout au long de son cycle de vie et ce, “à chaud”.

Il peut être intéressant maintenant d’en parcourir l’utilisation dans le concret (dans le prochain article sur OSGI) afin de mieux appréhender ses contraintes techniques.

Références

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

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