Thématiques principales

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

jeudi 8 novembre 2018

Design Pattern : Whiteboard

Pourquoi le pattern whiteboard?

Le pattern Whiteboard [1, 2] est un pattern un peu spécifique, il à la particularité d'être intimement associé à la technologie OSGI [3, 5] et comme il était question dans ce blog de parler de ce framework, nous voici donc avec une petite introduction de ce dernier via ce pattern de conception.

Au delà du prétexte de l'écriture d’un article introductif à OSGI, ce pattern pattern à bien entendu une raison d'être qui lui est propre: proposer une alternative raisonnable au pattern listener dans la conception de logiciel à base de microservice comme le permet OSGI [4].

On entrevoit donc ici les raisons de l’utilisation du pattern mais pour etre plus precis, nous regarderons avant cela les concepts de base de OSGI afin d’en comprendre les besoins et avant de détailler plus en avant le pattern whiteboard, nous regarderons pourquoi le pattern listener n’est plus satisfaisant dans certaines situations.

Techno OSGI dans les grandes lignes

La technologie OSGI ou Open Service Gateway Initiative est un framework Java pour la construction d’application à base de composants et de services. Il s’agit d’un framework ayant pour vocation initial la réalisation d’application à taille réduite où la gestion mémoire est millimétré.

Il permet techniquement de gérer simultanément des librairies java évoluant dans des versions différentes ainsi que de charger et décharger ces librairies dynamiquement sans nécessiter le redémarrage de la JVM. Par contre, pour permettre ces capacités, OSGI “impose” (mais en fait c’est un bien) un paradigme de modélisation impliquant la construction de nos application selon une architecture un peu spécifique à base de composant (les bundles) et de services.

Avec cette petite introduction, on perçoit rapidement l'intérêt de ce framework! Nous y reviendrons plus tard, nous en avons vu l’essentiel pour l’instant. Il faut surtout en retenir que OSGI nous permet de construire des applications modulaires à base de composants et de services. Dédié initialement à l’embarqué, il est sortie rapidement de son contexte d’utilisation initial et s’est alors confronté aux méthodes et techniques de construction des architectures logiciels classiques.

Pourtant avec une architecture tel que proposée par OSGI, ce pattern comporte de nombreuses limites.

En effet, dans une architecture à composant ou les dépendances entre composant ne peuvent être forte, il est nécessaire de permettre à deux objets de communiquer sans forcément qu’ils aient connaissance de l’un et de l’autre directement.

Pattern Listener et limites

Entre autre, le cas du pattern Listener est caractéristique car dans le cadre de son utilisation dans le cadre OSGI, il comporte quelques limites.

Nous avons traité le pattern listener dans un autre article [6], je n’y reviendrais pas ici. disons simplement que ce pattern est un pattern d’architecture et comportemental permettant le découplage d’un observateur et d’un observer tout en formalisant son moyen de communication via un objet de type événement. Il s’agit en fait du pattern observateur mais simplifié.

Ce pattern est souvent utilisé pour sa nature événementielle dans le cadre de la gestion des IHM. Ainsi, par exemple, lorsque utilisateur sollicite la souris de son ordinateur, le contrôleur associé va générer des événements permettant de suivre ses déplacements. L'utilisation du pattern listener est en première approche une solution intéressante pour traiter ces événements surtout lorsque les sources possibles sont multiples. Pourtant c’est là sa limite également. Car alors si ces sources produisent de multiples événements simultanément alors le système peut être soumis à un “EventStorm” [1] amenant à une utilisation élevée de la mémoire et du CPU, perdre gravement en performance et dans le pire des cas conduire à un crash.

Whiteboard pattern

Le pattern Whiteboard est une solution apportée comme alternative au pattern listener dans le cadre du framework OSGI.

Il est intimement lié à OSGI cependant comme nous verrons ce framework par la suite il me semble plus pertinent d'éviter d’entrer dans trop de détails d'implémentation lié à cette technologie. Ainsi nous nous attacherons à une présentation d’un modèle abstrait du pattern (sous la forme d’un type PIM, Plateform Independant Model dans le MDA [7])

Ce pattern propose de découpler la source des événements du listener en introduisant un objet supplémentaire entre les deux servant de registre et transformant la relation entre les deux éléments par une relation de service et de consommateur de service.

Ce qui servait donc de listener devient un composant fournissant un service qui sera enregistrer dans un registre. La source des événements aura alors la tâche de demander au registre le service adéquat afin de pouvoir lui transmettre les événements produits.




L'intérêt de l'approche en ajoutant ce registre qui finalement réalisé la réification de la relation listener/sources des événements est de permettre de contrôler cette relation et son utilisation par la source des événements.

Il va être alors possible :
  • de réaliser du monitoring sur le flux d’informations voir même de le debugger
  • de rendre indisponible le service du listener si celui ci est trop utilisé et implique une consommation des ressources trop importante
  • d’adjoindre des droits spécifiques d’utilisations en ajoutant sur le registre une couche de sécurité et des permissions afin de ne pas le rendre disponible à n’importe quel consommateur du service venu
  • injecter des propriétés spécifique pour customiser le mapping (comme par exemple pour préciser un quota en événement par seconde à traiter ou pour proposer une taille de tampon d'événements, etc…)

Exemple

Pour illustrer ces mécanismes voici dans un contexte hors OSGI ce qu proposerait une implémentation du pattern listener (observateur) et ensuite son équivalent dans la philosophie du pattern Whiteboard. On précise ici que l’on reste dans une implémentation de type PIM afin de ne pas perturber la compréhension du pattern avec les spécificités technologiques du framework OSGI

Avec Listener


package listener

class Event{
    String message

    Event(String m){
        this.message=m
    }
}

interface IObserver{
    def update(Event e)
}

interface IObserved{
    def notifyAll(Event e)
}

class TrucQuiEcoute implements IObserver{
    def update(Event e){
        println("Reception d'un message")
        println(">>"+e.message)
    }
}

class TrucQuiFait implements IObserved{
    List obs=new ArrayList<Observer>()

    def notifyAll(Event e) {
        for( Observer o in obs){
            o.update(e)
        }
    }

    def makeSomething() {
        def e = new Event("Ceci est le message")
        println("Envoie d'un message")
        this.notifyAll(e)
    }
}


def ob=new TrucQuiEcoute()
def obd=new TrucQuiFait()
obd.obs.add(ob)
obd.makeSomething()

Avec Whiteboard


package whiteboard

interface IService{
    def serve(Event e)
}

class ServiceRegister{
    Map services=new HashMap<String,IService>()
}

class Event{
    String message

    Event(String m){
        this.message=m
    }
}

class TrucQuiEcoute implements IService{
    def serve(Event e){
        println("Reception d'un message")
        println(">>"+e.message)
    }
}

class TrucQuiFait implements IService{
    ServiceRegister register;

    TrucQuiFait(ServiceRegister register)
    {
        this.register=register;
    }

    def serve(Event serviceNameEvent) {
        def e = new Event("Ceci est le message")
        println("Envoie d'un message")
        this.register.services.get(serviceNameEvent.getMessage()).serve(e)
    }
}

def reg=new ServiceRegister()
reg.services.put("TrucQuiEcoute",new TrucQuiEcoute())
reg.services.put("TrucQuiFait",new TrucQuiFait(reg))

reg.services.get("TrucQuiFait").serve(new Event("TrucQuiEcoute"))

Conclusion

Voilà nous avons fait le tour de ce pattern un peu spécial enfin, surtout un peu spécialisé, qu’est le pattern Whiteboard. Celui-ci est très associé à OSGI mais il peut se comprendre sans et surtout mieux comprendre certains mécanisme de ce framework que nous verrons dans un prochain article.

Références

[1] https://www.osgi.org/wp-content/uploads/whiteboard1.pdf
[2] https://en.wikipedia.org/wiki/Whiteboard_Pattern
[3] https://www.osgi.org/
[4] https://enroute.osgi.org/FAQ/400-patterns.html
[5] https://www.theserverside.com/news/1363820/The-Whiteboard-Pattern-for-OSGi
[6] https://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-observateur.html
[7] https://laine.developpez.com/tutoriels/alm/mda/generalites-approche-mda/



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

samedi 7 avril 2018

Design pattern : MVC

Le pattern MVC est l’un des patterns les plus connu du monde de l'ingénierie logicielle. Celui ci est peut être même le représentant et le plus emblématique de la notion de pattern de conception. Cependant ce pattern a beau être le plus connu, il est aussi probablement l'un des plus complexe et des plus diversifié dans ses implémentations.

Ce pattern MVC est avant tout un pattern de conception. Il a ensuite été généralisé en tant que pattern d'architecture mais aussi adapté (pour ne pas dire déformé) pour des contextes de conception et de réalisation un peu plus éloigné de son objectif initial.

Dans son principe, et nous l'avons déjà souvent vu avec les autres pattern, le pattern MVC mène lui aussi à la separation des preoccupations en s'attachant à traiter la problématique de l'indépendance des interfaces homme machine (et de ses interactions) et des composantes métiers du logiciel. Bien sur, si cela a été son but initial, aujourd'hui son utilisation a plus de cadre d'application que cela, et de nombreuse déclinaisons sont possibles tant que le soucis de découplage est présent au sein d'entités communicantes de façon cyclique (nous allons voir rapidement pourquoi).

Ainsi pour procéder a ce découplage, MVC propose de découpler la solution logicielle en 3 entité distinctes que sont le Modèle, la Vue et le Controller (d'où MVC... héhé!).


Je vais éviter de faire des paraphrases sur le rôle de ces différents éléments mais malgré tout je n'utiliserai pas non plus d'exemple pour illustrer ces concepts car cela dénaturerait l'objet initial de ce pattern qui a la base ne définit pas spécifiquement de moyen d'implémentation. Dans l'explication qui sera donnée, on pourrait aussi avoir l'impression que nous définissons les choses sur la base des autres définition donnant l'impression d'une explication un peu cyclique mais dans le principe, le MVC est cyclique donc... finalement c'est normal.

Le concept de modèle dans le MVC peut être vu (dans un premier temps car en fait non mais ca peut etre plus simple de prime abord expliqué ainsi) comme l'élément pivot du pattern. Le modèle concentre l'ensemble des informations qui seront nécessaires à la vue. Première confusion possible, le modèle n'est pas le modele metier de votre application, il n'est que le modèle de la vue et des informations qui devront y être exposé. C'est subtile mais important nous verrons pourquoi après.

La vue justement est le porteur de l'information graphique, elle définit structurellement la manière dont seront agencées les données extraites du modèle et les encapsulant dans des éléments décoratifs spécifiques à la technologie d'interface en se limitant justement au côté affichage.

Le contrôleur prend ensuite le relais justement sur les éléments de la vue susceptible d'être le lieu d'émission d'événements consécutif aux actions de l'utilisateur. Attention, le contrôleur n'est pas derrière la vue ou même dans la vue mais il est la facette événementielle de celle ci permettant la gestion des actions. Bien sûr, nous avons le sentiment que l'action est corrélé avec chacun des éléments de la vue mais cela est dû à une propriété particulière du pattern MVC que nous détaillerons par la suite, la composition. Gardons en tête pour l'instant que le contrôleur a pour rôle d'intercepter les actions utilisateur et en conséquence de quoi d'aller indiquer au modèle qu'une modification est à prendre en compte.

Nous voyons donc que la structure cyclique du pattern MVC est importante. Le modèle renseigne la Vue qui expose les actions possible que le contrôleur va intercepter et solliciter le modèle pour sa mise à jour qui du coup va permettre à la vue de se mettre à jour.

Structurellement on a compris comment cela fonctionne. Cependant nous n'avons pas parler de comment le pattern va s'implementer. Pour implementer le pattern MVC, il est important de comprendre que sa préoccupation principale est le découplage, que ce soit bien sur des aspects représentations, contrôle ou gestion des données mais aussi et surtout au niveau de la granularité. Ainsi ce pattern n'est pas forcément contraint de fonctionner en trinôme. Pour un même modèle, plusieurs vue peuvent exister, celle représentant l'information a sa manière et avoir en conséquence des contrôleurs spécifiques pour chacunes d'elle ou pas si l'action résultant est finalement la même (on pourrait imaginer des boutons tous différents mais ayant la même finalité....) De même un pattern MVC peut aussi être une composition de pattern MVC permettant par exemple une gestion coordonnées de fenêtre et de taille. On remarque justement très bien ici que pour plusieurs modèles et vues (éventuellement encapsulé les unes dans les autres) on n'aurait qu'un seul contrôleur. Ainsi les possibilité sont réellement très grande tant le découplage de ces aspects facilite les combinaisons de comportements.



Maintenant que nous avons vu de quoi le pattern retourne, nous pouvons traiter de son implémentation et des limites que ces dernières imposeront. Initialement le pattern MVC est apparu avant l'explosion du Web dans le langage smalltalk (une merveille d’ailleur… dommage…)Il a ensuite été consolidé sur les interfaces graphiques des clients Lourd comme AWT ou swing. Ainsi, le pattern MVC s’est défini principalement sur le pattern observateur ou chaque élément de pattern MVC est observateur d’un élément mais aussi observé par un autre élément. Ainsi, la vue est observateur du modèle qui est observateur du ou des contrôleurs (ces derniers étant indirectement les observateurs de la vue en interceptant les événements). C’est ainsi grâce à l’observateur que le pattern MVC tire non seulement ce découplage mais aussi sa réactivité.

Ce modèle du MVC a longtemps été le standard de ce pattern et on le retrouve implémenté sous cette forme dans la plupart des frameworks graphiques standard. Cependant avec l'expansion du web, et la mise en communication de plus en plus forte des interfaces clientes (appelé front) avec les couches server (appelé back), il a fallu trouver un moyen efficace de rationaliser la conception des IHM et leur communication avec le métier. Ainsi de façon naturelle et évidente, et comme elle avait fait ses preuves, on s’est naturellement tourné vers le pattern MVC.

Ainsi, le pattern MVC est aujourd’hui encore largement répandu, souvent vendu comme une nouvelle approche alors que vieille comme le monde, l'élément neuf étant surtout la façon de le mettre en oeuvre. On peut évoquer par exemple les approches des Framework JEE (avec le duo Servlet/JSP), le framework Spring MVC ou Django.

Nous reviendrons sur ces implémentations dans des articles dédié mais ces frameworks usent d’une implémentation spécifique de MVC et de la même manière, d’autres variantes existes comme le modèle MVP (Modèle Vue Présentateur, qui va impliquer la mise en place du présentateur entre la vue et le modèle afin de préparer les données) ou encore le modèle MVVM, porté par microsoft pour son framework silverlight, qui cherche lui aussi à découpler la vue du modèle des données de représentation.

Bien sur ce pattern reste riche et est toujours le sujet de nombreuse recherche. Il est important de bien le connaître afin de savoir non seulement le reconnaître dans ses différentes déclinaison mais il faut aussi savoir l’utiliser dans des contextes où on ne l'attendait pas forcément car s’il est catalogué dans les pattern dédié au IHM, ses principes de bases sont le découplage (pour preuve de l’utilisation massive de l’observateur sur lequel il repose essentiellement) et son utilité dans d’autre couche logiciel ne fait aucun doute (surtout pour concevoir des logiciels réactifs…..)

Références:

[1] https://www.tutorialspoint.com/design_pattern/mvc_pattern.htm
[2] http://www.msdotnet.co.in/2015/05/difference-between-mvcmvp-and-mvvc.html#.Wsf2RZ9fihc
[3] http://www.interaction-design.org/references/conferences/interact_87_-_2nd_ifip_international_conference_on_human-computer_interaction.html#objectType_journalArticleInConfProceedings__objectID_7547