Thématiques principales

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

dimanche 3 février 2019

OSGI : Felix

Nous revoilà avec OSGI mais cette fois ci pour aller dans le concret, plus de blabla, maintenant on fait chauffer la JVM.

Le problème du choix

Bien tout d’abord il nous faut choisir un framework, c’est à dire une implémentation. Nous en avons vu plusieurs dans l’article précédent [osgi-fwk] et il nous faudra faire un choix. À mon sens il y avait plusieurs possibilité:

  • Knoplerfish [knoplerfish] est trop compliqué bien qu’à jour dans l'implémentation des versions OSGI
  • Karaf [karaf] est un peu trop lourd pour ce que nous voulons montrer ici (mais nous ferons un article dessus)
  • Equinox [equinox] est sympas je l’aurais choisi si je ne prévoyais pas de présenter mon framework equinox-loader [eq-loader] qui l’utilise
  • Concierge [concierge] est hors propos, on ne va pas faire de Iot
  • Donc en toute logique, on va essayer l’utilisation de OSGI avec Felix [felix]

Tout d’abord il faut télécharger la version courante: c’est ici [felix-down]

Dockerisation

Pour simplifier son utilisation, nous allons préalablement le dockeriser pour faciliter son exploitation, on construit donc un fichier Dockerfile dans un répertoire (moi je l’ai nommé felix-framework) contenant le processus de construction suivant:

FROM openjdk:8-jre-alpine

RUN wget http://mirror.ibcp.fr/pub/apache//felix/org.apache.felix.main.distribution-6.0.1.tar.gz \
&& tar zxvpf org.apache.felix.main.distribution-6.0.1.tar.gz && rm org.apache.felix.main.distribution-6.0.1.tar.gz

COPY . /felix-framework-6.0.1/
CMD cd /felix-framework-6.0.1; java -jar bin/felix.jar


jeudi 24 janvier 2019

OSGI : Architecture

Nous avions vu dans un précédent article ce qu'était OSGI dans ses grandes lignes et ses principaux concepts [uetteu-osgi]. Dans celui ci, je vous propose de rentrer un peu plus dans le côté technique de cette technologie en détaillant globalement les utilisations de celle ci (surtout pour rappel), d’identifier son écosystème et ensuite de passer en revue la construction des bundles, leur constitution, leur cycle de vie et la déclaration et consommation des services.

À la suite de cela, nous nous intéresserons alors aux différents implémentations du framework réalisé au fil de ces dernières années pour enfin étudier (sommairement) leur différentes intégrations au sein des serveurs d’application Java les plus connu (à notre insu)

Architecture: Vue d’ensemble

Nous avions vu dans ce dernier article [uetteu-osgi] que OSGI était un framework Java modulaire orienté service permettant la modélisation et l'exécution de composants nommés bundle.

Ils permettent à mise en oeuvre d’application dans des contextes d'équipement à ressources limitées, s’appuient sur un modèle de collaboration utilisant une registry pour le partage de services et facilitant l'accès à ces derniers pour les consommateurs.

OSGI permet aussi une gestion à chaud de ses bundles offrant ainsi la possibilité de chargement, mise à jour et déchargement de fonctionnalité dynamiquement sans interruption de service.

Enfin OSGI offre un cycle de vie fourni un cycle de vie à ces bundles et services (que nous verrons dans un des chapitres suivant) permettant la mise en oeuvre d’une gestion intelligente des fonctionnalités mis en ligne.

C’est pour ces différentes raisons que OSGI est aujourd’hui intégrée par défaut dans la grande majorité des serveurs d’applications JEE permettant l’ajout de fonctionnalité java standard de manière plus aisé et ce même dans un contexte JEE.

Environnement d'exécution

De par son côté modulaire, OSGI offre donc la possibilité de construire des applications via une logique de puzzle où de briques, constitué selon des niveaux fonctionnelles différentes. Cette approche a donc permis de constituer un catalogue riche et varié de bundles offrant une multitude de services métiers et ou technique [tuto-osgi-oscar].

On trouvera ainsi facilement sous la forme de bundle des

  • services de base permettant l’utilisation de logs
  • bundle de gestion de configuration
  • bundles de gestions de Préférences
  • Services HTTP (e.g :servlets, ou Spark [uetteu-spark]))
  • Gestion des utilisateurs
  • Parseurs XML
  • Gestion de droits,
  • Politique de sécurité
  • Monitoring
  • de l'intégration JEE [integ-jee]



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

mardi 8 mai 2018

Iod, l'injection de dépendances

Nous avons vu dans l’article précédent, le principe de l’inversion de contrôle. Ce dernier se base sur les principe de SOLID et est indispensable aux frameworks modernes tels que Spring qui l’utilise, on peut presque le dire, à outrance dans leur système d’injection de dépendances.

Dans cet article, justement, je vous propose de regarder comment fonctionne un mécanisme d’injection de dépendance et vous invite donc si vous ne l’avez pas vu à regarder l’article [1] afin d’en acquérir les pré-requis.

J'aurais peut être dû faire un article commun pour traité les sujets de IOC et de IOD car les deux sont souvent intimement lié et même traité ensemble. Cependant, d’une part, le temps dont je dispose ces derniers temps c’est gravement amoindri donc c'était plus simple d’en faire deux et d’autre part, je trouve que si l’IOD dépendant de l’IOC, l’inverse n’est pas vrai et heureusement (ca serait presque paradoxale…. ^^)

Donc du coup voilà, qu’est que IOD où injection de dépendance [2]. Le principe est simple, il s’agit de donner la responsabilité de la construction de la dépendance entre deux objets a un troisième. A ce stade on peut se dire que ce que nous avions vu sur l’inversion de contrôle est suffisant, on crée une factory pour produire des objets et op on l'appelle lorsqu'il faut construire des objets qui en ont besoin. Si l’idée de base est bien celle-ci, il nous manque un partie importante, l’orchestration.

En effet, dans le principe, l’injection de dépendances repose globalement sur les même mécanismes que l’inversion de contrôle car, l’inversion de contrôle permet de déplacer la problématique de la gestion du cycle de vie d’un objet dont un objet en avait la responsabilité, auprès d’un autre. Du coup forcément, pour permettre la mise en relation, l’objet initial doit toujours pouvoir obtenir l’objet dont il a besoin.

Ainsi dans l’article précédent [1] on avait forcément en plus de l’IOC une forme implicite d’injection de dépendance.

def mFactory=new MoteurFactory()

new Voiture(mFactory.buildMoteurX()).drive()
new Voiture(mFactory.buildMoteurX()).drive()


Mais bon on s’accordera que ça mériterait, un petit peu plus d’outillage.

Ainsi, ce qui manque c’est d’un moyen permettant de faire cette mise en relation de la voiture avec le moteur implicitement. Le premier réflexe, qui n’est pas forcément une mauvaise idée, est de faire appelle à une autre factory. La combinaison des factory est souvent dans un périmètre limité une bonne solution.


class MoteurFactory
{
   def buildMoteurX()
   {
       return new MoteurX()
   }

   def buildMoteurY()
   {
       return new MoteurY()
   }
}

class VoitureFactory
{
   def buildWithMoteurX(MoteurFactory f)
   {
       return new Voiture(f.buildMoteurX())
   }

   def buildWithMoteurY(MoteurFactory f)
   {
       return new Voiture(f.buildMoteurY())
   }
}

def mFactory=new MoteurFactory()
def vFactory=new VoitureFactory()

def v1=vFactory.buildWithMoteurX(mFactory)
def v2=vFactory.buildWithMoteurY(mFactory)

v1.drive()
v2.drive()

Bien ça marche, cool mais en fait on se rend vite compte qu’on a déplacer le problème! ba oui car la on passe en paramètre la factory des moteurs à celle des voitures. On a donc aussi un problème de gestion de dépendance! Mais ne peut on pas faire une factory pour gérer cela? ba si allons jusqu'au bout de l’idée


class MoteurFactory
{
   def buildMoteurX()
   {
       return new MoteurX()
   }

   def buildMoteurY()
   {
       return new MoteurY()
   }
}

class VoitureFactory
{
   MoteurFactory facto

   def VoitureFactory(MoteurFactory facto)
   {
       this.facto=facto
   }
ca 
   def buildWithMoteurX()
   {
       return new Voiture(this.facto.buildMoteurX())
   }

   def buildWithMoteurY()
   {
       return new Voiture(this.facto.buildMoteurY())
   }
}

class MetaFactory
{
   def buildFactoryForCar()
   {
       return new VoitureFactory(new MoteurFactory())
   }
}

def mf=new MetaFactory()

mf.buildFactoryForCar().buildWithMoteurX().drive()
mf.buildFactoryForCar().buildWithMoteurY().drive()

Bien on voit que ça marche, mais ça fait beaucoup de factory! Et bien oui et c’est pour cela que l’on utilise des framework comme Spring [3] ou CDI [4].

Voilà, j'espère qu’avec ces deux articles et cet exemple très simple, on a un peu démystifier l’IOD et IOC et permit de mieux aborder les mécanismes sous jacent a des framework comme SPring et CDI qui se basent eux aussi (aux registres et annotations près) sur ce bon vieux pattern factory. A très vite donc sur Spring et CDI!

Références

[1] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/05/ioc-inversion-de-controle.html
[2] http://igm.univ-mlv.fr/~dr/XPOSE2010/guicespring/di_presentation.html
[3] https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
[4] https://rmannibucau.developpez.com/tutoriels/cdi/introduction-cdi/

samedi 5 mai 2018

Ioc, Inversion de controle

Dans les articles précédents, nous avons à traiter des sujets se rapportant à l'écosystème Java et entre autre a JEE avec JMS. Bien sûr nous ne nous arrêterons pas là et nous nous intéresserons à JPA, le JSP, les Servlet et les EJB. Cependant si ces concepts sont le cœur de JEE, il serait dommage de ne pas traiter également des alternatives possibles à JEE, comme par exemple Spring.

Dans cet article, nous n’allons pas traiter directement Spring mais introduire quelques concepts clefs afin d'appréhender au mieux les mécanismes qui en sont les fondamentaux : l’inversion de contrôle et l’injection de dépendance.

Commençons par l’inversion de contrôle. Nous l’avions évoqué dans un article précédent traitant des principes SOLID [1], nous avions traité de la problématiques de l’inversion de dépendances (principe D) qui par la construction d’entité abstraite, nous permet de découpler le code afin qu’il respecte au mieux le principe de responsabilité unique (principe S). Nous avions évoqué justement qu’en poussant le raisonnement plus loin, ce principe nous permet d’aller jusqu'à l’inversion de contrôle.

Bien sur il existe des patterns très bien fait qui nous permettent facilement de faire de l’inversion de contrôle comme par exemple le pattern Visiteur [2] ou le pattern Stratégie [3] couplé à un pattern factory [4]. C’est par cette deuxième approche que nous allons présenter l’inversion de contrôle (et donc implicitement de dépendances)

Posons le problème. Imaginons une voiture capable de rouler et pour cela, bien sur la voiture va employer un moteur. La première approche qui aura le mérite de marcher sera probablement d'écrire un code proche de celui ci (en groovy):


class Voiture{

   Moteur moteur=new Moteur()

   def drive() {
       this.moteur.active()
       println("la voiture avance")
   }
}

class Moteur
{
   def active() {
       println("le moteur tourne");
   }
}

def voiture=new Voiture()
voiture.drive()

Même s’il respecte une certaine décomposition, ce code souffre d’un problème de couplage fort entre les classes Voiture et Moteur. En effet, dans ce cas, il est impossible de prévoir une voiture avec un moteur qui serait différent en sortie d’usine! Il faudrait ajouter un moyen permettant de changer ce moteur après la construction comme une méthode supplémentaire du type changeMoteur. Ce n’est pas acceptable.

Ainsi pour répondre à cela, on va suivre le principe I de SOLID en rendant abstrait le moteur dans la voiture modélisant ainsi qu’il sera possible d’utiliser des moteurs différents.

Cela donne donc ceci:


class Voiture{

   Moteur moteur;

   def Voiture(Moteur m)
   {
       this.moteur=m;
   }

   def drive() {
       this.moteur.active()
       println("la voiture avance")
   }
}

abstract class Moteur{
   def abstract active()
}

class MoteurX extends Moteur{
   def active() {
       println("le moteur X tourne");
   }
}
class MoteurY extends Moteur{
   def active() {
       println("le moteur Y tourne");
   }
}

new Voiture(new MoteurX()).drive()
new Voiture(new MoteurY()).drive()

On a réussi à découpler le moteur de la voiture lors de sa création mais on a pas vraiment rationalisé la construction des moteurs pour les voitures. C’est donc la que va intervenir le pattern Factory en fournissant une entité unique permettant la création des moteurs (et cela nous permet de respecter le principe S). Voici donc comment évolue notre code, suite a l’ajout d’une factory:

class MoteurFactory
{
   def buildMoteurX()
   {
       return new MoteurX()
   }

   def buildMoteurY()
   {
       return new MoteurY()
   }
}

def mFactory=new MoteurFactory()

new Voiture(mFactory.buildMoteurX()).drive()
new Voiture(mFactory.buildMoteurX()).drive()

Voilà, les voitures n’ont plus la responsabilité de construire leur moteur, c’est la factory qui s’en chargera nous permettant de choisir le moteur le plus adapté. Du point de vue de la voiture, pour elle, cela reste transparent, puisque tous les moteurs que l’on peut lui donner respecte la charte d’utilisation (on respecte alors le principe I).

L’inversion de contrôle et l’inversion de dépendances ne sont pas des principes très complexe à appréhender ni à mettre en oeuvre pour le peu qu’on les démystifient . Il s’agit surtout d’un principe de décomposition [5], dont le but est le respect des principes SOLID.

Pour l’injection de dépendances, je vous invite au prochain rendez vous, dans un article suivant.

Références

[1] http://un-est-tout-ethttps://blog.imirhil.fr/2013/05/19/inversion-de-controle-cest-bon-mangez-en.html-tout-est-un.blogspot.fr/2018/04/solid.html
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/design-pattern-visiteur.html
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-strategie.html
[4] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-factory.html
[5] https://blog.imirhil.fr/2013/05/19/inversion-de-controle-cest-bon-mangez-en.html