• Aucun résultat trouvé

Types de composants atomiques pour Java

Chapitre 7 Gestion du dynamisme dans les composants atomiques et composites

3. Types de composants atomiques pour Java

La section précédente a décrit les éléments constituant d’un type de composant atomique. Ce modèle peut être projeté sur plusieurs langages d’implémentation. Cette section décrit comment ce modèle peut être projeté sur Java et plus particulièrement montre les liens entre les éléments du modèle et une classe Java28. Ce modèle est le modèle idéal qui pourrait être proposé pour Java. Ensuite, les mécanismes d’injection et d’interception nécessaire afin de permettre la gestion de composant Java seront présentés. Cette section se terminera en donnant quelques exemples d’utilisation afin de montrer le modèle de développement « idéal » proposé et le confrontera aux contraintes exposées dans la première section de ce chapitre.

3.1. Type de composant atomique implémenté en Java

La projection du modèle des types de composant atomique sur Java spécialise chacun des concepts de ce modèle pour le langage de programmation Java. Par exemple, l’attribut implémentation du modèle devient le nom de la classe Java. La plupart des autres concepts interagissent avec les éléments de cette classe Java tels que les méthodes, les membres (appelés également champs ou attributs) et les interfaces implémentées.

Ainsi les méthodes d’activation et de désactivation sont des méthodes de la classe d’implémentation. Le contrôleur de cycle de vie devient un membre booléen de la classe. Lorsque celui-ci devient faux, l’instance est invalidée. Si celui-ci devient vrai, alors le développeur donne son accord pour la validation de l’instance.

Les propriétés de configuration sont attachées soit à des membres de la classe, soit à des méthodes, soit aux deux. Une propriété attachée à un membre est supervisée par le conteneur de l’instance qui peut donc gérer la sauvegarde et la restauration d’état. Une propriété attachée à une méthode permet au code d’être notifié quand la valeur de cette propriété est modifiée (lors du démarrage ou de la restauration de l’état). Le modèle de développement ressemble alors à celui proposé par les JavaBeans.

Les dépendances de service peuvent également être attachées à un membre de la classe d’implémentation. Ainsi le développeur accèdera au service en utilisant ce membre comme n’importe quel autre. Cependant, bien que cette méthode masque intégralement le dynamisme, il est parfois nécessaire au code de gérer une partie du dynamisme. iPOJO propose donc également la possibilité d’attacher une dépendance de service à deux méthodes de la classe d’implémentation. Ces méthodes seront appelées

27

Java ne supporte pas les destructeurs. Cependant, de nombreux objets possèdent des méthodes d’arrêt tel que close pour les flux, shutdown pour les exécuteurs ou dispose pour les objets Swing/AWT

28 Bien que cette projection soit spécifique à Java, elle est applicable aux autres langages de programmation par objet.

119 lorsqu’un fournisseur de service compatible arrive ou disparaît29. Ainsi, le code peut réagir au dynamisme des services requis.

Lorsqu’une dépendance de service est optionnelle, il est généralement indispensable de tester la disponibilité de ce service avant de l’utiliser. Cependant, tel qu’expliqué précédemment, ceci est à la fois contraignant n’apporte pas de réelle garantie. iPOJO implante le motif de conception Null Object [187] afin d’éviter ce test. Ainsi, le développeur n’aura pas à effectuer ce test et pourra directement utiliser le membre attaché à la dépendance de service. S’il n’existe pas de fournisseur disponible alors le développeur utilisera de manière transparente une fausse implémentation du service n’ayant aucun effet. Ce motif de conception est également étendu avec la notion d’implémentation par défaut. Le développeur peut alors choisir d’utiliser une implémentation du service qu’il fournit lorsqu’aucun fournisseur compatible n’est disponible.

Figure 73. Modèle des types de composants atomiques implémentés en Java

Afin de fournir des services, la classe d’implémentation du composant doit impérativement implémenter les interfaces de ces services afin de garantir que toutes les opérations spécifiées soient implémentées. Le code peut manipuler certaines propriétés de service en les attachant à un membre de la classe. Chaque modification de ces membres mettra à jour la publication de service.

3.2. Une machine d’injection et d’interception

La section précédente a présenté le modèle de type de composant atomique pour Java. Ce modèle repose sur la liaison entre la description du type de composant et la classe d’implémentation de ce composant. Afin de permettre ces liaisons, il est nécessaire d’exécuter les objets de cette classe d’implémentation au dessus d’une machine d’injection et d’interception qui permettra au conteneur de superviser l’exécution de ces objets (Figure 74). Cette machine permet d’attacher les objets au conteneur les supervisant. Plusieurs implémentations de cette machine sont envisageables. Cette section adresse seulement les fonctionnalités requise et ne décrit pas une implémentation particulière.

29 Dans le cas de dépendance de service simple, ces méthodes seront appelées seulement pour le fournisseur sélectionné.

Clement Escoffier 120 Figure 74. Principe d'une machine d'injection et d'interception

Afin de permettre cette supervision tout en proposant un modèle de développement le plus simple possible, cette machine doit fournir les capacités suivantes :

 Permettre l’appel de méthodes sur un objet par le conteneur

 Permettre la création de nouvelles instances de la classe d’implémentation  Permettre d’injecter une valeur dans un membre d’un objet

 Permettre la notification lorsque la valeur d’un membre est modifiée

 Intercepter les appels de méthodes ainsi que les sorties et erreurs potentielles

La première capacité sert principalement à appeler les méthodes spécifiées dans la description du type de composant lorsque cela est nécessaire (liaison, activation, désactivation …). La deuxième capacité permet au conteneur de superviser la création des instances (c'est-à-dire les objets) de la classe d’implémentation. Plus particulièrement, ceci permet de retarder au maximum et de gérer de manière optimale ces créations. Ces deux capacités sont communes dans les machines d’injection et reposent sur les mécanismes de réflexion offerts par Java.

L’injection de valeur dans un membre est utile pour injecter un objet de service (objet fourni par le fournisseur de service servant véritablement ce service) ou pour restaurer un état. La notification, lorsqu’ un membre est assigné, est nécessaire à la supervision des propriétés de service attachées à ce membre, au suivi de l’état ainsi qu’au contrôleur de cycle de vie. Enfin, l’interception des appels de méthodes ainsi que des sorties et des erreurs est crucial pour garantir l’état de l’objet et gérer la quiescence. Ces capacités seraient implantables en utilisant les propriétés de réflexion de Java telles que les Proxies[116]. Cependant, une telle implantation poserait des problèmes de performance et serait limitée.

Afin de lever ces limitations, la machine d’injection et d’interception d’iPOJO procède en deux étapes :  Tout d’abord, la classe d’implémentation est manipulée lors de l’empaquetage du composant

(en tant que bundle OSGi™). La classe résultante permet l’injection de valeur dans des champs ainsi que l’interception de méthode et la notification lorsqu’un membre est assigné.

 Ensuite cette classe est utilisée à l’exécution à la place de la classe d’origine.

La combinaison de la réflexion (pour les deux premières capacités) et de la manipulation implantent toutes les capacités requises de la machine d’injection et d’interception. La manipulation modifie la classe d’implémentation afin d’encapsuler toutes les accès aux membres et toutes les méthodes par des appels de méthodes délégués au conteneur qui peut alors superviser intégralement ce qui se passe dans les objets (Figure 75 et Figure 76). De plus, lors de la manipulation, certaines informations sur la classe sont collectées. Ces informations sont alors utilisées à l’exécution afin de vérifier la cohérence du type de composant et de la

121 classe d’implémentation (méthodes et membres absents), ainsi que pour vérifier que le type de composant est bien une implémentation valide des services fournis.

Figure 75. Séquence d'appels sans injection ni interception

Figure 76. Séquence d'appels avec injections et interceptions

3.3. Démonstration du modèle de développement pour Java

Cette section a pour but de montrer le modèle de développement proposé et de le confronter avec les requis décrits précédemment dans ce chapitre. Plusieurs exemples sont présentés afin d’illustrer les différentes capacités du modèle.

3.3.1.Dépendances de service

Une classe Java contient à la fois des membres (parfois appelés champs) et des méthodes. Afin d’utiliser un service, iPOJO propose plusieurs alternatives. Tout d’abord, ce service peut être injecté dans un membre de la classe d’implémentation du composant. Dans ce cas, le membre de cette classe reçoit un objet de service. Cette solution a l’avantage de proposer un modèle de développement très simple garantissant la gestion du dynamisme (Figure 77). En effet, la valeur assignée dans le membre suit le dynamisme des arrivées et des départs des fournisseurs. Cette méthode permet également d’éviter les problèmes de synchronisation qui peuvent être gérés par iPOJO pour éviter par exemple qu’un service disparaisse avant la fin de la méthode ou que le code utilise différents fournisseurs au sein de la même méthode (ou du même flot d’appels). La description du composant n’a pas à redéfinir la spécification de service requise. En effet, celle-ci est collectée lors de la manipulation.

Clement Escoffier 122 Figure 77. Dépendance de service simple

Lorsqu’une dépendance de service est optionnelle, afin d’éviter les tests superflus testant la disponibilité du service, iPOJO injecte un objet factice. Cette méthode simplifie le modèle de développement lorsque des dépendances sont optionnelles (Figure 78).

Figure 78. Dépendance de service simple et optionnelle

Une seconde alternative permet aux développeurs d’être notifiés d’une partie du dynamisme. L’implémentation contient alors des méthodes de liaisons qui sont appelées lorsqu’un fournisseur apparaît ou disparaît. Bien que ceci offre plus de liberté aux développeurs, ce modèle de développement peut entrainer des erreurs. En effet, ces méthodes sont appelées de manières concurrentes par rapport à la logique-métier du composant. Le développeur doit alors gérer correctement ce problème (Figure 79). Cependant, là encore, le développeur n’a pas à déclarer la spécification de service requise. Celle-ci peut être collectée lors de la manipulation.

123 3.3.2.Publication et Fourniture de service

La publication de service doit également être masquée pour le développeur. iPOJO gère cette publication automatiquement (ainsi que le retrait lorsque l’instance devient invalide). Par défaut, iPOJO publie toutes les interfaces implémentées par la classe d’implémentation du composant. Ainsi le développeur n’a pas à gérer la publication (Figure 80). De plus, les instances de cette classe d’implémentation ne seront créées que lorsqu’un consommateur requiert un des services fournis.

Figure 80. Exemple de publication et de fourniture de service

Un code peut également manipuler des propriétés de service. Ainsi dès lors qu’une propriété est affectée d’une nouvelle valeur, iPOJO met à jour la publication de service afin de refléter ce changement (Figure 81).

Figure 81. Exemple de modification d'une propriété de service 3.3.3. Configuration et gestion d’état

Bien que le dynamisme issu des services soit un élément très important, il est également nécessaire de supporter la gestion de l’état lors de l’évolution d’un composant. Ainsi, le développeur peut déclarer des propriétés qui seront supervisées par iPOJO. Lors d’une invalidation ou d’un arrêt, celles-ci seront persistées puis réinjectées lors du redémarrage de l’instance ou de la revalidation.

Ces propriétés peuvent être injectées soit via des méthodes, soit dans des membres de la classe. Lorsque la propriété de configuration est injectée via une méthode, les changements sur cette propriété ne sont pas supervisés. Au prochain redémarrage la même valeur sera injectée (sauf si cette valeur a été modifiée dans le gestionnaire de configuration). Lors d’une injection dans un membre, iPOJO peut superviser les changements et donc stocker la dernière valeur afin de la réinjecter après une reconfiguration.

Par défaut, iPOJO utilise le gestionnaire de configuration spécifié dans OSGi™ afin de sauvegarder et restaurer les valeurs des propriétés.

Clement Escoffier 124

4. Principes et concepts des types de composants composites