Livre Icar2006
Livre Icar2006
Livre Icar2006
1
Université Joseph Fourier, Grenoble
2
France Telecom R&D
3
Université Pierre et Marie Curie et INRIA
4
Queensland University of Technology, Brisbane (Australie)
5
Université de Nice - Sophia Antipolis
6
École Nationale Supérieure des Télécommunications de Bretagne
7
INRIA
8
CNRS et projet Sardes (LIG-INRIA)
Table des matières
Préface ix
1 Introduction à l’intergiciel 1
1.1 Pourquoi l’intergiciel ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Quelques classes d’intergiciel . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Un exemple simple d’intergiciel : RPC . . . . . . . . . . . . . . . . . . . . . 8
1.3.1 Motivations et besoins . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.2 Principes de réalisation . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.3 Développement d’applications avec le RPC . . . . . . . . . . . . . . 12
1.3.4 Résumé et conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.4 Problèmes et défis de la conception de l’intergiciel . . . . . . . . . . . . . . 15
1.4.1 Problèmes de conception . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.4.2 Quelques principes d’architecture . . . . . . . . . . . . . . . . . . . . 16
1.4.3 Défis de l’intergiciel . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.4.4 Plan de l’ouvrage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5 Note historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8 Conclusion 225
Annexes 227
Glossaire 371
Bibliographie 373
viii TABLE DES MATIÈRES
Préface
Le domaine de l’intergiciel (middleware), apparu dans les années 1990, a pris une place
centrale dans le développement des applications informatiques réparties. L’intergiciel joue
aujourd’hui, pour celles-ci, un rôle analogue à celui d’un système d’exploitation pour les
applications centralisées : il dissimule la complexité de l’infrastructure sous-jacente, il
présente une interface commode aux développeurs d’applications, il fournit un ensemble
de services communs.
Les acteurs de l’intergiciel sont nombreux et divers : les organismes de normalisation,
les industriels du logiciel et des services, les utilisateurs d’applications. Les consortiums
et organisations diverses qui développent et promeuvent l’usage du logiciel libre tiennent
une place importante dans le monde de l’intergiciel. Enfin, ce domaine est l’objet d’une
recherche active, dont les résultats sont rapidement intégrés. Une conférence scientifique
annuelle ACM-IFIP-Usenix, Middleware, est consacrée à l’intergiciel, et de nombreux ate-
liers spécialisés sont fréquemment créés sur des sujets d’actualité.
Le domaine de l’intergiciel est en évolution permanente. Pour répondre à des besoins
de plus en plus exigeants, les produits doivent s’adapter et s’améliorer. De nouveaux do-
maines s’ouvrent, comme l’informatique ubiquitaire, les systèmes embarqués, les systèmes
mobiles. Le cycle de cette évolution est rapide : chaque année apporte une transformation
significative du paysage des normes et des produits de l’intergiciel.
Malgré la rapidité de ces changements, il est possible de dégager quelques principes ar-
chitecturaux qui guident la conception et la construction de l’intergiciel et des applications
qui l’utilisent. Ces principes sont eux-mêmes développés et enrichis grâce aux résultats de
la recherche et à l’expérience acquise, mais présentent une certaine stabilité.
Cet ouvrage vise à présenter une vue d’ensemble des systèmes intergiciels, sous la
forme d’études de cas des principaux standards du domaine et de leur mise en œuvre
pour la construction d’application réparties. Cette présentation s’attache aux aspects ar-
chitecturaux : on n’y trouvera pas les détails de la dernière version de tel ou tel produit.
Pour faciliter la compréhension des aspects communs, chaque chapitre consacré à un stan-
dard ou à une plate-forme met en évidence le traitement spécifique de chacun de ces as-
pects : architecture d’ensemble du système, composition, programmation, conditionnement
et déploiement des applications. En outre, un chapitre initial présente quelques principes
généraux de construction applicables à l’intergiciel. Le domaine de la composition d’ap-
plications, qui tient une place importante, est présenté au travers de l’étude d’un modèle
innovant issu de la recherche.
Pour illustrer des aspects techniques plus spécialisés, une part substantielle de l’ou-
vrage est constituée d’annexes. Certaines illustrent la mise en œuvre concrète des systèmes
x PRÉFACE
présentés. D’autres donnent un aperçu de systèmes ou d’applications qui n’ont pas trouvé
leur place dans le corps de l’ouvrage.
L’ouvrage ne couvre pas toutes les phases du cycle de vie des applications réparties.
L’accent est mis sur les méthodes et les outils pour la construction de ces applications.
Les phases antérieure (conception) et postérieure (administration) ne sont pas abordées.
Chacun de ces aspects justifierait une étude de même ampleur que celle ici présentée.
L’ouvrage s’adresse à toute personne intéressée par l’intergiciel, aux concepteurs,
développeurs et utilisateurs d’applications réparties, ainsi qu’aux étudiants en informa-
tique (niveau Master ou école d’ingénieurs) et aux chercheurs désirant aborder le domaine
de l’intergiciel. Nous supposons que le lecteur a des connaissances générales de base dans
le domaine des applications informatiques, des systèmes d’exploitation et des réseaux. La
pratique du langage Java, sans être requise, facilite la lecture des exemples.
Ce livre est issu d’une série d’écoles d’été organisées sur le thème de l’intergiciel et de la
construction d’applications réparties (Saint-Malo 1996, Autrans 1998, 1999, 2003 et 2006).
La forme et le contenu de ces écoles se sont naturellement adaptés pour suivre l’évolution
du domaine. Depuis 2003, l’école a pris le nom d’ICAR (Intergiciel et Construction d’Ap-
plications Réparties). Ce livre reprend largement le contenu de l’école ICAR 2006. Les
annexes sont notamment inspirées d’ateliers organisés au cours de cette école.
Nous tenons à remercier les organismes qui nous ont soutenus pour l’organisation
d’ICAR 2006 : l’IMAG (Informatique et Mathématiques Appliquées de Grenoble), l’IN-
RIA (Institut National de Recherche en Informatique et Automatique, unité de recherche
de Rhône-Alpes), et ObjectWeb [ObjectWeb 1999], consortium développant des systèmes
intergiciels en logiciel libre. Nous remercions également les organismes d’appartenance
des organisateurs et intervenants d’ICAR 2006 : France Telecom R&D, INRIA, Institut
National Polytechnique de Grenoble, Queensland University of Technology (Australie),
Scalagent Distributed Technologies. Université Joseph Fourier, Université de Nice–Sophia
Antipolis.
Sans avoir directement participé à la rédaction, Alan Schmitt et Christophe Taton sont
intervenus dans ICAR 2006. Danièle Herzog a assuré avec efficacité le soutien logistique
de cette école.
L’ouvrage a bénéficié d’interactions avec de nombreux collègues dont la liste serait
trop longue pour n’oublier personne. Mentionnons néanmoins, parmi les intervenants et
les organisateurs d’écoles précédentes, Daniel Hagimont (IRIT, Toulouse) et Philippe Merle
(INRIA et LIFL, Lille).
Chapitre 1
Introduction à l’intergiciel
Ce chapitre présente les fonctions de l’intergiciel, les besoins auxquels elles répondent,
et les principales classes d’intergiciel. L’analyse d’un exemple simple, celui de l’appel de
procédure à distance, permet d’introduire les notions de base relatives aux systèmes in-
tergiciels et les problèmes que pose leur conception. Le chapitre se termine par une note
historique résumant les grandes étapes de l’évolution de l’intergiciel.
application application
patrimoniale patrimoniale
interface
propriétaire composant
enveloppe enveloppe
(wrapper) (wrapper)
nouveau
interface interface
standard standard
“bus d’échange” inter-applications
composant enveloppe
(wrapper)
nouveau
application
patrimoniale
Une application patrimoniale ainsi « enveloppée » peut maintenant être intégrée avec
d’autres applications du même type et avec des composants nouveaux, en utilisant les
protocoles normalisés du courtier. Des exemples de courtiers sont CORBA, les files de
messages, les systèmes à publication et abonnement (en anglais publish-subscribe). On en
trouvera des exemples dans ce livre.
administrateur administrateur
routeur
équipements en réseau
particulier (par exemple télécommunications, finance, avionique, etc.) peut utiliser une
bibliothèque de composants développés pour ce domaine.
interface des
composants d’application services spécifiques
services
spécifiques
clients
base de base de
données données
cet effet, le mandataire utilise ses propres ressources de stockage et de traitement. Les
mandataires peuvent être hébergés sur des équipements dédiés (Figure 1.4), ou sur des
serveurs communs.
Clients
Proxy
l’Internet Serveurs
Figure 1.4 – Adaptation des communications aux ressources des clients par des mandataires
Des exemples d’adaptation sont la compression des données pour réagir à des variations
de bande passante du réseau, la réduction de la qualité des images pour prendre en compte
des capacités réduites d’affichage, le passage de la couleur aux niveaux de gris. Un exemple
de mise en œuvre de l’adaptation par mandataires est décrit dans [Fox et al. 1998].
Les quatre études de cas qui précèdent ont une caractéristique commune : dans chacun
des systèmes présentés, des applications utilisent des logiciels de niveau intermédiaire,
installés au-dessus des systèmes d’exploitation et des protocoles de communication, qui
réalisent les fonctions suivantes :
1. cacher la répartition, c’est-à-dire le fait qu’une application est constituée de parties
interconnectées s’exécutant à des emplacements géographiquement répartis ;
2. cacher l’hétérogeneité des composants matériels, des systèmes d’exploitation et des
protocoles de communication utilisés par les différentes parties d’une application ;
3. fournir des interfaces uniformes, normalisées, et de haut niveau aux équipes de
développement et d’intégration, pour faciliter la construction, la réutilisation, le por-
tage et l’interopérabilité des applications ;
4. fournir un ensemble de services communs réalisant des fonctions d’intérêt général,
pour éviter la duplication des efforts et faciliter la coopération entre applications.
Cette couche intermédiaire de logiciel, schématisée sur la Figure 1.5, est désignée par
le terme générique d’intergiciel (en anglais middleware). Un intergiciel peut être à usage
général ou dédié à une classe particulière d’applications.
L’utilisation de l’intergiciel présente plusieurs avantages dont la plupart résultent de
la capacité d’abstraction qu’il procure : cacher les détails des mécanismes de bas niveau,
assurer l’indépendance vis-à-vis des langages et des plates-formes, permettre de réutiliser
l’expérience et parfois le code, faciliter l’évolution des applications. En conséquence, on
6 CHAPITRE 1. INTRODUCTION À L’INTERGICIEL
Intergiciel
Système de communication
peut espérer réduire le coût et la durée de développement des applications (l’effort étant
concentré sur les problèmes spécifiques et non sur l’intendance), et améliorer leur porta-
bilité et leur interopérabilité.
Un inconvénient potentiel est la perte de performances liée à la traversée de couches
supplémentaires de logiciel. L’utilisation de techniques intergicielles implique par ailleurs
de prévoir la formation des équipes de développement.
de transmission d’un message ; si cette borne ne peut être établie, le système est dit
asynchrone 3 .
Ces caractéristiques se combinent comme suit.
– Fixe, imprévisible. C’est le cas le plus courant, aussi bien pour les réseaux locaux
que pour les réseaux à grande distance (comme l’Internet). Bien que l’on puisse
souvent estimer une moyenne pour la durée de transmission, il n’est pas possible de
lui garantir une borne supérieure.
– Fixe, prévisible. Cette combinaison s’applique aux environnements spécialement
développés pour des applications ayant des contraintes particulières, comme les ap-
plications critiques en temps réel. Le protocole de communication garantit alors une
borne supérieure pour le temps de transfert, en utilisant la réservation préalable de
ressources.
– Variable, imprévisible. C’est le cas de systèmes de communication qui comprennent
des appareils mobiles (dits aussi nomades) tels que les téléphones mobiles ou les as-
sistants personnels. Ces appareils utilisent la communication sans fil, qui est sujette à
des variations imprévisibles de performances. Les environnements dits ubiquitaires,
ou omniprésents [Weiser 1993], permettant la connexion et la déconnexion dyna-
mique de dispositifs très variés, appartiennent à cette catégorie.
Avec les techniques actuelles de communication, la classe (variable, prévisible) est vide.
L’imprévisibilité des performances du système de communication rend difficile la tâche
de garantir aux applications des niveaux de qualité spécifiés L’adaptabilité, c’est-à-dire
la capacité à réagir à des variations des performances des communications, est la qualité
principale requise de l’intergiciel dans de telles situations.
processus p
site A processus p
site A site B
P(x,y) P(x,y) P(x,y)
réseau
Elle peut également être portée aisément, sans modifications, d’un système centralisé vers
un réseau.
Néanmoins, le maintien de la sémantique est une tâche délicate, pour deux raisons
principales :
– les modes de défaillance sont différents dans les cas centralisé et réparti ; dans
cette dernière situation, le site client, le site serveur et le réseau sont sujets à des
défaillances indépendantes ;
– même en l’absence de pannes, la sémantique du passage des paramètres est différente
(par exemple, on ne peut pas passer un pointeur en paramètre dans un environnement
réparti car le processus appelant et la procédure appelée s’exécutent dans des espaces
d’adressage différents).
Le problème du passage des paramètres est généralement résolu en utilisant le passage
par valeur pour les types simples. Cette solution s’étend aux paramètres composés de
taille fixe (tableaux ou structures). Dans le cas général, l’appel par référence n’est pas
possible ; on peut néanmoins construire des routines spécifiques d’emballage et de déballage
des paramètres pour des structures à base de pointeurs, graphes ou listes. Les aspects
techniques de la transmission des paramètres sont examinés en 1.3.2.
La spécification du comportement du RPC en présence de défaillances soulève deux
difficultés, lorsque le système de communication utilisé est asynchrone. D’une part, on ne
connaı̂t en général pas de borne supérieure pour le temps de transmission d’un message ;
un mécanisme de détection de perte de message à base de délais de garde risque donc des
fausses alarmes. D’autre part, il est difficile de différencier l’effet de la perte d’un message
de celui de la panne d’un site distant. En conséquence, une action de reprise peut conduire
à une décision erronée, comme de réexécuter une procédure déjà exécutée. Les aspects
relatifs à la tolérance aux fautes sont examinés en 1.3.2.
client serveur
interface d’appel interface d’appel
de procédure de procédure
talon client service de noms talon serveur
interface de interface de
communication communication
sous-système de sous-système de
communication communication
réseau
(a) exécution directe (b) 1thread par appel (c) pool de threads
locales de représentation des données sur les sites client et serveur, comme l’ordre des oc-
tets. La conversion des données depuis leur représentation locale vers une forme standard
transmissible est appelée emballage (en anglais marshalling) ; la conversion en sens inverse
est appelée déballage (en anglais unmarshalling).
Un emballeur (marshaller ) est un jeu de routines, une par type de données (par exemple
writeInt, writeString, etc.), qui écrivent des données du type spécifié dans un flot de
données séquentiel. Un déballeur (unmarshaller ) remplit la fonction inverse et fournit des
routines (par exemple readInt, readString, etc.) qui extraient des données d’un type
spécifié à partir d’un flot séquentiel. Ces routines sont appelées par les souches quand
une conversion est nécessaire. L’interface et l’organisation des emballeurs et déballeurs
dépend du langage utilisé, qui spécifie les types de données, et du format choisi pour la
représentation standard.
Réaction aux défaillances. Comme indiqué plus haut, les défaillances (ou pannes)
peuvent survenir sur le site client, sur le site serveur, ou sur le réseau. La prise en
compte des défaillances nécessite de formuler des hypothèses de défaillances, de détecter
les défaillances, et enfin de réagir à cette détection.
Les hypothèses habituelles sont celle de la panne franche (fail-stop) pour les sites (ou
bien un site fonctionne correctement, ou bien il est arrêté), et celle de la perte de message
pour le réseau (ou bien un message arrive sans erreur, ou bien il n’arrive pas, ce qui suppose
que les erreurs de transmission sont détectées et éventuellement corrigées à un niveau plus
bas du système de communication). Les mécanismes de détection de pannes reposent sur
des délais de garde. À l’envoi d’un message, une horloge de garde est armée, avec une
valeur estimée de la borne supérieure du délai de réception de la réponse. Le dépassement
du délai de garde déclenche une action de reprise.
Les horloges de garde sont placées côté client, à l’envoi du message d’appel, et côté
serveur, à l’envoi du message de réponse. Dans les deux cas, l’action de reprise consiste
à renvoyer le message. Le problème vient du fait qu’il est difficile d’estimer les délais de
garde : un message d’appel peut être renvoyé alors que l’appel a déjà eu lieu, et la procédure
peut ainsi être exécutée plusieurs fois.
Le resultat net est qu’il est généralement impossible de garantir la sémantique d’appel
12 CHAPITRE 1. INTRODUCTION À L’INTERGICIEL
dite « exactement une fois » (après réparation de toutes les pannes, l’appel a été exécuté
une fois et une seule). La plupart des systèmes assurent la sémantique « au plus une fois »
(l’appel a été exécuté une fois ou pas du tout, ce qui exclut les cas d’exécution partielle
ou multiple). La sémantique « au moins une fois », qui autorise les exécutions multiples,
est acceptable si l’appel est idempotent, c’est-à-dire si l’effet de deux appels successifs est
identique à celui d’un seul appel.
call P(x, y)
m=marshall(P, x, y)
creates
network transmission or selects
thread [P, x, y]=
envoyer(m)
wait unmarshall(m)
m=receive()
call P(x, y)
unblock return r
envoyer(q)
q=marshall(r)
network transmission
q=receive()
thread exits or
returns to pool
return r
r=unmarshall(q)
: call P(x, y)
: lookup(P)
: return(IPaddress, port)
La liaison inverse (du serveur vers le client) est établie en incluant l’adresse IP et le
numéro de port du client dans le message d’appel.
Génération des souches. Comme nous l’avons vu en 1.3.2, les souches remplissent des
fonctions bien définies, dont une partie est générique (comme la gestion des processus) et
une autre est propre à chaque appel (comme l’emballage et le déballage des paramètres).
Ce schéma permet la génération automatique des souches.
Les paramètres d’un appel nécessaires pour la génération de souches sont spécifiés dans
une notation appelée langage de définition d’interface (en anglais, Interface Definition
Language, ou IDL). Une description d’interface écrite en IDL contient toute l’information
qui définit l’interface pour un appel, et fonctionne comme un contrat entre l’appelant et
l’appelé. Pour chaque paramètre, la description spécifie son type et son mode de passage
(valeur, copie-restauration, etc.). Des informations supplémentaires telles que le numéro
de version et le mode d’activation du serveur peuvent être spécifiées.
Plusieurs IDL ont été définis (par exemple Sun XDR, OSF DCE). Le générateur de
souches utilise un format commun de représentation de données défini par l’IDL ; il insère
les routines de conversion fournies par les emballeurs et déballeurs correspondants. Le
cycle complet de développement d’une application utilisant le RPC est schématisé sur la
figure 1.11 (la notation est celle de Sun RPC).
5
Si on connaı̂t le site du serveur, on peut utiliser sur ce site un service local d’annuaire (portmapper )
accessible sur un port prédéfini (n◦ 111).
14 CHAPITRE 1. INTRODUCTION À L’INTERGICIEL
application client
conversions
proc_xdr.c
code bibliothèques code
proc.x rpcgen exécutable
source
définitions
proc.h
définition
d’interface talon serveur
proc_svc.c
Engendré
(appel synchrone, communication asynchrone par messages, coordination via des objets
partagés).
L’architecture logicielle définit l’organisation d’un système réalisé comme assemblage
de parties. Les notions liées à la composition et aux composants logiciels sont maintenant
un élément central de la conception des systèmes intergiciels, aussi bien pour leur propre
structure que pour celle des applications qui les utilisent.
La gestion de données soulève le problème de la persistance (conservation à long terme
et procédures d’accès) et des transactions (maintien de la cohérence pour l’accès concurrent
aux données en présence de défaillances éventuelles).
La qualité de service comprend diverses propriétés d’une application qui ne sont pas
explicitement spécifiées dans ses interfaces fonctionnelles, mais qui sont très importantes
pour ses utilisateurs. Des exemples de ces propriétés sont la fiabilité et la disponibilité, les
performances (spécialement pour les applications en temps réel), et la sécurité.
L’administration est une étape du cycle de vie des applications qui prend une
importance croissante. Elle comprend des fonctions telles que la configuration et le
déploiement, la surveillance (monitoring), la réaction aux événements indésirables (sur-
charge, défaillances) et la reconfiguration. La complexité croissante des tâches d’adminis-
tration conduit à envisager de les automatiser (autonomic computing).
Les différents aspects ci-dessus apparaissent dans les études de cas qui constituent la
suite de cet ouvrage.
Évolution et adaptation
Les systèmes logiciels doivent s’accommoder du changement. En effet, les spécifications
évoluent à mesure de la prise en compte des nouveaux besoins des utilisateurs, et la diversité
des systèmes et organes de communication entraı̂ne des conditions variables d’exécution.
S’y ajoutent des événements imprévisibles comme les variations importantes de la charge et
les divers types de défaillances. La conception des applications comme celle de l’intergiciel
doit tenir compte de ces conditions changeantes : l’évolution des programmes répond à celle
des besoins, leur adaptation dynamique répond aux variations des conditions d’exécution.
Pour faciliter l’évolution d’un système, sa structure interne doit être rendue accessible.
Il y a une contradiction apparente entre cette exigence et le principe d’encapsulation, qui
vise à cacher les détails de la réalisation.
Ce problème peut être abordé par plusieurs voies. Des techniques pragmatiques, re-
posant souvent sur l’interception (voir chapitre 2, section 2.3.5), sont largement utilisées
dans les intergiciels commerciaux. Une approche plus systématique utilise la réflexivité. Un
système est dit réflexif [Smith 1982, Maes 1987] quand il fournit une représentation de lui-
même permettant de l’observer et de l’adapter, c’est-à-dire de modifier son comportement.
18 CHAPITRE 1. INTRODUCTION À L’INTERGICIEL
Pour être cohérente, cette représentation doit être causalement connectée au système :
toute modification apportée au système doit se traduire par une modification homologue
de sa représentation, et vice versa. Les méta-objets fournissent une telle représentation
explicite des mécanismes de base d’un système, ainsi que des protocoles pour examiner
et modifier cette représentation. La programmation par aspects, une technique destinée
à assurer la séparation des préoccupations, est également utile pour réaliser l’évolution
dynamique d’un système. Ces techniques et leur utilisation dans les systèmes intergiciels
sont examinées dans le chapitre 2.
Les concepteurs des futurs systèmes intergiciels sont confrontés à plusieurs défis.
– Performances. Les systèmes intergiciels reposent sur des mécanismes d’interception
et d’indirection, qui induisent des pertes de performances. L’adaptabilité introduit
des indirections supplémentaires, qui aggravent encore la situation. Ce défaut peut
être compensé par diverses méthodes d’optimisation, qui visent à éliminer les coûts
supplémentaires inutiles en utilisant l’injection 6 directe du code de l’intergiciel dans
celui des applications. La souplesse d’utilisation doit être préservée, en permettant
d’annuler, en cas de besoin, l’effet de ces optimisations.
– Passage à grande échelle. À mesure que les applications deviennent de plus en
plus étroitement interconnectées et interdépendantes, le nombre d’objets, d’usagers
et d’appareils divers composant ces applications tend à augmenter. Cela pose le
problème de la capacité de croissance (scalability) pour la communication et pour les
algorithmes de gestion d’objets, et accroı̂t la complexité de l’administration (ainsi, on
peut s’interroger sur la possibilité d’observer l’état d’un très grand système réparti,
et sur le sens même que peut avoir une telle notion). Le passage à grande échelle
rend également plus complexe la préservation des différentes formes de qualité de
service.
– Ubiquité. L’informatique ubiquitaire (ou omniprésente) est une vision du futur
proche, dans laquelle un nombre croissant d’appareils (capteurs, processeurs, ac-
tionneurs) inclus dans divers objets physiques participent à un réseau d’information
global. La mobilité et la reconfiguration dynamique seront des traits dominants de
ces systèmes, imposant une adaptation permanente des applications. Les principes
d’architecture applicables aux systèmes d’informatique ubiquitaire restent encore
largement à élaborer.
– Administration. L’administration de grandes applications hétérogènes, largement
réparties et en évolution permanente, soulève de nombreuses questions telles que
l’observation cohérente, la sécurité, l’équilibre entre autonomie et interdépendance
pour les différents sous-systèmes, la définition et la réalisation des politiques d’allo-
cation de ressources, etc.
6
Cette technique s’apparente à l’optimisation des appels de procédure par insertion de leur code à
l’endroit de l’appel (inlining).
1.5. NOTE HISTORIQUE 19
et Eden [Almes et al. 1985], suivis par Amoeba [Mullender et al. 1990], ANSA-
ware [ANSA ], Arjuna [Parrington et al. 1995], Argus [Liskov 1988], Chorus/COOL
[Lea et al. 1993], Clouds [Dasgupta et al. 1989], Comandos [Cahill et al. 1994], Emerald
[Jul et al. 1988], Gothic [Banâtre and Banâtre 1991], Guide [Balter et al. 1991], Network
Objects [Birrell et al. 1995], SOS [Shapiro et al. 1989], et Spring [Mitchell et al. 1994].
L’Open Software Foundation (OSF), qui deviendra plus tard l’Open Group
[Open Group ], est créée en 1988 dans le but d’unifier les diverses versions du système d’ex-
ploitation Unix. Cet objectif ne fut jamais atteint, mais l’OSF devait spécifier une plate-
forme intergicielle, le Distributed Computing Environment (DCE) [Lendenmann 1996],
qui comportait notamment un service d’appel de procédure à distance, un système réparti
de gestion de fichiers, un serveur de temps, et un service de sécurité.
L’Object Management Group (OMG) [OMG ] est créé en 1989 pour définir des normes
pour l’intergiciel à objets répartis. Sa première proposition (1991) fut la spécification de
CORBA 1.0 (la version courante, en 2005, est CORBA 3). Ses propositions ultérieures
sont des normes pour la modélisation (UML, MOF) et les composants (CCM). L’Object
Database Management Group (ODMG) [ODMG ] definit des normes pour les bases de
données à objets, qui visent à unifier la programmation par objets et la gestion de données
persistantes.
Le modèle de référence de l’Open Distributed Processing (RM-ODP) [ODP 1995a],
[ODP 1995b] a été conjointement défini par deux organismes de normalisation, l’ISO et
l’ITU-T. Il propose un ensemble de concepts définissant un cadre générique pour le calcul
réparti ouvert, plutôt que des normes spécifiques.
La définition du langage Java par Sun Microsystems en 1995 ouvre la voie à plusieurs
intergiciels, dont Java Remote Method Invocation (RMI) [Wollrath et al. 1996] et les En-
terprise JavaBeans (EJB) [Monson-Haefel 2002]. Ces systèmes, et d’autres, sont intégrés
dans une plate-forme commune, J2EE [J2EE 2005].
Microsoft développe à la même époque le Distributed Component Object Model
(DCOM) [Grimes 1997], un intergiciel définissant des objets répartis composables, dont
une version améliorée est COM+ [Platt 1999]. Son offre courante est .NET [.NET ], plate-
forme intergicielle pour la construction d’applications réparties et de services pour le Web.
La première conférence scientifique entièrement consacrée à l’intergiciel a lieu en 1998
[Middleware 1998]. Parmi les thèmes actuels de la recherche sur l’intergiciel, on peut noter
les techniques d’adaptation (réflexivité, aspects), l’administration et notamment les tra-
vaux sur les systèmes autonomes (pouvant réagir à des surcharges ou à des défaillances),
et l’intergiciel pour appareils mobiles et environnements ubiquitaires.
Intergiciel et Construction d’Applications Réparties
c
2006 S. Krakowiak (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Chapitre 2
Ce chapitre présente les grands principes de conception des systèmes intergiciels, ainsi
que quelques patrons élémentaires récurrents dans toutes les architectures intergicielles.
Divers patrons plus élaborés peuvent être construits en étendant et en combinant ces
constructions de base. Le chapitre débute par une présentation des principes architecturaux
et des éléments constitutifs des systèmes intergiciels, notamment les objets répartis et les
organisations multi-couches. Il continue par une discussion des patrons de base relatifs
aux objets répartis. Le chapitre se termine par une présentation des patrons liés à la
séparation des préoccupations, qui comprend une discussion des techniques de réalisation
pour l’intergiciel réflexif.
1
Dans ce chapitre, nous utilisons le terme de composant dans un sens non-technique, pour désigner une
unité de décomposition d’un système.
22 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
son environnement2 .
La fourniture de services peut être considérée à différents niveaux d’abstraction. Un
service fourni est généralement matérialisé par un ensemble d’interfaces, dont chacune
représente un aspect du service. L’utilisation de ces interfaces repose sur des patrons
élémentaires d’interaction entre les composants du système. Dans la section 2.1.1, nous
passons brièvement en revue ces patrons d’interaction. Les interfaces sont discutées dans
la section 2.1.2, et les contrats sont l’objet de la section 2.1.3.
messaging
A B A B A B
system
receive
event request
send m1 block
handler wait wait
deliver m1 reply
send m2
receive
deliver m2
A B A B
service request
for A
callback 1
callback
callback 2
Les interactions ci-dessus sont discrètes et n’impliquent pas explicitement une notion
de temps autre que l’ordre des événements. Les échanges continus nécessitent une forme
24 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
de synchronisation en temps réel. Par exemple les données multimédia sont échangées via
des flots de données, qui permettent la transmission continue d’une séquence de données
soumise à des contraintes temporelles.
2.1.2 Interfaces
Un service élémentaire fourni par un composant logiciel est défini par une interface, qui
est une description concrète de l’interaction entre le demandeur et le fournisseur du service.
Un service complexe peut être défini par plusieurs interfaces, dont chacune représente un
aspect particulier du service. Il y a en fait deux vues complémentaires d’une interface.
– la vue d’usage : une interface définit les opérations et structures de données utilisées
pour la fourniture d’un service ;
– la vue contractuelle : une interface définit un contrat entre le demandeur et le four-
nisseur d’un service.
La définition effective d’une interface requiert donc une représentation concrète des
deux vues, par exemple un langage de programmation pour la vue d’usage et un langage
de spécification pour la vue contractuelle.
Rappelons qu’aussi bien la vue d’usage que la vue contractuelle comportent deux par-
tenaires3 . En conséquence, la fourniture d’un service implique en réalité deux interfaces :
l’interface présentée par le composant qui fournit un service, et l’interface attendue par le
client du service. L’interface fournie (ou serveur) doit être « conforme » à l’interface re-
quise (ou client), c’est-à-dire compatible avec elle ; nous revenons plus loin sur la définition
de la conformité.
contract
service service
requester provider
client server
interface interface
conformance
opérations. Si le service est composé de plusieurs interfaces, il peut aussi exister des
contraintes entre l’exécution d’opérations appartenant à différentes interfaces.
– Le niveau 4 s’applique aux propriétés extra-fonctionnelles, c’est-à-dire à celles qui
n’apparaissent pas explicitement dans l’interface. Le terme de « Qualité de Service »
(QoS) est aussi utilisé pour ces propriétés, qui comprennent performances, securité,
disponibilité, etc.
Notons encore que le contrat s’applique dans les deux sens, à tous les niveaux : il engage
donc le demandeur aussi bien que le fournisseur. Par exemple, les paramètres passés lors
d’un appel de fonction sont contraints par leur type ; si l’interface comporte un rappel, la
procédure qui réalise l’action correspondante côté client doit être fournie (cela revient à
spécifier une procédure comme paramètre).
L’essence d’un contrat d’interface est exprimée par la notion de conformité. Une in-
terface I2 est dite conforme à une interface I1 si un composant qui réalise toute méthode
spécifiée dans I2 peut partout être utilisé à la place d’un composant qui réalise toute
méthode spécifiée dans I1. En d’autres termes, I2 est conforme à I1 si I2 satisfait le
contrat de I1.
La conformité peut être vérifiée à chacun des quatre niveaux définis ci-dessus. Nous les
examinons successivement.
Contrats syntaxiques
Un contrat syntaxique est fondé sur la forme des opérations. Un tel contrat s’exprime
couramment en termes de types. Un type définit un prédicat qui s’applique aux objets 4
de ce type. Le type d’un objet X est noté T (X ). La notion de conformité est exprimée
par le sous-typage : si T2 est un sous-type de T1 (noté T2 v T1 ), tout objet de type T2
est aussi un objet de type T1 (en d’autre termes, un objet de type T2 peut être utilisé
partout où un objet de type T1 est attendu). La relation de sous-typage ainsi définie est
appelée sous-typage vrai, ou conforme.
Considérons des interfaces définies comme un ensemble de procédures. Pour de telles
interfaces, le sous-typage conforme est défini comme suit : une interface I2 est un sous-type
d’une interface de type I1 (noté T (I2 ) v T (I1 )) si I2 a au moins le même nombre de
procédures que I1 (elle peut en avoir plus), et si pour chaque procédure définie dans I1
il existe une procédure conforme dans I2. Une procédure Proc2 est dite conforme à une
procédure Proc1 lorsque les relations suivantes sont vérifiées entre les signatures de ces
procédures.
– Proc1 et Proc2 ont le même nombre de paramètres et valeurs de retour (les excep-
tions déclarées sont considérées comme des valeurs de retour).
– pour chaque valeur de retour R1 de Proc1, il existe une valeur de retour correspon-
dante R2 de Proc2 telle que T(R2) v T(R1) (relation dite covariante).
– pour chaque paramètre d’appel X1 de Proc1, il existe un paramètre d’appel corres-
pondant X2 de Proc2 tel que T(X1) v T(X2) (relation dite contravariante).
Ces règles illustrent un principe général de possibilité de substitution : une entité E2
peut être substituée à une autre entité E1 si E2 « fournit au moins autant et requiert au
4
Ici le terme d’objet désigne toute entité identifiable dans le présent contexte, par exemple une variable,
une procédure, une interface, un composant.
2.1. SERVICES ET INTERFACES 27
plus autant » que E1. Ici les termes « fournit » et « requiert » doivent être être adaptés
à chaque situation spécifique (par exemple dans un appel de procédure , les paramètres
d’appel sont « requis » et le résultat est « fourni »). La relation d’ordre qu’impliquent les
termes « au plus autant » et « au moins autant » est la relation de sous-typage.
Notons que la relation de sous-typage définie dans la plupart des langages de program-
mation ne satisfait généralement pas la contravariance des types de paramètres et n’est
donc pas un sous-typage vrai. Dans un tel cas (qui est par exemple celui de Java), des
erreurs de conformité peuvent échapper à la détection statique et doivent être capturées
par un test à l’exécution.
La notion de conformité peut être étendue aux autre formes de définitions d’interface,
par exemple celles contenant des sources ou puits d’événements, ou des flots de données
(streams).
Rappelons que la relation entre types est purement syntaxique et ne capture pas la
sémantique de la conformité. La vérification de la sémantique est le but des contrats
comportementaux.
Contrats comportementaux
Les contrats comportementaux sont fondés sur une méthode proposée dans
[Hoare 1969] pour prouver des propriétés de programmes, en utilisant des pré- et post-
conditions avec des règles de preuve fondées sur la logique du premier ordre. Soit A une
action séquentielle. Alors la notation
{P} A {Q},
dans lequel P et Q sont des assertions (prédicats sur l’état de l’univers du programme), a
le sens suivant : si l’exécution de A est lancée dans un état dans lequel P est vrai, et si A
se termine, alors Q est vrai à la fin de cette exécution. Une condition supplémentaire peut
être spécifiée sous la forme d’un prédicat invariant I qui doit être preservé par l’exécution
de A. Ainsi si P et I sont initialement vrais, Q et I sont vrais à la fin de A, si A se termine.
L’invariant peut être utilisé pour spécifier une contrainte de cohérence.
Ceci peut être transposé comme suit en termes de services et de contrats. Avant
l’exécution d’un service,
– le demandeur doit garantir la précondition P et l’invariant I,
– le fournisseur doit garantir que le service est effectivement delivré dans un temps
fini, et doit assurer la postcondition Q et l’invariant I.
Les cas possibles de terminaison anormale doivent être spécifiés dans le contrat et
traités par réessai ou par la levée d’une exception. Cette méthode a été développée
sous le nom de « conception par contrat » [Meyer 1992] via des extensions au langage
Eiffel permettant l’expression de pré- et post-conditions et de prédicats invariants. Ces
conditions sont vérifiées à l’exécution. Des outils analogues ont été développés pour Java
[Kramer 1998].
La notion de sous-typage peut être étendue aux contrats comportementaux, en
spécifiant les contraintes de conformité pour les assertions. Soit une procédure Proc1 définie
dans l’interface I1, et la procédure correspondante (conforme) Proc2 définie dans l’inter-
face I2, telle que T (I2 ) v T (I1 ). Soit P1 et Q1 (resp. P2 et Q2 ) les pré- et post-conditions
définies pour Proc1 (resp. Proc2 ). Les conditions suivantes doivent être vérifiées :
28 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
P1 ⇒ P2 et Q2 ⇒ Q1
En d’autres termes, un sous-type a des préconditions plus faibles et des postconditions
plus fortes que son super-type, ce qui illustre de nouveau la condition de substitution.
Contrats de synchronisation
L’expression de la validité des programmes au moyen d’assertions peut être étendue
aux programmes concurrents. Le but ici est de séparer, autant que possible, la description
des contraintes de synchronisation du code des procédures. Les expressions de chemin (path
expressions), qui spécifient des contraintes sur l’ordre et la concurrence de l’exécution des
procédures, ont été proposées dans [Campbell and Habermann 1974]. Les développements
ultérieurs (compteurs et politiques de synchronisation) ont essentiellement été des exten-
sions et des raffinements de cette construction, dont la réalisation repose sur l’exécution
de procédures engendrées à partir de la description statique des contraintes. Plusieurs ar-
ticles décrivant des propositions dans ce domaine figurent dans [CACM 1993], mais ces
techniques n’ont pas trouvé une large application.
Une forme très simple de contrat de synchronisation est la clause synchronized de
Java, qui spécifie une exécution en exclusion mutuelle. Un autre exemple est le choix d’une
politique de gestion de file d’attente (par exemple FIFO, priorité, etc.) parmi un ensemble
prédéfini pour la gestion d’une resource partagée.
Les travaux plus récents (voir par exemple [Chakrabarti et al. 2002]) visent à vérifier les
contraintes de synchronisation à la compilation, pour détecter assez tôt les incompatibilités.
downcall upcall
Interface i-1 interface i
level i+1
interface i
level i
upcall
interface i-1
level i-1
… …
base level
L’interface fournie par chaque niveau peut être vue comme un ensemble de fonctions
définissant une bibliothèque, auquel cas elle est souvent appelée API (Application Program-
ming Interface)6 . Une vue alternative est de considérer chaque niveau comme une machine
virtuelle, dont le « langage » (le jeu d’instructions) est défini par son interface. En vertu du
principe d’encapsulation, une machine virtuelle masque les détails de réalisation de tous
les niveaux inférieurs. Les machines virtuelles ont été utilisées pour émuler un ordinateur
ou un système d’exploitation au-dessus d’un autre, pour émuler un nombre quelconque
de ressources identiques par multiplexage d’une ressource physique, ou pour réaliser l’en-
vironnement d’exécution d’un langage de programmation (par exemple la Java Virtual
Machine (JVM) [Lindholm and Yellin 1996]).
Ce schéma de base peut être étendu de plusieurs manières. Dans la première extension
(Figure 2.4b), une couche de niveau i peut utiliser tout ou partie des interfaces fournies par
les machines de niveau inférieur. Dans la seconde extension, une couche de niveau i peut
rappeler la couche de niveau i+1, en utilisant une interface de rappel (callback ) fournie
par cette couche. Dans ce contexte, le rappel est appelé « appel ascendant » (upcall) (par
référence à la hiérarchie « verticale » des couches).
Bien que les appels ascendants puissent être synchrones, leur utilisation la plus
fréquente est la propagation d’événements asynchrones vers le haut de la hiérarchie
des couches. Considérons la structure d’un noyau de système d’exploitation. La couche
supérieure (application) active le noyau par appels descendants synchrones, en utilisant
l’API des appels système. Le noyau active aussi les fonctions réalisées par le matériel
(par exemple mettre à jour la MMU, envoyer une commande à un disque) par l’équivalent
d’appels synchrones. En sens inverse, le matériel active typiquement le noyau via des inter-
ruptions asynchrones (appels ascendants), qui déclenchent l’exécution de traitants. Cette
structure d’appel est souvent répétée aux niveaux plus élevés : chaque couche reçoit des ap-
pels synchrones de la couche supérieure et des appels asynchrones de la couche inférieure.
Ce patron, décrit dans [Schmidt et al. 2000] sous le nom de Half Sync, Half Async,
est largement utilisé dans les protocoles de communication.
Architectures multiétages
Le développement des systèmes répartis a promu une forme différente d’architecture
multiniveaux. Considérons l’évolution historique d’une forme usuelle d’applications client-
serveur, dans laquelle les demandes d’un client sont traitées en utilisant l’information
stockée dans une base de données.
Dans les années 1970 (Figure 2.5a), les fonctions de gestion de données et l’application
elle-même sont exécutées sur un serveur central (mainframe). Le poste du client est un
simple terminal, qui réalise une forme primitive d’interface utilisateur.
Dans les années 1980 (Figure 2.5b), les stations de travail apparaissent comme machines
clientes, et permettent de réaliser des interfaces graphique élaborées pour l’utilisateur.
Les capacités de traitement de la station cliente lui permettent en outre de participer au
traitement de l’application, reduisant ainsi la charge du serveur et améliorant la capacité de
croissance (car l’addition d’une nouvelle station cliente ajoute de la puissance de traitement
6
Une interface complexe peut aussi être partitionnée en plusieurs APIs, chacune étant liée à une fonction
spécifique.
2.2. PATRONS ARCHITECTURAUX 31
(a)
user data
application
interface management
user data
application
interface management (c)
(b)
Ces défauts sont corrigés par l’architecture décrite sur la Figure 2.5c, introduite à la
fin des années 1990. Les fonctions de l’application sont partagées entre trois machines :
la station client ne réalise que l’interface graphique, l’application proprement dite réside
sur un serveur dedié, et la gestion de la base de données est dévolue à une autre machine.
Chacune de ces divisions « horizontales » est appelée un étage (en anglais tier ). Une
spécialisation plus fine des fonctions donne lieu à d’autres architectures multiétages. Noter
que chaque étage peut lui-même faire l’objet d’une décomposition « verticale » en niveaux
d’abstraction.
L’architecture multiétages conserve l’avantage du passage à grande échelle, à condition
que les serveurs puissent être renforcés de manière incrémentale (par exemple en ajoutant
des machines à une grappe). En outre les interfaces entre étages peuvent être conçues
pour favoriser la séparation de préoccupations, puisque les interfaces logiques coı̈ncident
maintenant avec les interfaces de communication. Par exemple, l’interface entre l’étage
d’application et l’étage de gestion de données peut être rendue générique, pour accepter
facilement un nouveau type de base de données, ou pour intégrer une application patri-
moniale, en utilisant un adaptateur (section 2.3.4) pour la conversion d’interface.
Des exemples d’architectures multiétages sont présentés dans le chapitre 5.
Canevas
Un canevas logiciel (en anglais framework ) est un squelette de programme qui peut
être directement réutilisé, ou adapté selon des règles bien définies, pour résoudre une
famille de problèmes apparentés. Cette définition recouvre de nombreux cas d’espèce ;
nous nous intéressons ici à une forme particulière de canevas composée d’une infrastructure
32 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
dans laquelle des composants logiciels peuvent être insérés en vue de fournir des services
spécifiques. Ces canevas illustrent des notions relatives aux interfaces, aux rappels et à
l’inversion du contrôle.
Le premier exemple (Figure 2.6a) est le micronoyau, une architecture introduite dans
les années 1980 et visant à développer des systèmes d’exploitation facilement configurables.
Un système d’exploitation à micronoyau se compose de deux couches :
– Le micronoyau proprement dit, qui gère les resources matérielles (processeurs,
mémoire, entrées-sorties, interface de réseau), et fournit au niveau supérieur une
API abstraite de gestion de ressources.
– Le noyau, qui réalise un système d’exploitation spécifique (une « personnalité ») en
utilisant l’API du micronoyau.
Un noyau de système d’exploitation construit sur un micronoyau est généralement
organisé comme un ensemble de serveurs, dont chacun est chargé d’une fonction spécifique
(gestion de processus, système de fichiers, etc.). Un appel système typique émis par une
application est traité comme suit .
– Le noyau analyse l’appel et active le micronoyau en utilisant la fonction appropriée
de son API.
– Le micronoyau rappelle un serveur dans le noyau. Au retour de cet appel ascendant,
le micronoyau peut interagir avec le matériel ; cette séquence peut être itérée, par
exemple si plusieurs serveurs sont en jeu.
– Le micronoyau rend la main au noyau, qui termine le travail et revient à l’application.
Pour ajouter une nouvelle fonction à un noyau, il faut donc développer et intégrer un
nouveau serveur.
application
application components
kernel callback interface callback interface
server
callback
interface microkernel
API client data
application management
microkernel
framework API
hardware
Le second exemple (Figure 2.6b) illustre l’organisation typique de l’étage médian d’une
architecture client-serveur à 3 étages. Ce canevas interagit avec l’étage client et avec l’étage
de gestion de données, et sert de médiateur pour l’interaction entre ces étages et le pro-
gramme de l’application proprement dite. Ce programme est organisé comme un ensemble
de composants, qui utilisent l’API fournie par le canevas et doivent fournir un ensemble
d’interfaces de rappel. Ainsi une requête d’un client est traitée par le canevas, qui active
les composant applicatifs appropriés, interagit avec eux en utilisant ses propres API et
l’interface de rappel des composants, et retourne finalement au client.
Des exemples détaillés de cette organisation sont présentés au chapitre 5.
2.2. PATRONS ARCHITECTURAUX 33
Les deux exemples ci-dessus illustrent l’inversion du contrôle. Pour fournir ses services,
le canevas utilise des rappels vers les modules logiciels externes (serveurs dans l’exemple
micronoyau, ou composants applicatifs dans l’étage médian). Ces modules doivent respec-
ter le contrat du canevas, en fournissant des interfaces de rappel spécifiées et en utilisant
l’API du canevas.
Les organisations en couches et en étages définissent une structure à gros grain pour
un système complexe. L’organisation interne de chaque couche ou étage (ou couche dans
un étage) utilise elle-même des entités de grain plus fin. Les objets, un moyen usuel de
définir cette structure fine, sont présentés dans la section suivante.
Objets distants
Les propriétés ci-dessus font que les objets sont un bon mécanisme de structuration
pour les systèmes répartis.
– L’hétérogénéité est un trait dominant de ces systèmes. L’encapsulation est un outil
puissant dans un environnement hétérogène : l’utilisateur d’un objet doit seulement
connaı̂tre une interface pour cet objet, qui peut avoir des réalisations différentes sur
différents sites.
– La création dynamique d’instances d’objets permet de construire un ensemble d’ob-
jets ayant la même interface, éventuellement sur des sites distants différents ; dans
ce cas l’intergiciel doit fournir un mécanisme pour la création d’objets distants, sous
la forme de fabriques (voir section 2.3.2).
– L’héritage est un mécanisme de réutilisation, car il permet de définir une nouvelle
interface à partir d’une interface existante. Il est donc utile pour les développeurs
d’applications réparties, qui travaillent dans un environnement changeant et doivent
définir de nouvelles classes pour traiter des nouvelles situations. Pour utiliser
l’héritage, on conçoit d’abord une classe (de base) générique pour capturer un en-
semble de traits communs à une large gamme de situations attendues. Des classes
spécifiques, plus spécialisées, sont alors définies par extension de la classe de base.
Par exemple, une interface pour un flot vidéo en couleur peut être définie comme
une extension de celle d’un flot vidéo générique. Une application qui utilise des flots
d’objets vidéo accepte aussi des flots d’objets en couleur, puisque ces objets réalisent
l’interface des flots vidéo (c’est un exemple de polymorphisme).
La manière la plus simple et la plus courante pour répartir des objets est de permettre
aux objets qui constituent une application d’être situés sur un ensemble de sites répartis
(autrement dit, l’objet est l’unité de répartition ; d’autres méthodes permettent de par-
titionner la représentation d’un objet entre plusieurs sites). Une application cliente peut
utiliser un objet situé sur un site distant en appelant une méthode de l’interface de l’objet,
comme si l’objet était local. Des objets utilisés de cette manière sont appelés objets dis-
tants, et leur mode d’interaction est l’appel d’objet distant (Remote Method Invocation) ;
c’est la transposition du RPC au monde des objets.
Les objets distants sont un exemple d’une organisation client-serveur. Comme un client
peut utiliser plusieurs objets différents situés sur un même site distant, des termes distincts
sont utilisés pour désigner le site distant (le site serveur ) et un objet individuel qui fournit
un service spécifique (un objet servant). Pour que le système fonctionne, un intergiciel
2.3. PATRONS POUR L’INTERGICIEL À OBJETS RÉPARTIS 35
approprié doit localiser une réalisation de l’objet servant sur un site éventuellement distant,
envoyer les paramètres sur l’emplacement de l’objet, réaliser l’appel effectif, et renvoyer
les résultats à l’appelant. Un intergiciel qui réalise ces fonctions est un courtier d’objets
répartis (en anglais Object Request Broker, ou ORB).
Name
Client ORB server Servant
register
lookup
invoke
return
La structure d’ensemble d’un appel à un objet distant (Figure 2.7) est semblable à
celle d’un RPC : l’objet distant doit d’abord être localisé, ce qui est généralement fait au
moyen d’un serveur des noms ou d’un service vendeur (trader ) ; l’appel proprement dit est
ensuite réalisé. L’ORB sert de médiateur aussi bien pour la recherche que pour l’appel.
2.3.1 Proxy
Proxy (ce terme anglais est traduit par « représentant » ou « mandataire ») est un
des premiers patrons de conception identifiés en programmation répartie [Shapiro 1986,
Buschmann et al. 1995]. Nous n’examinons ici que son utilisation pour les objets répartis,
bien que son domaine d’application ait été étendu à de nombreuses autres constructions.
1. Contexte. Le patron Proxy est utilisé pour des applications organisées comme
un ensemble d’objets dans un environnement réparti, communicant au moyen d’ap-
pels de méthode à distance : un client demande un service fourni par un objet
éventuellement distant (le servant).
36 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
service request
pre-processing
Interface I Interface I
service request
result
result post-processing
La structure interne d’un mandataire suit un schéma bien défini, qui facilite sa
génération automatique.
– une phase de pré-traitement, qui consiste essentiellement à emballer les paramètres
et à préparer le message de requête,
– l’appel effectif du servant, utilisant le protocole de communication sous-jacent pour
envoyer la requête et pour recevoir la réponse,
– une phase de post-traitement, qui consiste essentiellement à déballer les valeurs
de retour.
6. Usages connus.
Dans la construction de l’intergiciel, les mandataires sont utilisés comme
représentants locaux pour des objets distants. Ils n’ajoutent aucune fonction. C’est
le cas des souches (stubs) et des squelettes utilisés dans RPC ou Java-RMI.
Des variantes des proxies contiennent des fonctions supplémentaires. Des exemples
en sont les caches et l’adaptation côté client. Dans ce dernier cas, le proxy peut
2.3. PATRONS POUR L’INTERGICIEL À OBJETS RÉPARTIS 37
2.3.2 Factory
1. Contexte. On considère des applications organisées comme un ensemble d’objets
dans un environnement réparti (la notion d’objet dans ce contexte peut être très
générale, et n’est pas limitée au domaine strict de la programmation par objets).
2. Problème. On souhaite pouvoir créer dynamiquement des familles d’objets appa-
rentés (par exemple des instances d’une même classe), tout en permettant de reporter
certaines décisions jusqu’à la phase d’exécution (par exemple le choix d’une classe
concrète pour réaliser une interface donnée).
3. Propriétés souhaitées. Les détails de réalisation des objets créés doivent être
invisibles. Le processus de création doit pouvoir être paramétré. L’évolution du
mécanisme doit être facilitée (pas de décision « en dur »).
4. Contraintes. La principale contrainte résulte de l’environnement réparti : le client
(qui demande la création de l’objet) et le serveur (qui crée effectivement l’objet) sont
dans des espaces d’adressage différents.
5. Solution. Utiliser deux patrons corrélés : une usine abstraite Abstract Factory
définit une interface et une organisation génériques pour la création d’objets ; la
création est déléguée à des usines concrètes. Abstract Factory peut être réalisé
en utilisant Factory Methods (une méthode de création redéfinie dans une sous-
classe).
Un autre manière d’améliorer la souplesse est d’utiliser une usine de fabrication
d’usines, comme illustré sur la Figure 2.9 (le mécanisme de création est lui-même
paramétré).
Un usine peut aussi être utilisée comme un gestionnaire des objets qu’elle a créés, et
peut ainsi réaliser une méthode pour localiser un objet (en renvoyant une référence
pour cet objet), et pour détruire un objet sur demande.
6. Usages connus.
Factory est un des patrons les plus largement utilisés dans l’intergiciel. Il sert à
la fois dans des applications (pour créer des instances distantes d’objets applicatifs)
et dans l’intergiciel lui-même (un exemple courant est l’usine à liaisons). Les usines
sont aussi utilisées en liaison avec les composants (voir chapitres 5 et 7).
7. Références.
Les deux patrons Abstract Factory et Factory Method sont décrits dans
[Gamma et al. 1994].
38 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
FactoryFactory
create
Client Factory with
parameters
create
Object
optional
2.3.3 Pool
Le patron Pool est un complément à Factory, qui vise à réduire le temps d’exécution
de la création et de la destruction d’objets, en construisant à l’avance (lors d’une phase
d’initialisation) une réserve (pool) d’objets. Cela se justifie si le coût de la création et de
la destruction est élevé par rapport à celui des opérations sur la réserve. Les opérations de
création et de destruction deviennent alors :
observées.
– La gestion des threads ou des processus.
– La gestion des composants dans certains canevas (par exemple les Entity Beans dans
la plate-forme EJB, voir chapitre 5)
Dans tous ces cas, le coût élevé de création des entités justifie largement l’usage d’une
réserve.
2.3.4 Adapter
1. Contexte. Le contexte est celui de la fourniture de services, dans un environnement
réparti : un service est défini par une interface ; les clients demandent des services ;
des servants, situés sur des serveurs distants, fournissent des services.
2. Problème. On souhaite réutiliser un servant existant en le dotant d’une nouvelle
interface conforme à celle attendue par un client (ou une classe de clients).
3. Propriétés souhaitées. Le mécanisme de conversion d’interface doit être efficace à
l’exécution. Il doit aussi être facilement adaptable, pour répondre à des changements
imprévus des besoins. Il doit être réutilisable (c’est-à-dire générique).
4. Contraintes. Pas de contraintes spécifiques.
5. Solution. Fournir un composant (l’adaptateur, ou wrapper ) qui isole le servant en
interceptant les appels de méthode à son interface. Chaque appel est précédé par un
prologue et suivi par un épilogue dans l’adaptateur (Figure 2.10). Les paramètres et
résultats peuvent nécessiter une conversion.
service request
pre-processing
service request
result
Interface I1
result post-processing
Interface I2
Dans des cas simples, un adaptateur peut être automatiquement engendré à partir
d’une description des interfaces fournie et requise.
6. Usages connus.
40 CHAPITRE 2. PATRONS ET CANEVAS POUR L’INTERGICIEL
Les adaptateurs sont largement utilisés dans l’intergiciel pour encapsuler des fonc-
tions côté serveur. Des exemples sont le Portable Objet Adapter (POA) de CORBA et
les divers adaptateurs pour la réutilisation de logiciel patrimoniaux (legacy systems),
tel que Java Connector Architecture (JCA).
7. Références.
Adapter (également appelé Wrapper) est décrit dans [Gamma et al. 1994]. Un
patron apparenté est Wrapper Façade ([Schmidt et al. 2000]), qui fournit une
interface de haut niveau (par exemple sous forme d’objet) à des fonctions de bas
niveau.
2.3.5 Interceptor
1. Contexte. Le contexte est celui de la fourniture de services, dans un environnement
réparti : un service est défini par une interface ; les clients demandent des services ;
les servants, situés sur des serveurs distants, fournissent des services. Il n’y a pas de
restrictions sur la forme de la communication (uni- or bi-directionnelle, synchrone
ou asynchrone, etc.).
2. Problème. On veut ajouter de nouvelles capacités à un service existant, ou fournir
le service par un moyen différent.
3. Propriétés souhaitées. Le mécanisme doit être générique (applicable à une large
variété de situations). Il doit permettre de modifier un service aussi bien statiquement
(à la compilation) que dynamiquement (à l’exécution).
4. Contraintes. Les services peuvent être ajoutés ou supprimés dynamiquement.
5. Solution. Créer (statiquement ou dynamiquement) des objets d’interposition, ou
intercepteurs. Ces objets interceptent les appels (et/ou les retours) et insérent un
traitement spécifique, qui peut être fondé sur une analyse du contenu. Un intercepteur
peut aussi rediriger un appel vers une cible différente.
I I
I I I
Ce mécanisme peut être réalisé sous différentes formes. Dans la forme la plus simple,
un intercepteur est un module qui est inseré à un point spécifié dans le chemin
d’appel entre le demandeur et le fournisseur d’un service (Figure 2.11a et 2.11b).
Il peut aussi être utilisé comme un aiguillage entre plusieurs servants qui peuvent
fournir le même service avec différentes options (Figure 2.11c), par exemple l’ajout
de fonctions de tolérance aux fautes, d’équilibrage de charge ou de caches.
2.3. PATRONS POUR L’INTERGICIEL À OBJETS RÉPARTIS 41
Sous une forme plus générale (Figure 2.12), intercepteurs et fournisseurs de service
(servants) sont gérés par une infrastructure commune et créés sur demande. L’inter-
cepteur utilise l’interface du servant et peut aussi s’appuyer sur des services fournis
par l’infrastructure. Le servant peut fournir des fonctions de rappel utilisables par
l’intercepteur.
6. Usages connus.
Les intercepteurs sont utilisés dans une grande variété de situations dans les systèmes
intergiciels.
– pour ajouter des nouvelles capacités à des applications ou systèmes existants. Un
exemple ancien est le mécanisme des « sous-contrats »[Hamilton et al. 1993]. Les
Portable Interceptors de CORBA donnent une manière systématique d’étendre
les fonctions du courtier d’objets (ORB) par insertion de modules d’interception
en des points predéfinis dans le chemin d’appel. Un autre usage est l’aide aux
mécanismes de tolérance aux fautes (par exemple la gestion de groupes d’objets).
– pour choisir une réalisation spécifique d’un servant à l’exécution.
– pour réaliser un canevas pour des applications à base de composants (voir chapitres
5 et 7).
– pour réaliser un intergiciel réflexif (voir 2.4.1 and 2.4.3).
Supporting
Client
infrastructure
create
Servant
Interceptor create
use service
Interface I
callback
result
7. Références.
Le patron Interceptor est décrit dans [Schmidt et al. 2000].
implique souvent (pas toujours) un accès à distance, alors que Adapter est
généralement local.
– Adapter vs Interceptor. Adapter et Interceptor ont une fonction sem-
blable : l’un et l’autre modifient un service existant. La principale différence est
que Adapter transforme l’interface, alors que Interceptor transforme la fonc-
tion (de fait Interceptor peut complètement masquer la cible initiale de l’appel,
en la remplaçant par un servant différent).
– Proxy vs Interceptor. Un Proxy peut être vu comme une forme spéciale d’un
Interceptor, dont la fonction se réduit à acheminer une requête vers un servant
distant, en réalisant les transformations de données nécessaires à la transmission,
d’une manière indépendante des protocoles de communication. En fait, comme men-
tionné dans 2.3.1, un proxy peut être combiné avec un intercepteur, devenant ainsi
« intelligent » (c’est-à-dire fournissant de nouvelles fonctions en plus de la transmis-
sion des requêtes, mais laissant l’interface inchangée).
En utilisant les patrons ci-dessus, on peut tracer un premier schéma grossier et incom-
plet de l’organisation d’ensemble d’un ORB (Figure 2.13).
Proxy Adapter
Client-side Server-side
Interceptor Interceptor
Communication system
La réflexion est une propriété intéressante pour un intergiciel, parce qu’un tel système
fonctionne dans un environnement qui évolue, et doit adapter son comportement à des be-
soins changeants. Des capacités réflexives sont présentes dans la plupart des systèmes inter-
giciels existants, mais sont généralement introduites localement, pour des traits isolés. Des
plates-formes intergicielles dont l’architecture de base intègre la réflexion sont développées
comme prototypes de recherche [RM 2000].
Une approche générale de la conception d’un système réflexif consiste à l’organiser en
deux niveaux.
– Le niveau de base, qui fournit les fonctions définies par les spécifications du système.
– Le méta-niveau, qui utilise une représentation des entités du niveau de base pour
observer ou modifier le comportement de ce niveau.
Cette décomposition peut être itérée en considérant le méta-niveau comme un niveau
de base pour un méta-méta-niveau, et ainsi de suite, définissant ainsi une « tour réflexive ».
Dans la plupart des cas pratiques, la tour est limitée à deux ou trois niveaux.
La définition d’une représentation du niveau de base, destinée à être utilisée par le méta-
niveau, est un processus appelé réification. Il conduit à définir des méta-objets, dont chacun
est une représentation, au méta-niveau, d’une structure de données ou d’une opération
définie au niveau de base. Le fonctionnement des méta-objets, et leur relation aux entités du
niveau de base, sont spécifiées par un protocole à méta-objets (MOP) [Kiczales et al. 1991].
Un exemple simple de MOP (emprunté à [Bruneton 2001]) est la réification d’un appel
de méthode dans un système réflexif à objets. Au méta-niveau, un méta-objet Méta Obj
est associé à chaque objet Obj. L’exécution d’un appel de méthode Obj.meth(params)
comporte les étapes suivantes (Figure 2.14).
1. L’appel de méthode est réifié dans un objet m, qui contient une représentation de
meth et params. La forme précise de cette représentation est définie par le MOP. Cet
objet m est transmis au méta-objet, qui exécute Méta Obj.méta MethodCall(m).
Obj.meth(params)
meta-object
Meta_Obj reification
reification Meta_Obj.metaMethodCall(m)
meta-object
client Object Factory
Factory
X=newObject(…)
meta-object create meta-object
reification Factory Meta_Obj reification
Meta_Obj.metaNewObj(…)
meta level
baseNewObj(…) reflection
base level reflection X reference create
callback Object return object ref
X=newObject(…) callback
client Object create
X object Obj create
factory Meta-Object
– La partie principale de l’application (le programme de base), et les parties qui traitent
des différents aspects supplémentaires sont écrites indépendamment, en utilisant
éventuellement des langages spécialisés pour le code des aspects.
– Toutes ces parties sont intégrées pour former l’application globale, en utilisant un
outil de composition, le tisseur d’aspects (aspect weaver ).
Un point de jonction (join point) est un emplacement, dans le code source du pro-
gramme de base, où du code lié aux aspects peut être inséré. Le tissage d’aspects repose
sur deux notions principales : le point de coupure (point cut), c’est-à-dire la spécification
d’un ensemble de points de jonction selon un critère donné, et l’indication (advice), c’est-à-
dire la définition de l’interaction du code inséré avec le code de base. Par exemple, si l’AOP
est ajouté à un langage à objets, un point de coupure particulier peut être défini comme
l’ensemble des points d’appel à une famille de méthodes (spécifiée par une expression re-
gulière), ou l’ensemble des appels à un constructeur spécifié, etc. Une indication spécifie
si le code inséré doit être exécuté avant, après, ou en remplacement des opérations situées
aux points de coupure (dans le dernier cas, ces opérations peuvent toujours être appelées
depuis le code inséré). La composition peut être faite statiquement (à la compilation),
dynamiquement (à l’exécution), ou en combinant des techniques statiques et dynamiques.
Un problème important de l’AOP est la composition des aspects. Par exemple, si
différents fragments de code liés aux aspects sont insérés au même point de jonction, l’ordre
d’insertion peut être significatif si les aspects correspondants ne sont pas indépendants.
Cette question ne peut généralement pas être décidée par le tisseur et nécessite une
spécification supplémentaire.
Deux exemples d’outils qui réalisent l’AOP sont AspectJ [Kiczales et al. 2001] et JAC
[Pawlak et al. 2001]. Ils s’appliquent à des programmes de base en Java.
AspectJ
/* définition d’indication */
around(): Wrappable() {
prelude ; /* séquence de code devant e^tre insérée avant un appel */
proceed (); /* exécution d’un appel à la méthode originelle */
postlude /* séquence de code devant e^tre insérée après un appel */
}
}
JAC
JAC (Java Aspect Components) a des objectifs voisins de ceux d’AspectJ. Il permet
d’ajouter des capacités supplémentaires (mise sous enveloppe de méthodes, introduction)
à une application existante. JAC diffère d’AspectJ sur les points suivants.
– JAC n’est pas une extension de langage, mais un canevas qui peut être utilisé à
l’exécution. Ainsi des aspects peuvent être dynamiquement ajoutés à une application
en cours d’exécution. JAC utilise la modification du bytecode, et le code des classes
d’application est modifié lors du chargement des classes.
– Les point cuts et les advices sont définis séparément. La liaison entre point cuts et
advices est retardée jusqu’à la phase de tissage ; elle repose sur des informations
fournies dans un fichier de configuration séparé. La composition d’aspects est définie
par un protocole à méta-objets.
Ainsi JAC autorise une programmation souple, mais au prix d’un surcoût à l’exécution
dû au tissage dynamique des aspects dans le bytecode.
Chapitre 3
Le modèle de composants Fractal a été défini par France Telecom R&D et l’INRIA. Il se
présente sous la forme d’une spécification et d’implémentations dans différents langages de
programmation comme Java, C, C++, SmallTalk ou les langages de la plate-forme .NET.
Fractal est organisé comme un projet du consortium ObjectWeb pour le middleware open
source. Les premières discussions autour du modèle Fractal, initiées dès 2000 à France
Telecom R&D dans la lignée du projet Jonathan [Dumant et al. 1998], ont abouti en juin
2002 avec la première version officielle de la spécification et la première version de Julia,
qui est l’implémentation de référence de cette spécification. La spécification a évolué pour
aboutir en septembre 2003 à une deuxième version comportant un certain nombre de
changements au niveau de l’API. Dès le départ, un langage de description d’architecture,
Fractal ADL, a été associé au modèle de composants. Basé sur une syntaxe ad hoc au
départ, il a évolué et sa version 2, définie en janvier 2004 et implémentée en mars 2004,
est basée sur une syntaxe extensible.
Les caractéristiques principales du modèle Fractal sont motivées par l’objectif de pou-
voir construire, déployer et administrer des systèmes complexes tels que des intergiciels ou
des systèmes d’exploitation. Le modèle est ainsi basé sur les principes suivants :
– composants composites (i.e. composants qui contiennent des sous-composants)
pour permettre d’avoir une vue uniforme des applications à différents niveaux d’abs-
traction.
– composants partagés (i.e. sous-composants de plusieurs composites englobants)
pour permettre de modéliser les ressources et leur partage, tout en préservant l’en-
capsulation des composants.
– capacités d’introspection pour permettre d’observer l’exécution d’un système.
– capacités de (re)configuration pour permettre de déployer et de configurer dy-
namiquement un système.
Par ailleurs, Fractal est un modèle extensible du fait qu’il permet au développeur de
personnaliser les capacités de contrôle de chacun des composants de l’application. Il est
ainsi possible d’obtenir un continuum dans les capacités réflexives d’un composant allant
de l’absence totale de contrôle à des capacités élaborées d’introspection et d’intercession
(e.g. accès et manipulation du contenu d’un composant, contrôle de son cycle de vie). Ces
fonctionnalités sont définies au sens d’entités appelées contrôleurs
Il est ainsi possible de distinguer différents rôles dans les activités de développement
autour du modèle Fractal :
– les développeurs de composants applicatifs s’intéressent à la construction d’ap-
plications et de systèmes à l’aide de Fractal. Ils développent des composants et des
assemblages de composants à l’aide de l’API Fractal et du langage de description
d’architecture Fractal ADL. Ces composants utilisent des contrôleurs et des plates-
formes existants.
– les développeurs de contrôleurs s’intéressent à la personnalisation du contrôle
offerts aux composants. Ils développent de nouvelles politiques de contrôle compor-
tant plus ou moins de fonctionnalités extra-fonctionnelles et permettant d’adapter
les applications à différents contextes d’exécution (par exemple avec des ressources
plus ou moins contraintes). Le développement de contrôleurs est conduit à l’aide
des mécanismes offerts par les plates-formes : par exemple, mixin pour Julia (voir
section 3.2.1) ou aspect pour AOKell (voir section 3.2.2).
3.1. LE MODÈLE FRACTAL : HISTORIQUE, DÉFINITION ET PRINCIPES 51
Composants et liaisons
Un composant Fractal est une entité d’exécution qui possède une ou plusieurs interfaces.
Une interface est un point d’accès au composant. Une interface implante un type d’interface
qui spécifie les opérations supportées par l’interface. Il existe deux catégories d’interfaces :
les interfaces serveurs — qui correspondent aux services fournis par le composant —, et
les interfaces clients qui correspondent aux services requis par le composant.
Un composant Fractal est généralement composé de deux parties : une membrane —
qui possède des interfaces fonctionnelles et des interfaces permettant l’introspection et la
configuration (dynamique) du composant —, et un contenu qui est constitué d’un ensemble
fini de sous-composants.
Les interfaces d’une membrane sont soit externes, soit internes. Les interfaces externes
sont accessibles de l’extérieur du composant, alors que les interfaces internes sont acces-
sibles par les sous-composants du composant. La membrane d’un composant est constituée
d’un ensemble de contrôleurs. Les contrôleurs peuvent être considérés comme des méta-
objets. Chaque contrôleur a un rôle particulier : par exemple, certains contrôleurs sont
chargés de fournir une représentation causalement connectée de la structure d’un com-
posant (en termes de sous-composants). D’autres contrôleurs permettent de contrôler le
comportement d’un composant et/ou de ses sous-composants. Un contrôleur peut, par
exemple, permettre de suspendre/reprendre l’exécution d’un composant.
Le modèle Fractal fournit deux mécanismes permettant de définir l’architecture d’une
application : l’imbrication (à l’aide des composants composites) et la liaison. La liaison
est ce qui permet aux composants Fractal de communiquer. Fractal définit deux types de
liaisons : primitive et composite. Les liaisons primitives sont établies entre une interface
client et une interface serveur de deux composants résidant dans le même espace d’adres-
sage. Par exemple, une liaison primitive dans le langage C (resp. Java) est implantée à
52 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
l’aide d’un pointeur (resp. référence). Les liaisons composites sont des chemins de com-
munication arbitrairement complexes entre deux interfaces de composants. Les liaisons
composites sont constituées d’un ensemble de composants de liaison (e.g. stub, skeleton)
reliés par des liaisons primitives.
Une caractéristique originale du modèle Fractal est qu’il permet de construire des com-
posants partagés. Un composant partagé est un composant qui est inclus dans plusieurs
composites. De façon paradoxale, les composants partagés sont utiles pour préserver l’en-
capsulation. En effet, il n’est pas nécessaire à un composant de bas niveau d’exporter
une interface au niveau du composite qui l’encapsule pour accéder à une interface d’un
composant partagé. De fait, les composants partagés sont particulièrement adaptés à la
modélisation des ressources.
Système de types
Assemblage de composants
Le langage Fractal ADL permet de décrire, à l’aide d’une syntaxe XML, des assemblages
de composants Fractal. Nous verrons à la section 3.3 que la DTD de ce langage n’est pas
fixe, mais peut être étendue pour prendre en compte des propriétés extra-fonctionnelles.
La figure 3.3 donne un exemple de définition réalisée à l’aide de Fractal ADL. L’as-
semblage correspond est représenté figure 3.2. Le composant décrit est un composite dont
le nom est HelloWorld. Ce composite possède une interface serveur, de nom r et de si-
gnature java.lang.Runnable. Par ailleurs, le composite encapsule deux composants :
Client et Server. La définition du composant Client est intégrée à celle du compo-
sant HelloWorld : le composant a deux interfaces (r et s), sa classe d’implantation est
org.objectweb.julia.example.ClientImpl et il possède une partie de contrôle de type
primitive1. La définition du composant Server suit le même principe : une interface ser-
veur s est définie et org.objectweb.julia.example.ServerImpl correspond à la classe
d’implantation. Enfin, la description ADL mentionne deux liaisons : entre les interfaces r
du composite et du Client et entre les interfaces s du Client et du Server.
1
Les deux mots utilisés pour décrire les parties contrôle des composants (primitive et composite) sont
définis dans un fichier de configuration qui permet de spécifier l’ensemble des contrôleurs des différents
composants.
54 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
Figure 3.3 – Un exemple de définition ADL à l’aide du langage extensible Fractal ADL.
import o r g . o b j e c t w e b . f r a c t a l . a p i . c o n t r o l . B i n d i n g C o n t r o l l e r ;
// I m p l e m e n t a t i o n de Runnable
public void run ( ) {
s e r v i c e . p r i n t ( ” H e l l o world ! ” ) ;
}
// I m p l e m e n t a t i o n de B i n d i n g C o n t r o l l e r
public S t r i n g [ ] l i s t F c ( ) { return new S t r i n g [ ] { ” s ” } ; }
public Object lookupFc ( S t r i n g c I t f ) {
i f ( c I t f . e q u a l s ( ” s ” ) ) { return s e r v i c e ; }
return null ;
}
public void bindFc ( S t r i n g c I t f , Object s I t f ) {
i f ( c I t f . equals (”s” ) ) { service = ( Service ) s I t f ; }
}
public void unbindFc ( S t r i n g c I t f ) {
i f ( c I t f . e q u a l s ( ” s ” ) ) { s e r v i c e = null ; }
}
private S e r v i c e s e r v i c e ;
}
public c l a s s S e r v e r I m p l implements S e r v i c e {
public void p r i n t ( S t r i n g msg ) {
System . e r r . p r i n t l n ( msg ) ;
}
}
La figure 3.4 fournit une implémentation des composants Client et Server mentionnés
dans l’assemblage. La classe ClientImpl correspond au composant Client et fournit une
implémentation pour l’interface r de type java.lang.Runnable. Par ailleurs, le composant
Client manipule sa liaison avec le composant Server : il doit pour cela implémenter
l’interface BindingController définie dans l’API Fractal. Le contrôleur de liaison notifiera
cette implémentation de l’occurrence de toute opération concernant la gestion des liaisons
de ce composant. Finalement, la classe ServerImpl implémente le composant Server.
3.1.2 Contrôleurs
Le modèle de composants Fractal n’impose la présence d’aucun contrôleur dans la
membrane d’un composant. Il permet, au contraire, de créer des formes arbitrairement
complexes de membranes implantant diverses sémantiques de contrôle. La spécification
Fractal [Bruneton et al. 2003] définit un certain nombre de niveaux de contrôle. En l’ab-
sence de contrôle, un composant Fractal est une boı̂te noire qui ne permet ni introspection,
ni intercession. Les composants ainsi construits sont comparables aux objets instanciés
dans les langages à objets comme Java. L’intérêt de ces composants réside dans le fait
qu’ils permettent d’intégrer facilement des logiciels patrimoniaux.
Au niveau de contrôle suivant, un composant Fractal fournit une interface Component,
similaire à l’interface IUnknown du modèle COM [Rogerson 1997]. Cette interface donne
accès aux interfaces externes (clients ou serveurs) du composant. Chaque interface a un
nom qui permet de la distinguer des autres interfaces du composant.
Au niveau de contrôle supérieur, un composant Fractal possède des interfaces réifiant
sa structure interne et permettant de contrôler son exécution. La spécification Fractal
définit différents contrôleurs :
– le contrôleur d’attributs pour configurer les attributs d’un composant.
– le contrôleur de liaisons pour créer/rompre une liaison primitive entre deux in-
terfaces de composants.
– le contrôleur de contenu pour ajouter/retrancher des sous-composants au contenu
d’un composant composite.
– le contrôleur de cycle de vie pour contrôler les principales phases comporte-
mentales d’un composant. Par exemple, les méthodes de base fournies par un tel
contrôleur permettent de démarrer et stopper l’exécution du composant.
Au delà de cet ensemble prédéfini par les spécifications, les développeurs peuvent
implémenter leurs propres contrôleurs pour étendre ou spécialiser les capacités réflexives
de leurs composants.
3.2 Plates-formes
Fractal est un modèle de composant indépendant des langages de programmation.
Plusieurs plates-formes sont ainsi disponibles dans différents langages de programmation.
Julia (voir section 3.2.1) l’implémentation de référence de Fractal, a été développée pour
le langage Java. Une deuxième plate-forme Java, AOKell, développée plus récemment est
présentée en section 3.2.2. Par rapport à Julia, AOKell apporte une mise sous forme de
56 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
composants des membranes. La section 3.2.3 présente un aperçu des autres plates-formes
existantes.
3.2.1 Julia
Julia est l’implémentation de référence du modèle de composant Fractal. Sa première
version remonte à juin 2002. Julia est disponible sous licence open source LGPL sur le site
du projet Fractal2 .
Julia est un canevas logiciel écrit en Java qui permet de programmer les membranes
des composants. Il fournit un ensemble de contrôleurs que l’utilisateur peut assembler. Par
ailleurs, Julia fournit des mécanismes d’optimisation qui permettent d’obtenir un conti-
nuum allant de configurations entièrement statiques et très efficaces à des configurations
dynamiquement reconfigurables et moins performantes. Le développeur d’application peut
ainsi choisir l’équilibre performance/dynamicité dont il a besoin. Enfin, notons que Julia
s’exécute sur toute JVM, y compris celles qui ne fournissent ni chargeur de classe, ni API
de réflexivité.
Objet Interface
membrane Objet Contrôleur
content
interface serveur
interface client
Intercepteur
Modèle Implémentation en Julia
les composants liés sont parents d’un même composite quand les contrôleurs de contenu
sont utilisés, etc.
Il n’est pas envisageable d’utiliser l’héritage de classe pour fournir ces différentes im-
plantations. En effet cela conduirait à une explosion combinatoire du nombre de classes
nécessaires. Supposons que l’on souhaite effectuer des vérifications concernant le système
de types, le cycle de vie et le contrôleur de contenu. Il existe 2 3 = 8 combinaisons possibles
de ces différentes vérifications. De fait, pour implanter toutes les combinaisons possibles,
il serait nécessaire de fournir huit classes, ce qui engendrerait de nombreuses duplications
de code.
La solution adoptée dans Julia est l’utilisation de classes mixin
[Bracha and Cook 1990] : une classe mixin est une classe dont la super-classe est
spécifiée de manière abstraite en indiquant les champs et méthodes que cette super-classe
doit posséder. La classe mixin peut s’appliquer (c’est-à-dire surcharger et ajouter des
méthodes) à toute classe qui possède les caractéristiques de cette super-classe. Ces classes
mixin sont appliquées au chargement à l’aide de l’outil ASM [ASM 2002]. Dans Julia,
les classes mixin sont des classes abstraites développées avec certaines conventions. En
l’occurrence, elles ne nécessitent pas l’utilisation d’un compilateur Java modifié ou d’un
pré-processeur comme c’est le cas des classes mixins développées à l’aide d’extensions
du langage Java. Par exemple la classe mixin JAM [Ancona et al. 2000] illustrée sur la
partie gauche de la figure 3.6 s’écrit en Julia en pur Java (partie droite). Le mot clé
inherited en JAM est équivalent au préfixe super utilisé dans Julia. Il permet de
spécifier les membres qui doivent être présents dans la classe de base pour que le mixin
lui soit appliqué. De façon plus précise, le préfixe super spécifie les méthodes qui
sont surchargées par le mixin. Les méthodes qui sont requises mais pas surchargées sont
spécifiées à l’aide du préfixe this .
L’application de la classe mixin A à la classe Base décrite sur la partie gauche de la
figure 3.7 donne la classe C55d992cb 0 représentée sur la partie droite de la figure 3.7.
58 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
Optimisations
Julia offre deux mécanismes d’optimisation, intra et inter composants. Le premier
mécanisme permet de réduire l’empreinte mémoire d’un composant en fusionnant une par-
tie de ses objets de contrôle. Pour ce faire, Julia fournit un outil utilisant ASM [ASM 2002]
et imposant certaines contraintes sur les objets de contrôle fusionnés : par exemple, deux
objets fusionnés ne peuvent pas implémenter la même interface.
Le second mécanisme d’optimisation a pour fonction d’optimiser les chaı̂nes de liaison
entre composants : il permet de court-circuiter les parties contrôle des composites qui n’ont
pas d’intercepteurs. Comme nous l’avons expliqué au paragraphe 3.2.1, chaque interface
serveur de composant est représentée par un objet qui contient une référence vers un
3.2. PLATES-FORMES 59
r1 r2 i2
3.2.2 AOKell
Comme Julia, le canevas logiciel AOKell [Seinturier et al. 2006] est une implémentation
complète des spécifications Fractal. Le respect de l’API Fractal permet ainsi d’exécuter
telle quelle, avec AOKell, des applications conçues pour Julia ou vice-versa. AOKell est
disponible sous licence open source LGPL sur le site du projet Fractal 3 . Le développement
de AOKell a débuté en décembre 2004.
Le canevas logiciel AOKell diffère de Julia sur deux points : l’intégration des fonc-
tions de contrôle dans les composants est réalisée à l’aide d’aspects et les contrôleurs sont
implémentés eux-mêmes sous forme de composants. Par rapport à Julia qui utilise un
mécanisme de mixin [Bracha and Cook 1990] et de la génération de bytecode à la volée
avec ASM, l’objectif d’AOKell est de simplifier et de réduire le temps de développement
de nouveaux contrôleurs et de nouvelles membranes.
La suite de cette section présente le principe de mise sous forme de composants des
membranes et la façon dont AOKell utilise les aspects.
Membranes componentisées
La membrane d’un composant Fractal est composée d’un ensemble de contrôleurs.
Chaque contrôleur est dédié à une tâche précise : gestion des liaisons, du cycle de vie,
etc. Loin d’être complètement autonomes, ces contrôleurs collaborent entre eux afin de
remplir la fonction qui leur est assignée. Par exemple, lors du démarrage d’un composite,
son contenu doit être visité afin de démarrer récursivement tous les sous-composants 4 . De
ce fait, le contrôleur de cycle de vie dépend du contrôleur de contenu. Plusieurs autres
dépendances de ce type peuvent être exhibées entre contrôleurs.
3
fractal.objectweb.org
4
Notons que cela ne constitue pas une obligation formelle. Il est tout à fait possible de concevoir une
fonction de contrôle pour laquelle le démarrage n’implique pas cette récursion.
60 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
La membrane la plus courante dans les applications Fractal est celle associée aux com-
posants primitifs. L’architecture de cette membrane est illustrée figure 3.10. Cette mem-
brane fournit cinq contrôleurs pour gérer le cycle de vie (LC), les liaisons (BC), le nommage
(NC), les références vers les composants parents (SC) et les caractéristiques communes à
tout composant Fractal (Comp).
L’architecture présentée figure 3.10 illustre le fait que la fonction de contrôle des compo-
sants primitifs n’est pas réalisée simplement par cinq contrôleurs isolés, mais est le résultat
de leur coopération. Comparée à une approche purement objet, l’implémentation des mem-
branes sous la forme d’un assemblage permet de décrire explicitement les dépendances entre
contrôleurs. Elle permet également d’aboutir à des architectures logicielles de contrôle
plus explicites, plus évolutives et plus maintenables. De nouvelles membranes peuvent être
développées en étendant les existantes, ou en en développement des nouvelles.
Le code Fractal ADL suivant fournit la description de la membrane de la figure 3.10.
3.2. PLATES-FORMES 61
La DTD permettant d’écrire cet assemblage et l’API Java permettant de le manipuler sont
exactement les mêmes que ceux utilisés pour les composants Fractal applicatifs.
<definition name="org.objectweb.fractal.aokell.lib.membrane.primitive.Primitive">
<controller desc="mComposite"/>
</definition>
Les modèles de composants comme EJB ou CCM fournissent des environnements dans
lesquels les composants sont hébergés par des conteneurs fournissant des services tech-
niques. Par exemple, les spécifications EJB définissent des services de sécurité, persistance,
transaction et de gestion de cycle de vie. La plupart du temps, cet ensemble de services
est fermé et codé en dur dans les conteneurs. Une exception notable est le serveur J2EE
JBoss [Fleury and Reverbel 2003] dans lequel les services peuvent être accédés via des as-
pects définis à l’aide du canevas logiciel JBoss AOP [Burke 2003]. De nouveaux services
peuvent être définis qui seront alors accédés à l’aide de leurs aspects associés.
L’idée générale illustrée par le serveur JBoss est que les aspects, tout en fournis-
sant un mécanisme pour modulariser les fonctionnalités transverses, permettent également
d’intégrer de façon harmonieuse de nouveaux services dans les applications. Cela illustre
également une des bonnes pratiques de la programmation orientée aspect : il est conseillé
de ne pas implémenter directement les fonctionnalités transverses dans les aspects, mais
d’y implémenter simplement la logique d’intégration (i.e. comment la fonctionnalité inter-
agit avec le reste de l’application) et de déléguer la réalisation concrète de la fonctionnalité
à un ou plusieurs objets externes. On obtient ainsi une séparation des préoccupations quasi
optimale entre la logique d’intégration et celle du service à intégrer.
Cette pratique est mise en œuvre dans AOKell : chaque contrôleur est associé à un
aspect chargé de l’intégration de la logique du contrôleur dans le composant. La lo-
gique d’intégration repose sur deux mécanismes : l’injection de code et la modification de
comportement. Le premier mécanisme est connu dans AspectJ, sous la dénomination de
déclaration inter-type (en anglais ITD pour Inter-Type Declaration). Avec ce mécanisme,
les aspects peuvent déclarer des éléments de code (méthodes ou attributs) qui étendent
la définition de classes existantes (d’où le terme ITD car les aspects sont des types qui
déclarent des éléments pour le compte d’autres types, i.e. des classes). Dans le cas d’AO-
Kell, les méthodes des interfaces de contrôle sont injectées dans les classes implémentant
les composants5 . Le code injecté est constitué d’une souche qui délègue le traitement à
l’objet implémentant le contrôleur.
Le second mécanisme, la modification de comportement, correspond en AspectJ à la
notion de code dit advice. Ainsi, la définition d’un aspect est constituée de coupes et de
code advice. Les coupes sélectionnent un ensemble de points de jonction qui sont des points
dans le flot d’exécution du programme autour desquels l’aspect doit être appliqué. Les blocs
de code advice sont alors exécutés autour de ces points. Le code advice est utilisé dans
AOKell pour intercepter les appels et les exécutions des opérations des composants. Par
exemple, le contrôleur de cycle de vie peut rejeter des invocations tant qu’un composant
n’a pas été démarré.
Avec les mécanismes d’injection de code et de modification de comportement, les
aspects intègrent de nouvelles fonctionnalités dans les composants et contrôlent leur
exécution. La réalisation concrète de la logique de contrôle est déléguée par l’aspect au
contrôleur implémenté sous la forme d’un objet.
5
Ce comportement par défaut peut être modifié afin, par exemple, de conserver des classes libres de
toute injection.
3.3. FRACTAL ADL 63
éléments dans les deux sections suivantes. La troisième section décrit le procédé d’extension
de l’ADL.
factory loader
(content not shown)
compiler builder
(content not shown) (content not shown)
scheduler
exemple, si le module interface est utilisé, l’API typée contient des méthodes permettant
de récupérer les informations sur les interfaces de composants (nom, signature, rôle, etc.).
Le composant loader est un composite encapsulant une chaı̂ne de composants primitifs
(figure 3.12). Le composant le plus à droite dans la chaı̂ne (basic loader) est responsable
de la création des AST à partir de définitions ADL. Les autres composants effectuent
des vérifications et des transformations sur les AST. Chaque composant correspond à un
module de l’ADL. Par exemple, le composant binding loader vérifie les liaisons déclarées
dans l’AST ; le composant attribute loader vérifie les attributs, etc.
argument basic
interface loader
loader loader
compiler builder
implementation implementation
compiler builder
main
compiler
component component
compiler builder
un composite qui encapsule plusieurs builders primitifs (figure 3.13). Chaque canevas lo-
giciel peut définir ses composants builder. Par exemple, Julia fournit actuellement quatre
composants builder qui permettent respectivement de créer des composants avec l’API
Java, l’API Fractal ou de produire du code source permettant d’instancier les composants
à partir de ces deux API.
public i n t e r f a c e L o g g e r C o n t r o l l e r {
S t r i n g getLoggerName ( ) ;
void setLoggerName ( S t r i n g l o g g e r ) ;
int getLogLevel ( ) ;
void s e t L o g L e v e l ( i n t l e v e l ) ;
}
L’extension du langage consiste à créer un module logger qui s’applique aux modules
de base containment en permettant de rajouter un élément logger dont la syntaxe est la
suivante :
Le développeur du module doit effectuer le travail suivant : (1) définition des interfaces
qui seront implantées par l’arbre abstrait lorsque le fichier XML aura été analysé par le
basic loader ; (2) écriture de “fragments” de DTD spécifiant que le module logger permet
3.4. BIBLIOTHÈQUES DE COMPOSANTS 67
3.4.1 Dream
Dans cette section, nous présentons Dream [Quéma 2005], une bibliothèque de com-
posants dédiées à la construction d’intergiciels orientés messages dynamiquement configu-
rables plus ou moins complexes : de simples files de messages distribuées à des systèmes
publication/abonnement complexes. Nous décrivons les éléments principaux du canevas
Dream et illustrons son utilisation par la ré-ingénierie de Joram, une implantation open
source de la spécification JMS. Nous montrons que la version réalisée à l’aide de Dream a
des performances comparables et offre un gain de configurabilité significatif.
Architecture d’un composant Dream Les composants Dream sont des composants
Fractal ayant deux caractéristiques : la présence d’interfaces d’entrée/sortie de messages
et la possibilité de manipuler les ressources rencontrées dans les intergiciels de communi-
cation : messages et activités.
– Les interfaces d’entrée et sortie de messages. Le canevas Dream définit deux inter-
faces permettant aux composants de s’échanger des messages. Une interface d’entrée
(input) permet à un composant de recevoir un message. Une interface de sortie
(output) permet à un composant d’émettre un message. Les messages sont toujours
transmis des sorties vers les entrées (figure 3.15 (a)). On distingue néanmoins deux
modes de transfert : push et pull. Dans le mode push, l’échange de message est initié
par la sortie qui est une interface client Push liée au composant auquel le message
est destiné (figure 3.15 (b)). Dans le mode pull, l’échange de message est initié par
l’entrée qui est une interface client Pull liée au composant émetteur du message
(figure 3.15 (c)).
Output
– Les gestionnaires de messages. Les messages sont gérés par des composants partagés,
appelés gestionnaires de messages (message managers). Ces composants permettent
de créer, détruire et dupliquer des messages 8 . Leur but est de gérer les ressources
mémoires consommées par les MOM. Pour ce faire, ils utilisent des réserves (pools)
de messages permettant de réduire le nombre d’allocations d’objets.
– Les gestionnaires d’activités. Dream distingue deux sortes de composants : les
composants actifs et les composants passifs. Les composants actifs définissent des
tâches à exécuter. Ces tâches permettent au composant de posséder son propre flot
d’exécution. Au contraire, les composants passifs ne peuvent effectuer d’appels sur
leurs interfaces clients que dans une tâche d’un composant appelant une de leurs in-
terfaces serveurs. Les tâches d’un composant actif sont accessibles par l’intermédiaire
d’un contrôleur spécifique, appelé contrôleur de tâches (task controller). Pour qu’une
tâche soit exécutée, il faut qu’elle soit enregistrée auprès d’un gestionnaire d’activités
(activity manager). Les gestionnaires d’activités sont des composants partagés qui
encapsulent des tâches et des ordonnanceurs (schedulers). Les ordonnanceurs sont
en charge d’associer des tâches de haut niveau à des tâches de bas niveau. Les tâches
de plus haut niveau sont les tâches applicatives (i.e. enregistrées par le MOM). Les
tâches de plus bas niveau encapsulent des threads Java. Ces concepts sont représentés
8
Les messages sont des objets Java encapsulant des chunks et des sous-messages. Chaque chunk est
également un objet Java implantant des accesseurs (getter) et des mutateurs (setter).
3.4. BIBLIOTHÈQUES DE COMPOSANTS 69
sur la figure 3.16. Les composants A et B ont enregistré trois tâches qui sont ordon-
nancées par un ordonnanceur FIFO. Celui-ci utilise pour cela deux tâches de bas
niveau.
Activity manager
taskController
Task A1 Task A2 Task B
taskManager
Component A
schedule
execute
schedulerManager
Third party Task Task
(thread) (thread)
SCServer SCServer
Engine Engine
12
/0 &
!"
!"
# %$
( ) & *
intervenir trois composants actifs pour un serveur d’agents avec un seul network. Il
est possible d’obtenir une implémentation mono-threadée en supprimant les files de
messages de l’engine et du network. Enfin, il est possible d’adapter la plate-forme à
des environnements contraints. Ainsi, [Leclercq et al. 2005a] présente une modifica-
tion du serveur d’agents permettant son déploiement sur un équipement mobile.
– Comparaison des performances. Des mesures de performances ont montré que les
performances obtenues par la ré-ingénierie à l’aide de Dream étaient tout à fait
comparables à celles obtenues par la plate-forme ScalAgent. Par ailleurs, ces mesures
ont montré qu’il était possible d’avoir des gains de performances significatifs en
configurant le MOM de façon adéquat. Par exemple, la réduction du nombre de
composants actifs peut permettre des améliorations de performances de l’ordre de
15% pour certaines applications.
3.5 Comparaison
De nombreux modèles de composants ont été proposés ces dix dernières années. Dans
le domaine des intergiciels, les propositions peuvent être classées selon qu’ils sont issus
d’initiatives industrielles, de la communauté du logiciel libre ou d’équipes de recherche
académiques.
3.6 Conclusion
Ce chapitre a présenté le système de composants Fractal, les principales plates-formes le
mettant en œuvre, le langage de description d’architecture et les bibliothèques permettant
de développer des systèmes à base de composants Fractal.
La section 3.1 est consacrée à la présentation du modèle de composant Fractal. C’est un
modèle hiérarchique au sens où les composants peuvent être soit primitif, soit composite et
contenir des sous-composants (primitifs ou composites). Deux parties sont mises en avant
dans un composant Fractal : le contenu et la membrane. Cette dernière fournit un niveau
méta de contrôle et de supervision du contenu. Elle est composée d’entités élémentaires,
les contrôleurs, qui implémentent des interfaces dites de contrôle. Le modèle de composant
Fractal est ouvert au sens où il ne présuppose pas un ensemble fini et figé de contrôleurs : de
nouveaux contrôleurs peuvent être ajoutés par les développeurs en fonction des besoins.
74 CHAPITRE 3. LE SYSTÈME DE COMPOSANTS FRACTAL
De même, la granularité des contrôleurs est quelconque, allant d’un simple service de
gestion de noms à des services plus complexes de gestion de persistance ou de transaction.
Un composant Fractal est une entité logicielle qui possède des interfaces fournies (dite
serveur) et/ou des interfaces requises (dites client). Le concept de liaison permet de définir
des chemins de communication entre interfaces clientes et serveurs. La granularité des
liaisons est également quelconque allant d’une simple référence dans l’espace d’adressage
courant à des liaisons plus complexes mettant en œuvre des mécanismes de communication
distante.
Le modèle Fractal est indépendant des langages de programmation. La section 3.2
a présenté deux plates-formes, Julia et AOKell, mettant en œuvre ce modèle pour le
langage Java. D’autres plates-formes existent pour les langages Smalltalk, C, C++ et les
langages de la plate-forme .NET. Julia est la plate-forme de référence du modèle Fractal.
Le développement des contrôleurs se fait à l’aide d’un système de classes mixin. AOKell
a été développé par la suite et apporte une approche à base de composants de contrôle
pour le développement des membranes. Celles-ci sont des assemblages de composants de
contrôle qui gèrent et administrent les composants du niveau de base. Les composants
de contrôle sont eux-mêmes des composants Fractal qui sont contrôlés de façon ad-hoc.
Finalement, AOKell utilise des techniques issues de la programmation orientée aspect pour
l’intégration des niveaux de base et de contrôle.
L’API Fractal permet de construire d’assembler des composants élémentaires afin de
construire des applications complètes. Le langage de description d’architecture Fractal
ADL (voir section 3.3) permet de décrire ces architectures de façon plus concise qu’avec
une simple API. Fractal ADL est un langage ouvert basé sur XML. Contrairement à
nombre d’ADL existants, la DTD de Fractal ADL n’est pas figée et peut être étendue
avec de nouvelles balises permettant d’associer des caractéristiques supplémentaires aux
composants. Un mécanisme d’extension permet de définir les traitements à exécuter lorsque
ces nouvelles balises sont rencontrées. Notons enfin que l’outil Fractal ADL d’analyse et
d’interprétation d’un assemblage est lui même une application Fractal (donc un assemblage
de composant Fractal).
Plusieurs bibliothèques de composants sont disponibles pour faciliter la tâche des
développeurs Fractal. Nous en avons présentées quelques unes dans la section 3.4, dont
Dream, qui permet de développer des intergiciels. Nous aurions pu également mentionner
les outils qui existent autour de Fractal, comme Fractal GUI qui permet de concevoir gra-
phiquement une architecture de composants et de générer des squelettes de code, Fractal
Explorer [Merle et al. 2004] qui est une console graphique d’administration d’applications
Fractal, Fractal RMI qui permet de construire des assemblages de composants distribués
communicants via un mécanisme d’invocation de méthodes à distance et Fractal JMX pour
l’administration de composants à l’aide de la technologie JMX.
Après une première version stable diffusée en juillet 2002, la version 2 des spécifications
Fractal parue en septembre 2003 a permis d’en consolider l’assise. Fractal est maintenant
une spécification stable et mature grâce à laquelle de nombreux systèmes et applications ont
pu être développés. Par ailleurs, de nombreuses activités de recherche sont conduites autour
de Fractal. Sans être exhaustif, nous pouvons citer les travaux sur les approches formelles
et les calculs de composants, la vérification de comportements, la sécurité et l’isolation
des composants, les systèmes autonomiques, l’unification des styles de développement à
3.6. CONCLUSION 75
Chapitre 4
the Web service in a manner prescribed by its description using SOAP 2 mes-
sages, typically conveyed using HTTP with an XML serialization in conjunc-
tion with other Web-related standards.
D’autres définitions similaires adoptent un point de vue plus général en ne prescrivant
pas l’usage exclusif de WSDL pour la description des interfaces de service ou celui de
SOAP pour le traitement des messages 3 . Ainsi par exemple, certains auteurs proposent
l’utilisation de messages XML (sans les extensions apportées par SOAP) échangés directe-
ment sur le protocole HTTP. De façon similaire, la définition officielle du consortium W3C
fait spécifiquement référence au protocole HTTP, mais en pratique, d’autres protocoles
sont aussi utilisés (voir Section 4.4).
Une étude plus approfondie des points communs partagés par les différentes définitions
et par les usages qui sont faits des services Web, permet de dégager au moins deux principes
fondamentaux :
– Les services Web interagissent au travers d’échanges de messages encodés en XML.
Dans certains cas, les messages sont plutôt transmis dans un encodage binaire, même
s’ils sont généralement produits et consommés en XML ou en utilisant des interfaces
de programmation basées sur les concepts d’élément et d’attributco définis par le
modèle dit « XML Infoset ».
– Les interactions dans lesquelles les services Web peuvent s’engager sont décrites
au sein d’interfaces. La définition W3C restreint la portée des interfaces des ser-
vices Web aux aspects fonctionnels et structurels, en utilisant le langage WSDL qui,
essentiellement, ne permet de décrire que des noms d’opérations et des types de
messages. Cependant, d’autres auteurs proposent d’aller plus loin et de considérer
aussi les aspects comportementaux et non-fonctionnels comme nous le verrons dans
la section 4.3.
Par rapport à d’autres plates-formes pour le développement d’applications distribuées
telles que CORBA (voir le chapitre 1) et Java RMI (voir le chapitre 5), l’une des différences
primordiales est que les services Web n’imposent pas de modèles de programmation
spécifiques. En d’autres termes, les services Web ne sont pas concernés par la façon dont
les messages sont produits ou consommés par des programmes. Ceci permet aux vendeurs
d’outils de développement d’offrir différentes méthodes et interfaces de programmation
au-dessus de n’importe quel langage de programmation, sans être contraints par des stan-
dards comme c’est le cas de CORBA qui définit des ponts spécifiques entre le langage
de définition IDL4 et différents langages de programmation. Ainsi, les fournisseurs d’ou-
tils de développement peuvent facilement différencier leurs produits avec ceux de leurs
concurrents en offrant différents niveaux de sophistication. Par exemple, il est possible
d’offrir des environnements pour des méthodes de programmation de services Web mini-
malistes au-dessus de plates-formes relativement légères comme c’est le cas de NuSOAP
(pour le langage PHP) ou d’Axis (voir la section 4.4). En revanche, des plates-formes plus
lourdes telles que BEA WebLogic, IBM Websphere ou .Net (voir le chapitre 6) offrent un
environnement pour des méthodes de développement très sophistiquées.
2
Simple Object Access Protocol
3
Il faut noter que WSDL et SOAP sont des standards définis par le consortium W3C, ce qui explique
que la définition ci-dessus essaye d’imposer ces standards.
4
Interface Definition Language
4.2. MODÉLISATION DE SERVICES WEB 79
Les principes à la base des services Web, bien que simples, expliquent leur adoption
rapide : le nombre d’outils associés au services Web a connu un essor considérable dans
une période de temps réduite, assurant ainsi que les technologies associées aux services
Web seront utilisées pour de nombreuses années à venir.
La description explicite des interactions entre les services Web, est aussi un point fort
des services Web. Cependant, des efforts de recherche et de développement plus approfondis
sont nécessaires afin de réellement exploiter ce principe dans des outils ou des méthodes
de développement. En particulier, comme nous le discuterons dans la section 4.3, l’usage
des descriptions comportementales et non-fonctionnelles des services requièrent davantage
d’attention de la part de la communauté de recherche.
dans lesquels les actions correspondent à des interactions entre des rôles correspondant
à des types de service (client, fournisseur et entrepôt en l’occurrence). Pour simplifier,
pour chaque échange de message le diagramme montre soit l’envoi de ce message, soit la
réception, mais pas les deux. Par exemple, dans l’activité « Commande », nous faisons
référence à l’envoi du Bon de Commande (BdC) par le client au fournisseur. Bien entendu,
le fournisseur doit exécuteur l’action correspondant à la réception de ce message, mais
nous ne présentons pas cette action duale dans le diagramme d’activités.
Il est à remarquer aussi que les diagrammes d’activité permettent de modéliser des
aspects comportementaux, et en particulier des flots de contrôle. Ils ne permettent pas
cependant de modéliser des aspects structurels tels que les types de données décrivant le
contenu des messages échangés. Ceux-ci peuvent être décrits au travers de diagrammes de
classe mais nous omettons ces détails car ils ne contribuent pas à la compréhension du
concept général de chorégraphie.
Acceptation Rejet
à client] à client]
Paiement Facturation
Confirmation de Livraison
[envoyer copie d’ordre de
[envoyer facture à client]
transfert à fournisseur] [recevoir avis de livraison du client]
Il faut noter que la chorégraphie présentée dans la figure 4.1 n’adopte le point de vue
d’aucun des services (ou plus précisément des rôles) concernés. En effet, cette chorégraphie
4.2. MODÉLISATION DE SERVICES WEB 81
inclut des interactions entre le client et le fournisseur, entre le fournisseur et son entrepôt,
et entre l’entrepôt et le client. Lorsqu’on passe à des étapes ultérieures dans le cycle de
développement de services, on s’intéresse à voir des interactions du point de vue d’un
service spécifique, comme par exemple un service censé jouer le rôle de fournisseur dans
l’exemple en question. Dans ce cas, on utilise la notion d’interface (voir chapitre 2). Une
interface de service décrit un ensemble d’interactions dans lesquelles un service donné peut
ou doit s’engager. Le concept d’interface est dérivé des langages de programmation et des
intergiciels à base d’appels de procédures tels que CORBA. Mais alors que traditionnel-
lement le concept d’interface est associé surtout à une description structurelle (c’est-à-
dire une description des paramètres d’une opération), dans le cadre des services Web on
s’intéresse à des descriptions plus riches, incorporant des aspects comportementaux. A
titre d’exemple, l’interface correspondant au rôle de fournisseur dans la chorégraphie de
la figure 4.1 est représentée sur la figure 4.2. Dans cette dernière figure, chaque élément
de la séquence correspond à une réception (par exemple Recevoir bon de commande) ou un
envoi de message (par exemple Envoyer demande de disponibilité). L’ensemble des types
de messages échangés modélisent la dimension structurelle de l’interface alors que le flot
de contrôle exprimé par la séquence modélise la dimension comportementale de l’interface.
Figure 4.2 – Interface correspondant au rôle de fournisseur dans la chorégraphie de la figure 4.1.
Selon que l’on se réfère à l’interface qu’un service existant est capable de fournir, ou
de l’interface qu’un service est censé fournir dans le cadre d’une chorégraphie, on parle
d’interface fournie ou d’interface requise (voir chapitre 2). L’interface présentée dans la
figure 4.2 correspond à l’interface requise du fournisseur, dérivée de la chorégraphie donnée
dans la figure 4.1. Parfois, l’interface d’un service existant est identique ou compatible avec
82 CHAPITRE 4. LES SERVICES WEB
l’interface requise pour participer à une chorégraphie. Cependant, ceci n’est pas toujours
le cas. A titre d’exemple, considérons le cas où l’interface représentée sur la figure 4.3
est l’interface fournie par un service que l’on voudrait utiliser pour remplir le rôle de
fournisseur dans la chorégraphie de la figure 4.1. Dans ce contexte, l’interface fournie diffère
de l’interface requise en deux points. Premièrement, alors que dans l’interface requise le
service peut répondre par une acceptation ou un rejet, dans l’interface fournie il répond
avec le même type de message quel que soit le cas. Deuxièmement, dans l’interface requise,
l’ordre d’expédition et la facture sont envoyés dans n’importe quel ordre, alors que dans
l’interface requise l’envoi de la facture et la réception de l’ordre de paiement précèdent
l’envoi de l’ordre d’expédition. Dans ce contexte, il est nécessaire de réconcilier ces deux
interfaces. Ce problème est connu sous le nom d’adaptation de services et fait l’objet de
plusieurs efforts de recherche (voir section 4.5.1).
Réponse négative
Réponse positive
Envoyer facture
Réponse négative
Réponse positive
Vérifier paiement
figure 4.1, il est possible de dériver les interfaces requises des services fournisseur,
client et entrepôt.
– Le contrôle, au moment de la conception, de la cohérence de l’interface existante
d’un service avec le modèle de chorégraphie auquel il participe. Ainsi, la capacité du
service à assumer le rôle qu’il est sensé jouer dans cette chorégraphie est vérifiée.
– La génération d’une ébauche d’un modèle d’orchestration pour chaque participant.
Cette ébauche peut ensuite être détaillée dans la perspective, pour chaque partici-
pant, de remplir son rôle.
Pour une définition formelle des concepts de chorégraphie, d’interface et d’orchestra-
tion, le lecteur peut se référer à [Dijkman and Dumas 2004]. Il faut noter que ces concepts
apparaissent parfois avec des noms différents dans la littérature. Par exemple, Casati et
al. [Alonso et al. 2003] utilisent le terme « protocole de collaboration »pour se référer au
concept de chorégraphie. De façon similaire, en ebXML [UN/CEFACT and OASIS 1999],
une famille de standards pour le développement de services pour le commerce électronique,
l’interface fournie est appelée « profil de protocole de collaboration »(collaboration pro-
tocol profile en anglais) alors que l’interface requise est appelée « accord de protocole de
collaboration »(collaboration protocol agreement).
Le lecteur intéressé par les problèmes liés à la détection d’incompatibilités
entre interfaces fournies et interfaces requises peut se référer à [Martens 2005]. La
résolution de telles incompatibilités fait l’objet de recherches en cours (voir par
exemple [Benatallah et al. 2005a, Fauvet and Ait-Bachir 2006]).
de services s’appuient aujourd’hui sur UDDI et le futur de ce standard est incertain. Par
conséquent, nous ne détaillons pas UDDI dans ce chapitre, malgré les associations souvent
faites entre UDDI et la problématique de la description de services.
<?xml version="1.0"?>
<definitions name="RecevoirBdC"
targetNamespace="http://www.yothuyindi.fr:8080/exemple/fournisseur.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema/"
xmlns:wns="http://www.yothuyindi.fr:8080/exemple/fournisseur.wsdl"
xmlns:xsdl="http://www.yothuyindi.fr:8080/exemple/fournisseur.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
Les types de données, paramètres d’entrée et/ou de sortie sont éventuellement décrits
ensuite en XMLSchema [W3C-XMLSchema ]. La description d’une commande est décrite
ci-dessous comme une date et une liste non vide de produits à commander, chacun dans
une quantité non nulle donnée.
<types>
<schema targetNamespace="http://www.yothuyindi.fr:8080/exemple/fournisseur.xsd">
<xsd:complexType name="Commande"><xsd:sequence>
<xsd:element name="dateCommande" type="xsd:date">
<xsd:element name="LigneDeCommande" minOccurs="1" maxOccurs="unbounded">
<xsd:complexType><xsd:sequence>
<xsd:element name="RéférenceProduit" type="xsd:string"/>
<xsd:element name="Quantité" type="xsd:positiveInteger"/>
</xsd:sequence></xsd:complexType></xsd:element>
</xsd:sequence></xsd:complexType>
86 CHAPITRE 4. LES SERVICES WEB
...</schema>
</types>
<message name="SoumissionBdC">
<part name="body" element="xsdl:Commande"/>
</message>
<message name="RécépisséBdC">
<part name="body" element="xsd:string"/>
</message>
Les opérations offertes par le service sont exposées par le biais de points d’entrée. Un
point d’entrée (élément portType) fournit la signature de chaque opération et doit par la
suite être associé à une implantation particulière (voir plus loin la description de la partie
binding). WSDL permet l’existence de plusieurs points d’entrée dans un même document.
Ainsi, la même opération peut être rendue disponible au travers d’implantations différentes.
<portType name=pt_RecevoirBdC>
<operation name="op_Commande">
<input message="wsn:SoumissionBdC">
<ouput message"=wsn:RécépisséBdC">
</operation>
<operation name=...
...
</portType>
Dans la description ci-dessus les valeurs des attributs message font référence à un
message défini plus haut (élément message du document WSDL).
La définition de la signature d’une opération s’appuie sur trois sous-éléments possibles :
input pour le message en entrée, output pour celui en sortie et fault pour un message
d’erreur émis par le service. Les combinaisons possibles de ces sous-éléments permettent
de décrire divers modèles d’opérations :
– Réception de message (sous-élément input) : le service reçoit un message envoyé par
un client ;
– Réception - émission (sous-élément input suivi de output et éventuellement de
fault) : le service reçoit une requête sous forme d’un message puis émet une réponse
sous forme d’un retour de message. C’est le modèle classique RPC (Remote Procedure
Call, voir chapitre 1) ;
– Émission - retour (sous-élément output suivi de input et éventuellement de fault) :
le service envoie une requête à un client (suite à une inscription préalable) sous forme
d’un message ; le client répond sous forme d’un message de retour.
– Émission (sous-élément output) : le service envoie un message (d’alerte par exemple)
à un client.
4.3. DESCRIPTION DE SERVICES WEB 87
<service name=ServiceFournisseur>
<documentation> Service de réception des commandes </documentation>
<port name="PortSoumissionCommande" binding="bd_opCommande">
<soap:address location="//www.yothuyindi.fr/exemple/fournisseur"/>
</port>
</service>
<!-- fermeture de la balise racine -->
</wsdl:definitions>
Le document final est obtenu par la concaténation des extraits fournis ci-dessus.
Comme le suggère l’exemple traité ici, la production de documents WSDL est un
travail fastidieux dont le programmeur peut être déchargé grâce à l’utilisation d’outils
pour la génération automatique de la description WSDL. Ces outils prennent en entrée
la description du service sous forme d’une classe Java (ou une archive jar) ou d’un objet
COM.
<sequence>
<receive operation="BdC"/>
...
<while ...>
<invoke operation="reponse-a-BdC"/>
</while>
...
<onMessage operation="annulation-de-BdC" ...>
<throw faultName="annulationEnCours"/>
</onMessage>
5
Voir http ://www.rosettanet.com.
4.3. DESCRIPTION DE SERVICES WEB 89
...
<catch faultName="annulationEnCours">
<invoke operation="RecepisseAnnulation-de-BdC" .../>
</catch>
...
</sequence>
Dans cet extrait, on distingue deux types d’actions communicationnelles : receive pour
la réception, et invoke pour l’envoi. Ces actions sont composées en utilisant les opérateurs
de séquencement et d’itération (sequence and while) que l’on retrouve dans les langages
de programmation impératifs. En outre, à tout moment (clause onMessage) un message de
type annulation-de-BDC peut être reçu et alors une exception est levée et ensuite attrapée
au travers des constructions throw et catch que l’on retrouve aussi dans des langages
de programmation. On peut remarquer que le code BPEL ci-dessus se limite à décrire
des actions communicationnelles et leurs dépendances. Si l’on voulait utiliser BPEL pour
implanter le service correspondant, beaucoup plus de détails seraient nécessaires.
En tant que langage d’implantation de services, BPEL est incorporé dans plusieurs
outils de construction d’applications à base de services (voir Section 4.4). Cependant, en
tant que langage de description d’interfaces, l’outillage autour de BPEL est presqu’in-
existant. Quelques travaux de recherche montrent comment des interfaces comportemen-
tales décrites en BPEL peuvent être utilisées pour la vérification statique [Fu et al. 2004]
ainsi que pour le suivi et analyse d’exécutions de services, que ce soit en temps
réel [Baresi et al. 2004] ou a posteriori [Aalst et al. 2005]. Cependant, ces techniques n’ont
pas encore été adoptées dans des outils commerciaux.
Ce dernier commentaire s’applique également à WS-CDL, qui essaye d’aller plus
loin que BPEL en s’attaquant non seulement à la description d’interfaces comportemen-
tales, mais aussi à la description de chorégraphies à différents niveaux de détails, allant
de la modélisation conceptuelle jusqu’à l’implantation. L’idée de WS-CDL est que les
chorégraphies décrites dans un langage semi-exécutable, peuvent être vérifiées statique-
ment, testées par simulation, et dans le cas des chorégraphies définies au niveau le plus
détaillé elle peuvent être utilisées pour générer automatiquement les interfaces correspon-
dant à chacun des rôles mis en jeu. A ce jour, les outils de développement de services basés
sur WS-CDL sont très loin d’avoir atteint leur maturité 6 . Pour un aperçu et une analyse
critique de WS-CDL, voir [Barros et al. 2005b].
une certaine qualité de service par rapport à des préoccupations telles que la sécurité, la
fiabilité, la journalisation des accès ou la gestion de transactions (voir le chapitre 1).
Ce manque est en partie compensé par le concept de politiques d’usage. Une poli-
tique d’usage est une énonciation explicite des possibilités et des restrictions d’usage d’un
service Web. « WS-Policy »est un langage extensible permettant d’exprimer des poli-
tiques (ou règles) d’usage sous forme de conjonctions et de disjonctions (au sens logique)
d’assertions [Kaler and Nadalin ]. Dans ce contexte, une assertion est une donnée par la-
quelle un service exprime qu’il permet aux clients (ou qu’il requiert des clients) de procéder
d’une certaine manière lorsqu’ils accèdent aux opérations du service. « WS-Policy »ne
définit pas des types d’assertions d’usages particuliers. Cependant, ces types d’assertions
sont définis par d’autres standards proposés tels que « WS-Security-Policy », « WS-
Reliable-Messaging »et « WS-Addressing », qui définissent respectivement des types d’as-
sertion liés à la sécurité, la fiabilité, et l’adressage. Ainsi, en utilisant les types d’asser-
tion définis dans « WS-Security-Policy », il est possible d’exprimer qu’un service requiert
que tous les messages soient signés ou qu’ils soient signés et encryptés avec une clé pu-
blique donnée. De même, en utilisant les types d’assertion définis dans « WS-Reliable-
Messaging », on peut exprimer qu’un service donné opère selon un protocole de renvoi
de messages permettant d’assurer que les messages arrivent au moins une fois, ou exacte-
ment une fois. Enfin, les types d’assertion définis dans « WS-Addressing »permettent par
exemple d’exprimer qu’un service peut envoyer le résultat d’un appel d’opération à une
entité différente de celle ayant initié l’appel.
« WS-Policy »constitue un pas en avant vers des modèles de développement d’applica-
tions réparties basées sur des descriptions détaillées couvrant des aspects aussi bien fonc-
tionnels que non-fonctionnels. Cependant, pour réaliser la vision de services Web comme
entités indépendantes, davantage d’efforts de recherche sont nécessaires afin de déboucher
sur des modèles et des langages permettant de décrire l’environnement organisationnel
dans lequel les services Web évoluent. Par exemple, pour faciliter l’adoption des services
Web dans des applications de type business-to-business, il serait souhaitable d’avoir des
descriptions de services couvrant des garanties de disponibilité et de temps de réponse, ainsi
que des politiques de prix et de pénalités, de modalités de paiement, de réputation, etc.
(voir [O’Sullivan et al. 2002]). Il ne faut pas s’attendre à ce que toutes ces propriétés fassent
l’objet de standardisation ou soient prises en compte dans des outils de développement de
services car la plupart d’entre elles touchent à des aspects socio-techniques et varient selon
les domaines d’application. Cependant, de telles descriptions non-fonctionnelles pourraient
constituer un outil d’évaluation et de sélection lors des phases initiales (c’est-à-dire au cours
de l’analyse du domaine) dans la construction d’applications à base de services.
Client
Service Application
appels de méthodes
moteur SOAP moteur SOAP
requête SOAP
Fournisseur
La figure 4.5 schématise un scénario d’interaction entre les partenaires Client et Four-
nisseur déjà introduits dans la section 4.2 (les interactions avec l’entrepôt ne sont pas
montrées). Le client transmet une commande au fournisseur en lui envoyant un bon de
commande. Ce bon de commande est transmis, par une application du client, sous forme
d’une requête SOAP. Chez le fournisseur, le serveur d’application reçoit la requête (sous
forme d’une requête HTTP) et la transmet au moteur SOAP. Ce dernier décode le message
reçu et effectue les appels aux procédures correspondantes de l’application locale. Lorsque
les vérifications de disponibilité des articles commandés et les interactions avec l’entrepôt
sont terminées, l’application du fournisseur transmet, de la même manière, la facture au
serveur d’application du client (les serveurs d’application ne sont pas figurés).
Il existe plusieurs mécanismes pour construire, analyser, et échanger des messages
SOAP dans des langages variés tels que Java, C++, Perl, C], etc. Ces implantations
permettent de générer les en-têtes de messages SOAP et de mettre en correspondance le
contenu du corps du message avec les structures de données définies dans le langage hôte
(voir Section 4.4.4).
SOAP. Un fournisseur peut en théorie exprimer que son service est capable de recevoir
des en-têtes X, Y, Z, avec une certaine sémantique pour chacun, et les programmeurs des
applications qui accèdent à ce service sont alors libres de les utiliser ou pas. De façon
symétrique, une application peut envoyer un message SOAP avec n’importe quel ensemble
d’en-têtes, et c’est au service destinataire de déterminer ceux qu’il peut interpréter et ceux
qu’il peut ignorer. L’attribut XML mustUnderstand peut être associé à chaque en-tête pour
exprimer si cet en-tête doit obligatoirement être interprété par le destinataire ou si il peut
être ignoré. Le destinataire est tenu de renvoyer un message d’erreur s’il détecte un en-tête
qui devrait être interprété obligatoirement, et qu’il ne lui est pas possible d’interpréter.
Différentes spécifications (certaines en passe de devenir des standards), connues généra-
lement sous le nom collectif WS-*, définissent des ensembles d’en-têtes SOAP perçus
comme étant particulièrement importants pour un grand nombre d’applications. Trois
des spécifications les plus avancées en terme de leur standardisation sont WS-Addressing
(pour l’adressage), WS-Security (pour l’authentification et la non-répudiation) et WS-
Reliable-Messaging (pour le renvoi répété de messages afin de garantir leur livraison fiable
et ordonnée). Par exemple, WS-Addressing définit les (sous-)en-têtes suivants :
– MessageID : où l’on peut associer un identificateur au message.
– RelatesTo : où l’on peut faire référence à l’identificateur d’un message antérieur.
– From, To : qui fixent l’expéditeur et le destinataire du message.
– Reply-to : qui donne l’adresse de retour, c’est-à-dire l’URL à laquelle des réponses
éventuelles au message doivent être envoyées. Cet en-tête est utile par exemple
lorsque la réponse à une requête est envoyée au travers d’une deuxième connexion
HTTP établie ultérieurement (selon le mode d’interaction dit asynchrone), au lieu
d’être incluse dans le message de retour de la connexion HTTP courante (selon le
mode d’interaction dit synchrone).
A titre d’exemple, le message HTTP suivant correspond à l’envoi d’un bon de com-
mande par un client, d’après l’interaction décrite dans la section 4.2. Ce message inclut un
identificateur de message et une adresse à laquelle l’éventuelle réponse doit être expédiée
ultérieurement.
</Address>
</ReplyTo>
</soapenv:Header>
<soapenv:Body>
<BonDeCommande> ... </BonDeCommande>
</soapenv:Body>
</soapenv:Envelope>
La réponse du fournisseur peut alors être envoyée au travers d’une deuxième connexion
HTTP. Cette réponse doit être envoyée à l’adresse indiquée dans l’en-tête « ReplyTo »du
message ci-dessus, et doit se référer à l’identificateur du message du message ci-dessus au
travers de l’en-tête « RelatesTo ».
Un exemple de message de réponse est donné ci-dessous :
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<RelatesTo xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing">
bpel://www.yothuyindi.fr/default/Customer~1.1/301-BpInv0-BpSeq0.3-3
</RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ReponseBonDeCommande>
...
</ReponseBonDeCommande>
</soapenv:Body>
</soapenv:Envelope>
Cette approche, ainsi que d’autres caractéristiques de SOAP, ont fait l’objet de nom-
breuses critiques. Les arguments principaux mis en avant contre SOAP reposent sur le
fait que : (i) il rajoute peu de fonctionnalités au-dessus de ce qu’il est déjà possible de
faire avec HTTP et XML (sans les extensions apportées par SOAP) ; et (ii) en associant
plusieurs opérations à une même URL, il rend difficile, voire impossible, l’utilisation de
l’infrastructure de « caching »associée au protocole HTTP, qui constitue sans doute l’un
des points forts de HTTP.
Une approche alternative pour l’implantation de services Web appelée REST (RE-
presentational State Transfer) a été définie [Fielding 2000]. Dans cette approche, chaque
opération d’un service est associée à une URL distincte et l’accès à chaque URL peut être
réalisé en utilisant l’une des quatre méthodes fournies par HTTP : POST, GET, PUT
et DELETE. Le contenu des messages est alors encodé en XML, et la distinction entre
en-tête et contenu de message est laissée à la charge des applications qui requièrent cette
distinction. Le résultat est un ensemble de conventions plus simples que celles de SOAP, et
la possibilité d’utiliser des bibliothèques existantes pour l’échange de messages sur HTTP,
éliminant ainsi le besoin de plates-formes implantant SOAP, qui dans certains cas peuvent
être considérées comme étant plus « lourdes »et plus difficiles à utiliser.
Plusieurs services Web populaires disponibles à l’heure actuelle utilisent l’ap-
proche REST (appelée aussi « XML sur HTTP »). Ceci est le cas notamment
de la maison de ventes au enchères en ligne eBay, qui rend une partie de
ses fonctionnalités, accessibles sous forme de services Web « style REST »(voir
http ://developer.ebay.com/rest). Il en est de même du site de vente par Internet
Amazon (voir http ://www.amazon.com/webservices).
Le débat SOAP vs. REST est encore ouvert et les avis divergent considérablement
dans la communauté. Les avantages et désavantages relatifs de SOAP et REST peuvent
être résumés comme suit :
– REST ne requiert pas d’infrastructure spécifique pour le développement et
l’exécution des services Web. Il repose sur l’infrastructure HTTP existante, qui a
fait ses preuves dans le contexte des applications HTML classiques. De nombreux
outils de programmation d’applications au-dessus de HTTP existent, et peuvent être
combinés avec des outils de manipulation de documents XML pour construire des
services Web « style REST ». Dans le cas de SOAP, il est nécessaire d’utiliser des
outils spécifiques (voir la section 4.4.4) qui sont parfois difficiles à déployer et n’ont
pas complètement fait leurs preuves.
– SOAP peut opérer au-dessus d’autres protocoles que HTTP, en particulier sur des
protocoles permettant des communications asynchrones telles que SMTP, JMS,
MSMQ et MQSeries.
– Lorsqu’il est utilisé en conjonction avec des spécifications WS-*, SOAP permet de
prendre en compte des aspects liés à la sécurité, la fiabilité et l’adressage et routage
de messages. Bien sûr, il serait possible de faire de même en utilisant l’approche
REST, mais les standards et l’infrastructure correspondant ne sont pas en place.
En conclusion, l’approche REST est viable (voire préférable) dans le cas de services
Web avec les caractéristiques suivantes : (i) ils n’ont besoin d’être exposés que sur HTTP
ou HTTPS ; (ii) les communications asynchrones (et donc l’adressage explicite) ne sont
pas requises ; (iii) ils ne requièrent pas de garanties de sécurité au delà de celles offertes
96 CHAPITRE 4. LES SERVICES WEB
par HTTPS ; et (iv) la fiabilité peut facilement être traitée au niveau des applications.
BPELJ [Blow et al. 2004], une extension de BPEL avec laquelle il est possible d’intégrer du
code Java dans un programme BPEL. Cette approche est similaire à celle des JSPs (Java
Server Pages) où du code Java est inclus dans des programmes HTML. La considération
de BPEL dans les deux plates-formes .Net et Java laisse à penser que d’autres dialectes
proches de BPEL vont émerger.
Ci-dessous, nous fournissons un squelette du processus exécutable BPEL correspon-
dant au service « fournisseur »modélisé dans la figure 4.2. Ce squelette montre comment
les opérateurs de flot de contrôle sequence, switch et flow sont combinés pour expri-
mer l’ordre dans lequel les messages sont échangés entre le fournisseur d’une part, et le
client et l’entrepôt d’autre part. En particulier, les actions envoyerBordereauExpédition et
recevoirOrdreDeTransfert peuvent s’exécuter en parallèle ou dans n’importe quel ordre
puisqu’elles sont emboı̂tées dans une activité de type flow. Le squelette montre aussi
comment BPEL est lié avec WSDL : les types et les opérations définis en WSDL dans
la section 4.3.1 sont utilisés ici pour définir les types des variables et les types des mes-
sages envoyés et reçus par les actions receive, reply et invoke. De plus, les expéditeurs
et destinataires des messages envoyés sont spécifiés par le biais des liens de communica-
tion (« partner links ») qui devront être associés aux services décrits dans la description
WSDL.
<process name="ProcessusFournisseur">
<partnerLinks>
<partnerLink name="client" ... />
<partnerLink name="entrep^
ot" ... />
</partnerLinks>
<variables>
<variable name="BdC" type="xsld:Commande"/>
<variable name="réponseBDC" messageType= "string" />
<variable name="disponibilité" type="string"/>
</variables>
<sequence>
<receive name="recevoirBdC"
partnerLink="client" portType="pt_Commande"
operation="op_Commande" variable="BdC"
createInstance="yes"/>
<invoke name="demanderDisponibilité"
partnerLink="entrep^ot"
...
inputVariable="BdC"
outputVariable="disponibilité"/>
<switch>
<case ....> <!-- cas disponible -->
<!-- Initialiser la variable réponseBdC avec réponse positive -->
<reply name="RépondreBdC"
partnerLink="client"
portType="pt_Commande"
operation="op_Commande"
inputVariable="réponseBDC"/>
<flow>
<invoke name="envoyerBordereuExpédition" .../>
<receive name="recevoirOrdreDeTransfert" .../>
4.5. PERSPECTIVES 99
</flow>
</case>
<case ...> <!-- cas indisponible -->
<!-- Initialiser la variable réponseBdC avec réponse négative -->
...
</case>
</switch>
</sequence>
</process>
Bien que BPEL offre des constructions spécifiques pour le développement de services
Web, sa complexité pose problème. Comme le montre [Wohed et al. 2003] l’opérateur link
est dans une certaine mesure redondant avec les opérateurs switch et flow dans le sens
que tout processus BPEL écrit en utilisant switch et flow peut être réécrit en utilisant un
seul flow et un certain nombre de links. De plus, BPEL manque d’abstractions de haut
niveau lorsqu’il s’agit de développer des services qui mettent en jeu des multicasts avec
des conditions de synchronisation partielles, comme par exemple dans le cas d’un service
« client »qui requiert des devis de plusieurs services « fournisseurs » [Barros et al. 2005a].
On peut s’attendre à ce que dans le futur, des efforts de recherche portent sur la résolution
de ces problèmes et proposent soit des extensions de BPEL, soit des langages alternatifs.
4.5 Perspectives
4.5.1 Fouille d’interface, test de conformité et adaptation
Dans les architectures à base de services, nous l’avons déjà vu, la description structu-
relle et comportementale des services s’appuie sur la notion d’interface, il est donc d’at-
tendu que les services respectent leur interface. Ces dernières peuvent ainsi être perçues
comme un contrat entre les services. Cependant, les services sont indépendants les uns
des autres et ne peuvent exercer aucun contrôle les uns sur les autres. Pour ces raisons,
un service ne peut jamais être sûr que les autres se comportent comme convenu dans
leur interface. Tout ce dont un service peut être sûr au sujet des autres services avec les-
quels il interagit s’appuie sur l’ensemble des messages qu’il envoie et de ceux qu’il reçoit.
L’existence de traces des événements qui se sont passés ainsi que la donnée des inter-
faces permettent de poser la question de la confrontation de « ce qu’il s’est passé »à « ce
qui était prévu qu’il se passe ». Cette question peut être étudiée selon deux points de
vue. Le premier consiste à prendre les interfaces des services comme étant la référence,
puisqu’elles spécifient la manière dont les services partenaires doivent se comporter. La
question est alors de savoir si les événements enregistrés dans la trace sont cohérents avec
les interfaces. Par exemple, la trace peut contenir une séquence d’événements impossible
selon les interfaces, ce qui traduit une violation du contrat. Le second point de vue est
inverse : la trace des événements est supposée correcte car elle reflète ce qui se passe
réellement. Dans ce cas, se pose le problème de savoir si les interfaces ne sont plus va-
lides et en conséquence si elles doivent être modifiées. Il est alors extrêmement utile de
pouvoir dériver les nouvelles interfaces des traces. Lorsque le problème est étudié selon
la dimension structurelle des interfaces, cela revient à comparer un ensemble de messages
en XML à un schéma exprimé dans un langage tel que XMLSchema [W3C-XMLSchema ]
100 CHAPITRE 4. LES SERVICES WEB
(voir aussi [Bertino et al. 2004], un état de l’art sur ce sujet). Moins d’attention a été
consacrée à la résolution des problèmes de test de conformité et de fouille d’interfaces en
prenant le point de vue de la dimension comportementale des interfaces. Il est possible
d’envisager la résolution de ces problèmes en appliquant des méthodes de fouille de pro-
cessus [Aalst et al. 2003] ou de tests de conformité de processus [Rozinat and Aalst 2005].
Quelques investigations préliminaires sur le problème de la fouille d’interfaces comporte-
mentales sont rapportées dans [Dustdar et al. 2004, Gaaloul et al. 2004].
L’adaptation des interfaces est un autre problème saillant, complémentaire à celui de la
conformité. Lorsqu’il s’avère, que ce soit a priori par une comparaison, ou a posteriori par
un test de conformité, que l’interface fournie d’un service ne correspond pas à l’interface
que ses partenaires requièrent, il y a deux solutions : (1) adopter l’interface requise comme
interface fournie et modifier le service en conséquence ; (2) introduire un adaptateur qui
réconcilie l’interface fournie avec celle requise par les partenaires. La première solution est
en général mauvaise car le même service peut interagir avec plusieurs autres services par-
tenaires qui considèrent son interface originale. Ce qui conduit à la situation où le même
service peut participer à diverses collaborations qui nécessitent différentes interfaces four-
nies. La seconde solution pallie ce défaut par la fourniture d’autant d’adaptateurs que
d’interfaces requises. Comme nous l’avons vu dans la section 4.3, une interface peut être
décrite selon la dimension structurelle, comportementale ou encore non-fonctionnelle. Ainsi
l’adaptation doit être étudiée selon chacune de ces dimensions. Le problème de l’adapta-
tion structurelle se ramène essentiellement à celui de la réconciliation entre des types de
messages. Il existe sur ce sujet de très nombreuses études et plusieurs systèmes sont com-
mercialisés (par exemple Microsoft’s BizTalk Mapper). A l’inverse, le problème de l’adap-
tation selon la dimension comportementale est en cours d’étude [Benatallah et al. 2005a,
Altenhofen et al. 2005, Fauvet and Ait-Bachir 2006]. L’adaptation des règles d’usage (par
exemple, la réconciliation de différentes politiques de sécurité) est par contre l’objet de
peu de recherche. La technologie des services Web gagnant en maturité et tendant à être
utilisée dans le cadre de l’intégration de projets à grande échelle, l’adaptation des règles
d’usage devrait très vite devenir un sujet important et crucial.
4.5.2 Transactions
Certains services web, en particulier dans le domaine du commerce électronique, ont
des propriétés transactionnelles inhérentes [Baı̈na et al. 2004]. Ceci est le cas notamment
des services associés à la gestion de ressources (au sens large), comme par exemple la
réservation de chambres d’hôtel, de places de spectacle, de services professionnels, etc.
En principe, les propriétés transactionnelles de ces services peuvent être exploitées lors
de leur composition pour répondre à des contraintes et des préférences établies par le
concepteur et l’utilisateur final. Aujourd’hui cependant, les langages et outils disponibles
permettant de programmer des transactions sur des services Web ne fournissent pas de
concepts de haut niveau pour : (i) exprimer les propriétés transactionnelles désirées au
niveau du service composé ; (ii) assurer ces propriétés de façon automatisée en exploitant
les propriétés transactionnelles des services composants.
L’exécution de services composés avec propriétés transactionnelles s’appuie sur
l’exécution de transactions distribuées, complexes, souvent de longue durée, qui
éventuellement peuvent mettre en œuvre des mécanismes de compensation (une opération
4.5. PERSPECTIVES 101
de compensation est une opération dont l’objectif est d’annuler les effets d’une autre
opération qui n’a pas pu être terminée avec succès). De nombreux modèles de transactions
ont été proposés dans le domaine des bases de données, des systèmes distribués et des
environnements coopératifs (voir par exemple [Elmagarmid 1990, Gray and Reuter 1993,
Alonso et al. 2003, Papazoglou 2003]).
Il est bien connu que les approches traditionnelles pour assurer les propriétés ACID
d’une transaction (Atomicity, Consistency, Isolation, Durability) ne sont typiquement
pas adéquates pour les transactions de longue durée telles que celles rencontrées dans le
domaine des services web, puisqu’il n’est pas acceptable de verrouiller des ressources dans
une transaction qui s’exécute sur une durée prolongée. De plus, le protocole de validation
à deux phases, couramment utilisé dans les systèmes distribués, n’est pas applicable aux
services composites car, dans ce protocole, il est fait l’hypothèse que tous les partenaires
de la transaction supportent les opérations de préparation et de validation indispensables
à sa mise en œuvre ; ce qui n’est pas toujours le cas dans le cadre des services web. Dans
ce contexte, il peut être approprié de relaxer les contraintes d’atomicité tout-ou-rien. A
cela s’ajoutent des problèmes d’intégration, puisque chaque service composant s’appuie
sur un système de gestion de transactions choisi ou conçu pour le composant, considéré
individuellement. Lorsqu’un service est intégré comme composant, il est fortement
probable que son système de gestion de transactions ne réponde pas aux besoins de
la composition vue comme un tout. Ce dernier aspect est la motivation principale
pour l’émergence de protocoles tels que WS-Coordination [Cabrera et al. 2002a],
WS-AtomicTransaction8 [Cabrera et al. 2002b] et pour la conduite de travaux
de recherche (voir par exemple [Arregui et al. 2000, Hagen and Alonso 2000,
Vidyasankar and Vossen 2003, Limthanmaphon and Zhang 2004, Fauvet et al. 2005]).
Chapitre 5
La plate-forme J2EE
Ce chapitre se propose de fournir une vue synthétique des grands principes qui gou-
vernent la mise en œuvre de l’environnement J2EE [J2EE 2005]. Cet environnement, pro-
posé dans le contexte de Java par Sun Microsystem, offre un support au développement, au
déploiement, ainsi qu’à l’exécution d’applications s’exécutant en mode serveur, comme par
exemple des applications Web ou des applications réparties offrant des prises de service.
Le chapitre commence par une introduction de l’environnement J2EE, suivie par une
présentation des principes de construction et d’assemblage d’application J2EE à base
de composants. Les trois sections qui suivent introduisent les principales fonctions tech-
niques offerte par l’environnement, permettant de garantir un certain nombre de propriétés
nécessaires aux applications d’entreprise visées, ayant souvent un caractère critique. Deux
sections sont ensuite consacrées aux deux principaux modèles de programmation de com-
posants applicatifs, à savoir d’une part les composants de présentation Web permettant de
gérer la logique d’interaction avec les utilisateurs munis d’un navigateur, et d’autre part
les composants portant la logique dite métier. Ces derniers incluent notamment les com-
posants représentant le modèle d’informations métier, généralement projeté dans une base
de données relationnelle. La dernière section conclut sur la synthèse présentée et dessine
quelques perspectives sur les évolutions en cours de cet environnement.
5.1 Introduction
5.1.1 Historique
L’environnement Java pour l’entreprise a commencé à émerger assez rapidement après
les débuts de Java au milieu des années 90. A son origine, Java était destiné aux environne-
ments contraints (par exemple des petits équipements électroniques). Il a en fait percé dans
l’environnement du Web, notamment dans les navigateurs pour le support d’interfaces gra-
phiques riches (notion d’appliquette Java, en anglais applet). Les premières déclinaisons
de Java dans l’environnement des serveurs sont apparues en 1997 avec les servlets, dont
l’objectif est la construction programmatique de pages Web, puis avec les Entreprise Java
Beans dont l’objectif est le support de code métier nécessitant un contexte d’exécution
transactionnel (cf. [Gray and Reuter 1993]).
Après ces premiers pas et un relatif succès de ces technologies, Sun a structuré l’offre
technique autour des serveurs d’application Java à travers le standard J2EE. L’objectif
de ce dernier est de fédérer dans un cadre cohérent toutes les technologies nécessaires à
la mise en oeuvre des applications de l’entreprise (applications orientées « serveur »). La
première version des spécification de J2EE est publiée en 1999. Suivront alors la version
1.3 de J2EE en 2001, puis la version 1.4 en 2003 incluant un support complet des standards
XML et le support des services Web.
Java est désormais bien installé dans l’écosystème des applications d’entreprise et
est devenu le principal concurrent de l’environnement .NET de Microsoft dont le cha-
pitre 6donne un aperçu. Le langage lui-même a acquis de la perennité puisqu’on compte
désormais plus de 4 millions de développeurs Java dans le monde.
L’étage client
L’étage client est représenté par le terminal et prend en charge l’interaction avec l’uti-
lisateur. Du point de cette interaction, deux architectures sont possibles :
– Architecture client léger : dans ce cas, le terminal embarque un mécanisme qui per-
met d’interpréter les informations de présentation et d’interaction avec l’utilisateur
produite par le serveur. Cela peut être un navigateur qui interprète des pages XML
(XHTML, WML, ou VoiceXML suivant la modalité utilisée) produite par le serveur
106 CHAPITRE 5. LA PLATE-FORME J2EE
Le choix entre ces deux architectures est un problème de compromis entre la facilité de
déploiement, la complexité de gestion de versions de logiciels avancés au niveau du terminal
d’un côté, et l’expérience ergonomique perçue par l’utilisateur d’autre part, notamment
pour les interfaces graphiques. Dans la seconde architecture, l’environnement J2EE fournit
un support d’exécution pour aider la partie cliente à se lier facilement avec la partie serveur.
5.1. INTRODUCTION 107
L’étage serveur
L’étage serveur exécute le code applicatif J2EE pour le compte de plusieurs utilisateurs
simultanément. Ce même code est à son tour décomposé en deux étages distincts :
– L’étage présentation : cet étage exécute le code des interfaces utilisateur de type client
léger. Il produit généralement des pages XML qui sont retournées à un navigateur
représentant l’étage client qui se charge alors de les interpréter pour construire le
rendu attendu.
– L’étage métier : cet étage correspond au code définissant le métier ou la fonction
même de l’application. L’environnement J2EE propose plusieurs types de comp-
sants pour mettre en œuvre cet étage. Les composants de session gére le code métier
représentant un session d’interaction entre un utilisateur et l’application, ces ses-
sions pouvant ou non contenir des données propres. Les composants de données en-
capsulent l’accès aux bases de données relationnelles. Enfin les composants réactifs
permettent au code métier de réagir sur l’arrivée d’événements provenant d’applica-
tions externes ou d’événements internes à l’application (programmation asynchrone
dans le cadre d’une application J2EE).
L’étage information
L’étage information (étage EIS dans la figure 5.1) se compose des systèmes qui four-
nissent les informations de l’entreprise nécessaires aux applications. Ces informations
peuvent être fournies par d’autres applications patrimoniales, ou directement par des bases
de données.
Ces principes d’architecture ont un impact sur l’organisation des différentes phases du
cycle de vie d’une application J2EE : sur la phase de développement avec les principes
d’organisation du code mais aussi sur les phases de déploiement et d’exécution.
aux autres composants ou aux ressources dont ils ont besoin pour leur exécution.
Quatre types de conteneurs sont proposés par l’environnement J2EE. Deux sont liés
à l’étage client et deux autres à l’étage serveur J2EE. Pour l’étage client, il s’agit du
conteneur d’application cliente J2EE et du conteneur d’applet, ce dernier ne proposant
l’accès à aucune des fonctions de l’environnement J2EE. Côté serveur, il y a le conteneur
de composants de présentation / servlets (conteneur Web) et le conteneur de composants
métier (conteneur EJB).
Comme le montre la figure 5.2, les conteneurs (sauf le conteneur d’applet) donnent
accès aux services de l’environnement J2EE. Parmi ces services, certains peuvent être
appelés depuis n’importe quel étage client ou serveur. Ces services comprennent :
– La pile Web Service : elle est composée de trois parties. La partie basse (SAAJ)
est le canevas SOAP pour Java avec le support des attachements pour le transport
de données binaires. Il permet d’adapter SOAP à différents protocoles de transport
des appels. La partie haute (JAX-RPC) implante la fonction de RPC au-dessus de
SOAP. Enfin, la troisième partie (JAXR) donne accès aux annuaires de Web Services
(par exemple un annuaire UDDI).
– Le support de communication asynchrone : les communications asynchrones sont un
élément important pour l’intégration d’applications. La spécification JMS définit la
manière de gérer des files de messages, que ce soit à travers des queues garantissant
l’acheminement d’un message d’un producteur à un consommateur de messages, ou
à travers des sujets (topic) permettant la publication et la réception anonyme de
messages (tous les consommateurs recoivent tous les messages). Elle définit aussi
5.1. INTRODUCTION 109
Cette définition des différents rôles fait ressortir l’ampleur des compétences qui doivent
être mises en œuvre par le développeur J2EE. Il doit maı̂triser la plate-forme, au minimum
les outils de développement associés, les services de l’environnement J2EE ainsi que les bons
principes d’architecture à mettre en œuvre. C’est un défi majeur en termes de formation
nécessaire pour avoir un développeur efficace, même si dans de grosses structures ces
développeurs peuvent être spécialisés, par exemple par étage.
Un composant J2EE est un ensemble logiciel défini par des classes, des inter-
faces et des informations descriptives. Comme dans les autres modèles de composants
112 CHAPITRE 5. LA PLATE-FORME J2EE
[Szyperski and Pfister 1997], il se définit comme un élément logiciel proposant des inter-
faces de service (en général une seule dans le cas de J2EE) et explicitant ses dépendances
vis-à-vis d’autres composants en exhibant ses interfaces requises. Il peut aussi définir un
certain nombre de paramètres permettant d’adapter sa configuration à différents contextes
de déploiement. Toutes ces informations sur le composant sont décrites dans un descripteur
qui est utilisé lors de son assemblage au sein d’une application et lors de son déploiement.
Une application J2EE est donc un assemblage de composants qui sont empaquetés
dans un paquetage qui lui est associé. Les deux processus d’assemblage et d’empaque-
tage sont néanmoins relativement indépendants. La figure 5.4 montre bien que les liaisons
créées entre les composants à l’assemblage font fi des frontières définies entre les paque-
tages tels que définis dans la figure 5.3 ; un composant d’un paquetage peut très bien être
relié à un composant d’un autre paquetage. Il reste que la politique d’empaquetage suit
généralement une logique de pré-assemblage. Au minimum, une application J2EE distingue
les paquetages par étage client, Web, métier, et pour chaque connecteur. Cela signifie qu’il
y a toujours au minimum deux niveaux d’empaquetage : le niveau applicatif et le niveau
d’empaquetage de composants par étage.
Les deux sections qui suivent décrivent comment s’organisent les deux processus d’em-
paquetage et d’assemblage d’application.
Le descripteur XML qui suit est celui de l’application J2EE A1 de la figure 5.3. Il
correspond au contenu du fichier application.xml du répertoire META-INF du paquetage
a1.ear. La définition n’est pas complète. Elle montre la définition de deux des modules
composant l’application A1. Le premier est le module Web défini dans w1.war et le second
est le module métier défini dans ejb1.jar. Comme le montre la figure 5.5, les paquetages
de ces modules doivent donc être stockés à la racine du fichier a1.ear pour pouvoir être
pris en charge au déploiement.
<application>
<display-name>A1</display-name>
<description>Application description</description>
...
<module>
<web>
<web-uri>w1.war</web-uri>
<context-root>monsite_a1</context-root>
</web>
</module>
...
<module>
<ejb>ejb1.jar</ejb>
</module>
...
</application>
Nous ne donnons pas une vue exhaustive de ces descripteurs de déploiement, la suite
du chapitre montrant des éléments d’autres types de descripteur pour illustrer diverses
constructions J2EE.
114 CHAPITRE 5. LA PLATE-FORME J2EE
Comme le montre la figure 5.6, l’assemblage entre les composants s’effectue d’abord
au niveau de la description des composants. Cette description contient la définition des
interfaces fournies et des interfaces requises (les dépendances vers d’autres composants).
En fait, la définition de ces composants est spécifique au type de composant. Par
exemple, un servlet ne définit pas d’interface fournie puisqu’on la connait exactement :
c’est javax.servlet.Servlet. Un composant métier définit des interfaces métier fournies,
comme le composant CM1 avec l’interface Cm1Home, sachant que l’interface Cm1 n’est là que
pour signaler l’interface métier supportée par les instances de ce composant. La liaison
avec un composant se fait la plupart du temps à travers une interface de type usine (cf.
section 2.3.2). Dans un monde objet tel que celui de Java, il est naturel de constater qu’un
composant logiciel gère des instances d’un même type. Par ailleurs, ce patron d’architecture
est utilisé par le serveur d’application J2EE pour avoir le contrôle sur le cycle de vie des
instances par interception des fonctions de l’usine. Cela lui permet d’opérer la gestion
de ressources adéquate à l’exécution et d’optimiser par exemple cette gestion pour des
raisons de montée en charge. Pour les composants auxquels on va pouvoir se lier, deux
informations importantes sont définies dans le descripteur :
– Information de nommage : un nom est associé à chaque composant. Ce nom iden-
tifie de façon unique un composant dans le contexte du paquetage dans lequel il
est défini. Ce nom est ensuite utilisé par les autres composants qui veulent y faire
référence. Un exemple d’une telle définition est l’élément ejb-name utilisé comme
nom du composant CM1 dans la figure 5.6.
– Information de typage : une ou deux interfaces sont généralement associées à un
composant. Une seule est vraiment importante : c’est celle qui permet de se lier au
composant. C’est généralement une interface usine comme l’est l’interface Ejb1Home
associée au composant CM1 dans la figure 5.6. Cette interface est utilisée par la suite
5.2. APPROCHE À COMPOSANTS 115
Il est clair ici que le code du composant référençant est indépendant de celui du com-
posant référencé. En effet, aucune information relative à CM1 n’est présente dans le code
de CP2. Pour lier CP2 à un autre CMx, il suffit de changer le lien dans son descripteur de
déploiement en spécifiant un autre composant : par exemple, on se lie à un ejb1b avec
<ejb-link>../ejb1b.jar/ejb1b</ejb-link>.
Le mécanisme de mise en liaison dans le code est donc le même quel que soit le type
de composant ou de ressource : il s’effectue par un lookup approprié sur le contexte corres-
pondant à l’environnement propre au composant. L’exemple ci-dessus est compliqué par
l’utilisation d’une fonction de conversion de type, nécessaire dans le cas où on se lie à un
composant offrant une interface accessible à distance (interface Remote).
Il faut noter que ce mécanisme est en train d’évoluer quelque peu dans le cadre des
nouvelles versions des spécifications, notamment dans le cadre d’EJB 3.0. En effet, l’af-
fectation de la variable de liaison à partir du lookup pourra être prise en charge par le
conteneur. Il suffira pour cela d’annoter la variable ou un accesseur à celle-ci pour définir
cette liaison (injection de la mise en liaison par le conteneur. L’utilisation des annota-
tions est généralisée dans cette version des spécifications, ce qui fait que les informations
de description d’un composant qui étaient auparavant présentes dans le descripteur de
déploiement sont de retour dans son code, même si elles sont isolées du code proprement
dit à travers ce mécanisme d’annotation. Cette nouvelle approche a ses avantages et ses
inconvénients. Elle rend le code moins lisible à cause de la surchage due aux annotations.
Par contre, la description d’un composant est attachée à ce dernier plutôt que d’être ag-
glomérée dans un descripteur général. Cela devrait faciliter l’émergence de bibliothèques
de composants et de vrais outils d’assemblage s’appuyant sur celles-ci, donnant ainsi accès
à un véritable environnement de réutilisation de code. Dans une telle démarche, on pri-
vilégie l’utilisation des annotations pour toutes les informations relatives au comportement
du composant (propriétés déclaratives telles que les propriétés transactionnels ou les pro-
priétés liés à la persistance), et l’utilisation des descripteurs pour toutes les informations
d’assemblage.
Ces principes d’organisation sont garantis par l’environnement J2EE à travers l’orga-
nisation d’une hiérarchie de chargeurs de classes Java. En effet, on a affaire à un jeu de
poupées russes comme le montre la figure 5.7. On peut distinguer deux types de chargeurs.
Les premiers contiennent le code commun à toutes les applications déployées dans un
serveur J2EE. Au niveau de ces chargeurs, on a généralement une hiérarchie distinguant le
code de J2SE à la base, puis celui du serveur J2EE avec ses services de base, puis un dernier
contenant le code des ressources mises à disposition des applications (dans l’exemple, le
chargeur en question contient le code du composant CO2).
Les autres chargeurs sont spécifiques à chaque application et sont organisés en deux
niveaux :
– Le chargeur du code métier : tous les composants du code métier d’une application,
quels que soient les paquetages dans lesquels ils ont été embarqués, sont chargés
dans le même espace. Pour l’application A1, on voit dans la figure 5.7 que tous les
composants métier (CM1, CM2, CM3, et CM4) ont leur code de l’espace d’un chargeur
unique. A partir de ce chargeur, ceux-ci ont accès au code de tous les chargeurs
partagés, donc à toutes les ressources J2EE, à la plate-forme J2EE, ainsi qu’au
code de J2SE. Il n’ont en aucun cas accès au code de l’étage de présentation de
l’application.
– Les chargeurs de code Web : concernant le code de présentation, un chargeur différent
est créé pour chaque paquetage Web déployé. Ainsi, le code de CP1 et de CP2 sont
embarqués dans le même chargeur, alors que celui de CP3 est dans un chargeur
différent. Ces chargeurs ont accès au code de tous les autres étages, étage métier
inclus. Le seul point notable est qu’il y a une isolation entre les différents composants
de présentation qui ont été empaquetés indépendamment les uns des autres. Cette
fonction ne parait néanmoins pas apporter grand chose dans l’environnement, les
différents éléments de la présentation étant généralement empaquetés ensemble (il
peut être nécessaire de partager des informations concernant les transitions entre les
différentes pages d’une présentation, ou encore des informations de session).
118 CHAPITRE 5. LA PLATE-FORME J2EE
L’important avec ce modèle de chargement du code est que le code d’une appli-
cation, principalement composé du code métier et du code de présentation, peut être
chargé/déchargé indépendamment des autres éléments de l’environnement J2EE. Ceci est
rendu possible par l’isolation du code d’une application dans un chargeur dédié comme le
montre la figure 5.7 (il suffit alors de suppimer ce chargeur pour décharger l’application).
C’est important notamment en phase de développement où il peut être lourd de relancer
tout le serveur J2EE à chaque fois qu’on veut relancer l’application après une modification.
De la même manière, on veut pouvoir descendre dans certains cas à un grain de recharge-
ment encore plus fin, par exemple lors de la mise au point de l’étage de présentation, en
ne rechargeant que le code de ce étage. Nous verrons par la suite que ce type de fonction
est proposé en standard dans certains cas, comme par exemple les JSP (cf. section 5.5.4).
Les sections qui suivent s’attachent à présenter les différents éléments programmatiques
qui sont mis à la disposition du programmeur d’applications J2EE. Elles se focalisent
pour l’essentiel sur les éléments concernant la programmation de l’étage de présentation
(interfaces servlet) et de l’étage métier (interfaces EJB), avec un point sur trois aspects
prépondérants requis par les applications d’entreprise : les mécanismes permettant de faire
communiquer et d’intégrer les différentes applications, les transactions comme premier
support à la sûreté de fonctionnement, et la sécurité pour gérer l’authentification des
utilisateurs et leurs autorisations d’accès aux ressources de l’entreprise.
Java RMI
Dans le cas de RMI, les règles de projection sont relativement simples. La sémùantique
de l’invocation de méthode n’est pas exactement la même qu’une invocation locale (pure
Java) de l’interface. En effet, concernant le passage d’objets en paramètre d’invocation de
méthode, dans un monde pur Java, il s’agit dans tous les cas d’un passage par référence.
5.3. APPLICATIONS RÉPARTIES ET INTÉGRATION 119
Dans le cas de RMI, les objets répartis (objets RMI implantant l’interface Remote) sont
passés par référence alors que les autres sont passés par valeur.
Concernant la partie encodage des invocations à distance, RMI propose deux proto-
coles. L’un, JRMP, est le protocole natif de Java RMI qui fait d’ailleurs partie de Java de
base (c’est à dire J2SE). L’autre est IIOP, le protocole de Corba. C’est ce protocole qui a
été choisi pour l’interopérabilité entre les serveurs J2EE interagissant en mode RMI.
Web Services
La projection vers les services Web est encore plus restrictive que dans le cas de RMI.
En effet, il n’existe pour l’instant (spécification en cours) aucun moyen de passer des
références vers d’autres services Web lors de l’invocation d’un service Web. Il n’y a donc
que du passage par valeur, avec des contraintes sur l’encodage XML des objets passés en
paramètre. Par exemple, si un objet est référencé plusieurs fois dans le graphe à encoder,
l’encodage amène à ce qu’il y a autant d’instances différentes de l’objet en question dans
le graphe décodé que de références dans le graphe d’origine.
Concernant l’encodage XML des invocations à distance, c’est le mode document/wrap-
ped spécifié dans le cadre du WS-I qui a été retenu comme protocole d’interopérabilité.
Dans le second cas, les messages reçus par le connecteur sont ensuite dirigés vers des
composants EJB réactifs (cf. section 5.6.3) pour être traités.
Un connecteur JCA doit implanter des API conforme à cette spécification pour pou-
voir s’intégrer à un serveur J2EE. Suivant les interfaces qu’il implante, il peut s’intégrer
principalement à trois supports techniques fournis pour l’environnement J2EE :
– Gestion des réserves de connexions : Un des principes partagés par tous les types de
connecteurs est qu’un connecteur gère des connexions permettant de communiquer
avec le système externe auquel il donne accès. Ces connexions sont généralement
lourdes et coûteuses à mettre en place. En conséquence, le serveur J2EE prend en
charge le recyclage des connexions pour éviter leur mise en place et leur destruction
lors de chaque échange avec le système externe (gestion d’une réserve de connexions
par le serveur).
– Gestion des transactions : L’utilisation de connecteurs se fait bien souvent pour
intégrer l’environnement J2EE avec d’autres systèmes critiques, offrant eux-mêmes
des capacités transactionnelles. Suivant le mode d’interaction, cela signifie que soit le
serveur J2EE doit inclure les interactions avec le système externe dans une transac-
tion répartie initialisée par J2EE, soit à l’inverse que le système externe interagissant
avec le serveur J2EE doit inclure les actions effectuées dans l’environnement J2EE
dans une transaction répartie initialisée par le système externe. JCA propose pour
cela différents niveaux d’intégration transactionnelle : pas de support transactionnel,
support de transaction locale (transactions à validation une phase), et support de
transaction globale répartie (transaction à validation à deux phases).
– Gestion de la sécurité :
Ce standard est maintenant utiliser de façon usuel comme mécanisme d’intégration
standard de sous-systèmes J2EE, même si ces derniers ne sont pas transactionnels. C’est
en tout cas souvent ce qui se passe pour les connecteurs standard présenté ci-dessous (cf.
section 5.3.4), ou encore les connecteurs JMS présentés dans la section 5.3.2. Notons que
l’objectif des contrats requis par l’environnement J2EE pour ce type de composant est de
permettre au serveur de garder la responsabilité de la gestion des ressources. Cela permet
ainsi d’avoir une vision et donc des politiques de gestion de ressources plus globales, de
façon à exploiter au mieux cette gestion suivant le contexte d’utilisation.
En terme d’intégration, une limite de la spécification JCA est qu’elle ne propose rien
concernant la gestion du cycle de vie de ce type de composant. Cela peut poser des
problèmes quant à l’initialisation de tels composants ou encore vis-à-vis de l’ordre dans
lequel ils sont activés.
échanges nécessaires avec de telles bases, en s’appuyant bien sûr sur le standard SQL (voir
http://www.jcc.com/sql.htm comme point d’entrée pour de nombreuses références). Les
échanges se font donc sur la base d’émissions de requêtes SQL vers la base et récupération
en retour d’appel (interaction synchrone).
Pour ce faire, un pilote JDBC dédié au produit base de données à accéder est chargé
comme un connecteur dans l’environnement J2EE. Il permet d’adapter les appels à l’API
JDBC au format d’échange avec le produit en question (par exemple MySQL). En effet,
ces échanges se font généralement grâce à un protocole réseau spécifique au produit.
différentes bases de données). Dans le cas des transactions réparties, JTA définit les in-
terfaces nécessaires à l’utilisation de ressources supportant le protocole XA (protocole de
validation à 2 phases).
UserTransaction systx;
// Un composant \emph{servlet} se liant au gestionnaire de transaction
// lors de son initialisation.
public void init(ServletConfig config) throws ServletException {
systx = new InitialContext().lookup("java:comp/UserTransaction");
systx.begin(); // démarrage d’une transaction
... // actions transactionnelles
systx.commit(); // validation de la transaction courante
...
}
Cette liaison est faite ici à l’initialisation d’un composant de présentation. L’accès à ce
contexte JNDI peut néanmoins s’opérer dans n’importe quelle séquence de code s’exécutant
dans le cadre d’un serveur J2EE.
type, mixant le code HTML et le code Java pour la définition des parties dyna-
miques de ces pages. Même s’il est principalement utilisé pour construire des pages
HTML téléchargées à travers HTTP, le support JSP est indépendant du protocole
d’échange de pages avec le client. Il est donc par principe indépendant de HTTP,
même s’il contient aussi une déclinaison pour HTTP. Les APIs relevant du support
JSP appartiennent aux packages Java javax.servlet.jsp, javax.servlet.jsp.el,
et javax.servlet.jsp.tagext. Les deux derniers packages contiennent des fonctions
avancées améliorant et/ou simplifiant la définition de pages JSP.
Les deux premiers niveaux d’abstraction sont couverts par la spécification des
servlets dans [Sun JSR-000154 2003]. Le niveau JSP est couvert par une spécification
complémentaire dans [Sun JSR-000152 2003].
Il y a évidemment des dépendances fonctionnelles des niveaux les plus hauts vers ceux
du dessous. La partie JSP dans sa définition dépend du niveau servlet de base et est déclinée
pour les servlets HTTP. Elle pourrait néanmoins être déclinée pour d’autres protocoles, par
exemple WTP pour le monde mobile WAP. Nous détaillons dans la suite les trois niveaux
énumérés précédemment, sachant que la plupart des utilisateurs s’appuient sur le niveau
le plus haut, que ce soit à base du standard JSP au d’autres moteurs de templates comme
XMLC proposé par le consortium ObjectWeb [Objectweb Enhydra 2005]. Des canevas lo-
giciels d’encore plus haut niveau et basés sur le patron architectural modèle/vue/contrôleur
(MVC) pour les interfaces graphiques existent. Ils sont largement utilisés mais non stan-
dard. Nous pouvons citer par exemple STRUTS [The Apache Software Foundation 2005]
qui est de loin le plus répandu, ou encore JSF qui est standardisé dans le cadre de J2EE
[Sun JSR-000127 2004].
à deux niveaux effectué par un conteneur de servlet HTTP). Une fois ce démultiplexage ef-
fectué par le moteur de servlets, la servlet sélectionnée est activée pour opérer le traitement
grâce à l’opération service. Nous verrons par la suite qu’il existe aussi des mécanismes
pour intervenir sur ce processus de demultiplexage.
Concernant l’opération de traitement de requête, le message à traiter ainsi que le mes-
sage de réponse sont abstraits par le modèle au travers de deux interfaces permettant leur
manipulation, à savoir ServletRequest et ServletResponse. Ces deux interfaces donnent
accès à la fois aux informations d’en-tête des messages requises par les servlets et aux
contenus de ces messages. Elles déterminent donc les principes de bases de construction
d’une pile protocolaire à base de servlet.
la méthode init est appelée par le conteneur pour initialiser la servlet en question.
Elle est alors prête à être activée. De façon symétrique, le conteneur peut éliminer
une servlet ; à ce moment-là, il appelle destroy et l’instance de la servlet est alors
ramassé. L’autre fonction concerne l’activation de la servlet lors de l’arrivée d’un
message. Le rôle du conteneur est alors d’encapsuler ce message dans les interfaces
telles que présentées précédemment, et à partir d’informations récupérées dans ce
message, de déterminer le contexte dans lequel se trouve la servlet à laquelle le
traitement du message est délégué.
– Le contexte de servlets
Un contexte contient un certain nombre de servlets qui y ont été déployées auxquelles
un nom est associée et qui permet d’identifier celle-ci dans le contexte. Au contexte
lui-même est associé un nom qui sert de préfixe lors du processus de démultiplexage.
Il offre aussi à ces servlets de gérer un ensemble d’attributs qui leur permettent
de partager des informations (communication entre servlets). Il permet aussi de de
manipuler le système de démultiplexage (par exemple, retrouver une servlet d’un
nom donné ou le contexte auquel elle appartient).
– La servlet
Une fois qu’une servlet est prête (initialisation terminée), le conteneur peut l’activer
au rythme qu’il juge bon (invocation de service en parallèle). Comme au niveau
contexte, la servlet gère aussi un ensemble d’attributs accessible aux requêtes traitées
par service.
Filtres de servlets
Il est possible d’associer des filtres pour effectuer des traitements préalablement à
l’exécution d’une servlet. Ces filtres peuvent agir sur les données d’entête lié à une requête
que sur son contenu, ainsi bien sûr que sur la réponse qui doit être produite par la servlet.
Ces filtres doivent au préalable avoir été enregistrés auprès du conteneur et associés à une
ou plusieurs servlets. Le conteneur crée alors une chaı̂ne de filtres pour chaque servlet qui a
été déployée et exécute cette chaı̂ne lors de chaque activation d’un traitement de requête.
128 CHAPITRE 5. LA PLATE-FORME J2EE
Contrôle du démultiplexage
Lors de l’exécution d’une servlet, il est possible d’utiliser d’autres ressources d’exécution
permettant de déléguer tout ou partie son traitement : les RequestDispatcher. Ces res-
sources peuvent être récupérées par la servlet auprès du conteneur à partir du nom auquel
est associé la ressource en question.
A partir de là, cette ressource peut être utilisée soit pour agir à la place de la servlet
en cours d’exécution (voir la méthode forward), soit pour inclure une partie de la page
(voir la méthode include) générée par cette servlet (par exemple un bandeau commun à
toutes les pages d’un site). Dans le cas d’un forward, aucune information ne doit avoir été
envoyée au client par la servlet au moment de son activation (génération d’une exception).
contexte dont le nom correspond à un préfixe du chemin d’une URL puis à rechercher dans
ce contexte une servlet dont le nom correspond au reste du chemin auquel on a soustrait
ce préfixe.
Nous n’allons pas détailler les informations relatives à HTTP mais simplement rappeler
brièvement en quoi elles consistent. Elles sont pour l’essentiel introduites dans le cadre des
interfaces de manipulation des requêtes et des réponses, correspondant à des extensions des
interfaces préalablement définies par les servlets de base. Par ailleurs, la classe abstraite
HttpServlet propose un raffinement de l’opération service en appels vers des opérations
correspondant aux actions définies par le protocole HTTP, à savoir doGet pour une requête
GET, doPost pour une requête POST, doHeader pour une requête HEADER, etc. Ces opérations
ont la même signature que l’opération service, ayant en paramètre les objets représentant
la requête et la réponse.
Les informations mises à disposition par cette interface correspondent aux données
véhiculées par une requête HTTP, et notamment :
– Les informations sur l’URL. Il s’agit de toutes les informations relatives à l’URL
d’invocation et plus particulièrement les éléments qui la compose tels qu’ils ont été
définis précédemment (URI ou chemin, les informations supplémentaires s’il y en a
(ce qui est entre ; et ?), la requête s’il y en a une (ce qui suit ?), ou encore le nom du
contexte de la servlet).
– Les cookies. Il s’agit de tous les cookies qui ont été envoyés par le client dans la
requête.
– L’utilisateur. Il s’agit du nom de l’utilisateur à l’origine de la requête s’il a été
authentifié.
– Les paramètres d’en-tête HTTP. Il s’agit de paramètres passés en en-tête de la
requête HTTP spécifiant par exemple des dates ou des informations sur les langues
supportées par l’utilisateur. On peut récupérer ces valeurs sous différentes formes
telles que entier, chaine de caractères, date (long).
– La session. Il s’agit de la session dans laquelle cette requête s’exécute. En général,
s’il n’y en a pas encore, celle-ci est créée par le conteneur. Cette information n’est
pas définie par HTTP (spécifique aux servlets). Elle est transportée dans la requête
soit sous la forme d’un cookie, soit dans les informations supplémentaires de l’URL
d’invocation (cf. partie de l’URL entre ; et ?).
Par rapport à une réponse de servlet de base, cette interface donne accès à des
opérations permettant soit de formatter l’en-tête du message de réponse HTTP, soit de
formatter les URL diffusées dans le contenu.
Dans le premier cas, il s’agit de définir dans la réponse les cookies retournés, le code
de statut de la réponse, des paramètres d’en-tête. Cela peut aussi consister à retourner
directement une réponse d’erreur ou une réponse de redirection de la requête vers une
autre URL.
130 CHAPITRE 5. LA PLATE-FORME J2EE
Dans le second cas, il s’agit d’opérations qui permettent d’encoder les informations de
session dans les URL de dialogue diffusées dans les pages de réponse, permettant ainsi de
transporter systématiquement cette information entre le client et le serveur pour toutes
les interactions les concernant. Cette approche permet d’éviter le passage par les cookies,
permettant ainsi de gérer des sessions même si l’utilisateur a désactivé les cookies dans
son navigateur par exemple.
Nous observons aussi les échanges avec le monde Java par l’utilisation d’un bean. La
directive JSP useBean crée un bean de la classe donnée en paramètre et l’affecte à la
variable date. La directive suivante affecte la propriété localisation du même bean (appel
de la méthode setLocalisation de la classe MaDate définie ci-dessous). Enfin, on récupère
la chaine de caractères correspondant à la date à afficher par l’instruction ${date.date}.
package monorg;
class MaDate {
public setLocalisation(String loc) {
...
}
public String getDate() {
return ...
}
}
Notons que le code d’une page JSP peut accéder à toutes les informations de l’envi-
ronnement de servlet et a donc accès à toutes les APIs de ce dernier.
Les pages JSP sont déployées comme des servlets. En effet, au déploiement d’une page
JSP, le conteneur génére la classe servlet correspondante, la compile et la charge. Lors
de chaque interaction avec cette servlet, le conteneur vérifie que la servlet est plus jeune
que la page JSP associée. Si ce n’est pas le cas, le conteneur met à jour la classe servlet,
supprime les anciennes instances et en recrée de nouvelles pour traiter les requêtes. Cela
offre une grande souplesse notamment en phase de développement.
D’autres mécanismes sont aussi offerts par JSP, et notamment les tags et les librairies de
tags ainsi que le langage d’expression. Cela sort du sujet du présent chapitre dont l’objectif
est de présenter les principes des technologies fournies par l’environnement J2EE. Nous
ne les présentons donc pas.
132 CHAPITRE 5. LA PLATE-FORME J2EE
déploiement. Cela se fait à l’aide d’un annuaire dédié accessible par JNDI.
– La deuxième action consiste à exporter les dépendances de ces composants dans un
espace de noms associé au composant lui-même et à y associer la référence vers le
composant auquel se lier grâce à son nom dans l’espace de nommage de l’application.
Rappelons que le découplage en deux actions permet de rendre le code indépendant
du processus d’assemblage (pas de lien dans le code avec un composant particulier). A
partir de là, les composants vont pouvoir accéder aux composants auxquels ils sont liés
en effectuant la phase de liaison effective (phase de bind) dans le code applicatif (voir
code utilisant lookup dans la section 5.2.3 équivalent au bind). Notons que cette phase est
prise en charge par le conteneur dans la version 3 des EJB (injection des références par le
conteneur dans une variable du code du composant) et devient donc transparente pour le
code applicatif.
A l’exécution, le conteneur prend en charge le fait que le composant est activé de
façon locale (appel à l’intérieur de la même JVM), de façon distante (appel à distance), ou
sur arrivée d’un événement (messagerie asynchrone). Il prend aussi en charge les aspects
techniques associés à ces composants comme l’activation de transactions, la vérification
de contraintes de sécurité, ou les échanges de données avec un espace de sécurisation des
données (en général une base de données relationnelles). Nous détaillons cela dans les
sections qui suivent pour chaque catégories de composants métier.
La figure 5.10 nous montre que le conteneur met en œuvre du code (pour gérer les
aspects techniques qu’il supporte de façon transparente au code applicatif) par interception
des appels aux interfaces métier des composants (seulement pour les composants de session
et les composants entité). Malheureusement, cette interception n’est pas transparente dans
le modèle de programmation. La spécification fait l’hypothèse architecturale qu’il existe un
objet d’interception indépendant de l’instance du composant métier, cet objet implantant
l’interface métier. Dans ce cas, le comportement fonctionnel est différent suivant qu’on
appelle une opération métier sur l’objet d’interposition ou sur l’instance du composant.
134 CHAPITRE 5. LA PLATE-FORME J2EE
Les spécifications EJB distinguent deux types de composants de session : les compo-
sants de session sans état et ceux avec état. Concernant la première catégorie, elle sert à
supporter des processus métier sans mémoire. Ces processus peuvent être mis en œuvre
localement ou à distance à l’aide de RMI ou de Web Services.
5.6. PROGRAMMATION DE L’ÉTAGE MÉTIER 135
Figure 5.11 – Le cycle de vie d’un composant métier EJB de session sans état
Comme le montre la figure 5.11, un composant de session sans état est relativement
primitif dans son fonctionnement. Des instances sont allouées pour effectuer des opérations
dans le cadre d’une session et libérées ensuite. Leur cycle de vie contient deux états : l’état
non alloué ou non existant et l’état prêt dans lequel les opérations de son interface métier
peuvent être activées.
Deux aspects techniques peuvent être pris en charge pour ces composants : le sup-
port des transactions et le support de l’accès distant. Pour les transactions, le niveau de
transparence offert permet de caler la démarcation transactionnelle sur l’activation d’une
opération de l’interface d’un composant : le démarrage de la transaction s’opère à l’ap-
pel de l’opération et la terminaison à la sortie de l’opération. Différents comportements
transactionnels peuvent être définis :
– NotSupported : si le thread appelant l’opération avec ce comportement transactionnel
est associé à un contexte transactionnel, celui-ci est suspendu le temps d’exécuter
cette opération.
– Required : si le thread appelant l’opération avec ce comportement transactionnel
n’est associé pas à un contexte transactionnel, un nouveau contexte est activé le
temps d’exécuter cette opération. Dans le cas contraire, l’opération s’exécute dans
le contexte transactionnel courant sans toucher à l’association entre le thread et le
contexte transactionnel.
– RequiresNew : le thread appelant l’opération avec ce comportement transactionnel
suspend le contexte transactionnel courant s’il existe et associe un nouveau contexte
transactionnel le temps d’exécuter cette opération.
– Mandatory : si le thread appelant l’opération avec ce comportement transactionnel
n’est pas associé à un contexte transactionnel, une exception est levée par le conte-
neur.
– Supports : si le thread appelant l’opération avec ce comportement transactionnel est
associé à un contexte transactionnel, cette opération s’exécute dedans sans toucher
à l’association entre le thread et le contexte transactionnel.
136 CHAPITRE 5. LA PLATE-FORME J2EE
Figure 5.12 – Le cycle de vie d’un composant métier EJB de session avec état
Etant données les propriétés que doivent supporter ces composants, il est proposé de
pouvoir les passiver de façon à éviter d’emcombrer la mémoire du serveur dans le cas d’un
nombre important de sessions à gérer en parallèle. Cette étape, correspondant à un nouvel
état dans le cycle de vie du composant de session (cf. figure 5.12), implique que lors d’une
5.6. PROGRAMMATION DE L’ÉTAGE MÉTIER 137
Comme pour les composants de présentation (les servlets), les composants réactifs ne
peuvent pas être invoqués de l’extérieur du conteneur, ce qui n’est pas le cas des composants
de session pour lesquels l’allocation et la libération d’instance ou encore l’activation du
code métier sont pilotées de l’extérieur du conteneur. Nous verrons que c’est aussi le cas
pour les composants entité.
Les composants réactifs supportent des comportements transactionnels plus limités que
les composants de session. En fait, seuls deux comportements transactionnels peuvent être
spécifiés pour l’opération onMessage :
– Required : un contexte transactionnel est alloué et est associé au thread uti-
lisé pour exécuter onMessage le temps de son exécution. Le message est nor-
malement consommé de manière transactionnelle (dépendant des capacité tech-
nique de l’émetteur de message). Cela signifie que si la transaction exécutée par
onMessage échoue, une nouvelle tentative de consommation du message sera effec-
tuer ultérieurement (nouvelle activation de onMessage).
138 CHAPITRE 5. LA PLATE-FORME J2EE
Le cycle de vie d’un composant entité est assez semblable aux précédents. La principale
différence consiste à exhiber le passage par une réserve d’instances pour leur allocation.
Etant donné que ces instances peuvent être manipuler en grand nombre et de manière
récurrente d’une transaction à l’autre, il est important d’optimiser leur processus d’alloca-
tion. En fait, les instances qui sont dans la réserve ont a priori exécuté tout leur cycle de
configuration (mise en liaison avec les ressources dont elles dépendent). Lors de l’allocation
d’une instance, le conteneur en cherche une disponible dans la réserve avant d’allouer un
nouvel objet Java à configurer.
Pour passer dans l’état Prêt, il faut qu’une instance soit liée à un son représentant
dans la base (en général un n-uplet dans une table). Cette liaison est définie par le nom
dans la base de ce représentant, c’est à dire sa clé primaire. Cette liaison peut être créée
de plusieurs manière :
– Création d’une nouvelle instance : la création d’une nouvelle instance de composant
entité crée implicitement un représentant dans la base et donc provoque la création
d’un nouveau nom définissant la liaison.
5.6. PROGRAMMATION DE L’ÉTAGE MÉTIER 139
pas besoin d’implanter des interfaces. Il suffit de définir une correspondance entre des
opérations du composant et les événements qui peuvent être traités.
Le service d’échéanciers (en anglais Timer Service) est accessible aux composants (sauf
pour les composants de session avec état qui ne sont pas pris en compte dans la version 2.1
de la spécification EJB) ou plus précisément à leurs instances à travers leur contexte. Il per-
met donc de créer de nouveaux échéanciers qui sont associés aux instances de composants
de la manière suivante :
– Pour les composants de session sans état et les composants réactifs : les échéanciers
créés ne sont en fait pas associés à une instance particulière puisque dans le cas de
ces composants, il n’y a pas de distinction entre les instances. Lorsqu’un événement
temporel est émis par le conteneur, celui-ci active une instance quelconque du com-
posant pour le traiter.
– Pour les composants entité : les échéanciers sont effectivement associés à l’instance
qui les crée. En fait, l’association est faite avec l’identifiant (basé sur la clé primaire)
de l’instance. Cela permet au conteneur d’activer la bonne instance de composant
pour traiter les échéances d’événements.
Dans tous les cas, les échéanciers ainsi définis sont persistants. En effet, ils doivent
survivre à l’arrêt ou à la panne du serveur qui les héberge. Si des échéances sont interve-
nues pendant de tels arrêts, tous les événements correspondants doivent être émis lors du
redémarrage.
On comprend bien que cette approche ne peut pas être utilisée pour les composants
de session avec état car les instances de ceux-ci ont une identité propre mais ne survive
pas à l’arrêt du serveur qui les héberge. Des échéanciers persistants n’ont donc pas de sens
pour de tels composants. Leur support dans de futures versions de la spécification ne doit
néanmoins pas poser de problème.
notamment le cas pour les EJB où la version 3.0 des spécifications simplifie largement le
développement.
144 CHAPITRE 5. LA PLATE-FORME J2EE
Intergiciel et Construction d’Applications Réparties
c
2006 M. Riveill, A. Beugnard, D. Emsellem (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Chapitre 6
La plate-forme .NET
.NET1 est en passe de devenir une technologie fréquemment utilisée pour la réalisation
de projets informatiques. Dans ce chapitre nous abordons les principaux principes de
cette plate-forme, diverse et parfois complexe intégrant les standards actuels. La section
d’introduction met en évidence la complétude et la richesse de l’ensemble des bibliothèques
de la plate-forme .NET. Elle nous permet de fixer le vocabulaire pour différencier les
éléments de la plate-forme normalisés par Microsoft de ceux qui sont restés propriétaires
afin de pouvoir très rapidement comparer les différentes plates-formes de développement.
La section 6.2 insiste sur l’architecture générale de la plate-forme et sur ses principales
caractéristiques. La section 6.2.4 présente le modèle de composants et la machine virtuelle
permettant leur exécution. Les trois sections suivantes font respectivement le lien entre
les composants .NET et les services techniques (6.4), l’étage d’accès aux données (6.4.1)
et l’étage de présentation (6.3.2) présents dans toute architecture à N étages (N-tier ). La
section 6.3 est constituée d’un petit exemple permettant d’illustrer les principaux principes
de la plate-forme. Le chapitre se termine par une évaluation qualitative et quantitative et
par une présentation de l’évolution future de cette plate-forme.
– des outils de développement dont le cœur est constitué de Visual Studio .NET ;
– un ensemble de systèmes serveurs représentés par Windows Server 2003, SQL Server
ou Microsoft Biztalk Server qui intègrent, exécutent et gèrent les services Web et les
applications ;
– un ensemble de systèmes clients représenté par Windows XP, Windows CE ou Mi-
crosoft Office 2003.
En fait, Microsoft .NET est essentiellement un environnement de développement et
d’exécution reprenant, entre autres, les concepts de la machine virtuelle de Java, via le
CLR (Common Language Runtime). Le principe, bien connu, est le suivant : la compilation
du code source génère un objet intermédiaire dans le langage MSIL (Microsoft Intermediate
Language) indépendant de toute architecture de processeur et de tout système d’exploi-
tation hôte. Cet objet intermédiaire, est ensuite compilé à la volée, au sein du CLR, au
moyen d’un compilateur JIT (Just In Time) qui le transforme en code machine lié au
processeur sur lequel il réside. Il est alors exécuté.
Au contraire de Java, pour lequel le code intermédiaire (byte-code) est lié à un seul
langage source, le code intermédiaire de la plate-forme .NET est commun à un ensemble de
langages (C#, VB2 , C++, etc.). Le CLR en charge de son exécution contient un ensemble
de classes de base liées à la gestion de la sécurité, gestion de la mémoire, des processus et
des threads.
La figure 6.1 résume les différents aspects de ces normes qui concernent le langage
de programmation C# “inventé” pour l’occasion 3 et la plupart des interfaces internes
4 de la plate-forme. Cette même figure présente les principales implémentations de ces
spécifications.
2
VB.NET est la nouvelle version de Visual Basic, il intègre la notion d’objets qui existe dans .NET et
les programmeurs de l’actuelle version 6.0 sont invités à faire migrer leurs codes
3
ECMA 334 puis ISO/IEC 23270 : C# Langage Specification
4
ECMA 335 puis ISO/IEC 23271 : Common Langage Interface (CLI)
6.2. ARCHITECTURE GÉNÉRALE ET CONCEPTS 147
6.1.2 Implantations
Rotor - SSCLI
Le code source partagé CLI (Common Language Infrastructure) 5 [Stutz et al. 2003]
correspond à une implémentation fonctionnelle des spécifications du langage C# et de
la CLI normalisées par l’ISO. Cette implémentation va au delà de ces spécifications en
proposant un environnement d’exécution, des bibliothèques de classes, le compilateur C#
et JScript, un débogueur, des outils, la couche PAL (Platform Adaptation Layer), une
version portable du système et portée sur Windows XP, FreeBSD et MacOS X, ainsi
qu’une suite complète de test. Elle est proposée à des fins non commerciales via la licence
SSCLI dont les principales caractéristiques sont :
– le logiciel doit être utilisé à des fins non commerciales (enseignement, recherche
universitaire ou expérimentations personnelles). Il peut toutefois être distribué au
sein d’ouvrages ou autres supports de cours liés à l’enseignement, ou publié sur des
sites Web dont le but est de former à son utilisation.
– toutes utilisations du logiciel à des fins commerciales sont prohibées.
– le logiciel peut être modifié et le résultat de ces modifications peut être distribué dans
un but non commercial à la condition de ne pas accorder de droit supplémentaire.
– le logiciel est livré sans garanties.
Autres implémentations
Suite à l’adoption officielle de ces deux standards par l’ECMA en décembre 2001, et
plus tard par l’ISO2 en avril 2003, la réaction ne se fit pas attendre. Différentes implanta-
tions du CLI basées sur la spécification ECMA ont vu le jour permettant ainsi l’exécution
et le développement d’applications .NET sur des systèmes d’exploitation non Microsoft
comme Linux, FreeBSD et MacOS. Nous pouvons par exemple citer deux initiatives lo-
giciels libres (open-source), indépendantes de Microsoft, et ayant une réelle importance :
Mono 6 [Edd Dumbill and M. Bornstein 2004] et DotGNU 7 [Weatherley and Gopal 2003].
Un autre concept majeur, lié au CLR est celui de code géré, c’est à dire de code
exécuté sous le contrôle de la machine virtuelle. Dans cet environnement, un ensemble
de règles garantissent que les applications se comporteront d’une manière uniforme, et ce
indépendamment du langage ayant servi à les écrire.
Pour répondre à ce souci d’interopérabilité entre les langages, la plate-forme .NET
contient une bibliothèque de classes très complète, utilisable depuis tous les langages et
permettant aux développeurs d’utiliser une même interface de programmation pour toutes
les fonctions offertes.
Dans .NET, le langage lui-même est essentiellement une interface syntaxique des bi-
bliothèques définies dans le CLR. Grâce au jeu commun de ces bibliothèques, tous les
langages disposent théoriquement des mêmes capacités car ils doivent, exception faite de
la déclaration des variables, passer par ces bibliothèques pour créer des threads, les syn-
chroniser, sérialiser des données, accéder à internet, etc.
Fichier calculatrice.cs
using System;
6.2. ARCHITECTURE GÉNÉRALE ET CONCEPTS 149
class MaCalculatrice {
public int Add(int a, int b) {
return a+b;
}
public int Sub(int a, int b) {
return a-b;
}
}
Fichier application.cs
using System;
using System.IO;
using System.Reflection;
registre. .NET permet aussi de gérer dans le GAC plusieurs versions du même fichier et
de rediriger à la demande les appels vers un assemblage vers une version plus récente si
c’est souhaité par le concepteur de l’application. En effet, grâce aux métadonnées et à
la réflexion, tous les composants sont désormais auto-descriptifs, y compris leur version.
Cela à pour conséquence, que toute application installée est automatiquement associée aux
fichiers faisant partie de son assemblage.
Une partie de la sécurité du code repose sur la possibilité de signer celui-ci à l’aide
d’une clé privée afin de pouvoir identifier ultérieurement la personne ou l’organisation qui
a écrit ce code à l’aide de la clé publique associée. Si l’exécution dans le répertoire courant
ne nécessite pas la signature du code, toute publication dans le GAC ainsi que la gestion
des versions dans celui-ci le nécessite.
veur (stub/skeleton) n’est nécessaire pour accéder à un composant depuis un autre langage
ou un autre processus. Il n’est pas nécessaire de déployer des informations de configuration
séparées pour identifier les attributs de services requis par le développeur. Plus important
encore, les métadonnées étant générées à partir du code source durant le processus de
compilation et stockées avec le code exécutable, elles ne sont jamais désynchronisées par
rapport à celui-ci.
Le rôle du composant est de plusieurs natures.
– C’est une unité de déploiement unique. C’est une unité d’exécution unique. Chaque
composant est chargé dans un domaine d’application différent lui permettant ainsi
d’être isolé des autres composants de l’application.
– Il intègre un mécanisme de gestion de version spécifique.
– Il permet également de définir la visibilité des types qu’il contient afin d’exposer
publiquement ou non certaines fonctionnalités vers d’autres domaines d’application.
– Il permet de sécuriser l’ensemble des ressources qu’il contient de manière homogène
car c’est à la fois une unité de déploiement, d’exécution (au sein d’un domaine
d’application) et une unité pour la gestion des versions.
d’une application peut se limiter à la copie de fichiers dans une arborescence de répertoires.
Les informations de configuration sont stockées dans des fichiers XML qui peuvent être
modifiés avec tout éditeur de texte. La tâche se complique légèrement si des composants
non-gérés (unmanaged components) sont nécessaires au fonctionnement de l’application.
Le terme ”unmanaged” rencontré ci-dessus fait référence au processus de compilation
des composants .NET.
– Soit la compilation du composant respecte toutes les spécifications de la plate-forme
.NET et alors l’exécution de ce composant est complètement prise en charge par la
machine virtuelle .NET et l’on parle alors de code ”managed”. Le composant associé
est lui aussi déclaré ”managed”.
– Soit la compilation du composant ne respecte pas toutes les spécifications de la
plate-forme .NET, et c’est particulièrement le cas si l’on souhaite reprendre sans
modification du code existant (C ou C++ par exemple) manipulant directement
des pointeurs. Alors la machine virtuelle .NET isole ce composant dans un espace
spécifique, n’assure pas pour ce composant une gestion mémoire spécifique (pas de
ramassage des miettes) et l’on parle alors de code ”unmanaged” et de composants
”unmanaged”.
Gestion de version
et tableaux). Les valeurs sont directement allouées dans la pile. La valeur d’un tel type
contient directement les bits représentant la donnée valeur. Par exemple une valeur de
type int correspondra à une case mémoire de 8 octets. Afin de garantir l’intégrité des
types à l’exécution, le CLR garde la trace des types contenus dans la pile. Les références
sont allouées dans le tas.
Un type valeur contient directement sa valeur tandis qu’une référence désigne l’objet
qui contient la valeur. On retrouve ici la différence entre variable et pointeur dans certains
langages de programmation. La conséquence de cette distinction est que deux variables
sont nécessairement distinctes et que la modification de la valeur de l’une n’aura pas
d’incidence sur l’autre tandis que deux références peuvent désigner le même objet et que
la modification de la valeur de celui-ci sera aussi perçue par l’autre.
Par exemple au type System.Boolean du moniteur d’exécution .NET correspond le
type C# bool et le type VB Boolean. Idem pour les types System.Byte, System.Char,
System.Object qui sont respectivement couplés avec les types byte, char et object de
C# et les types Byte, Char et Object de VB.
Dans .NET les deux types valeur et référence sont unifiés pour les différents types de
base du langage. Il est ainsi possible de transformer un type valeur en un type de référence
par le mécanisme appelé empaquetage ou objectification (boxing). Celui-ci transforme le
type primitif en son homologue objet. L’opération inverse est appelée dépaquetage (un-
boxing) (voir figure 6.4)
6.2.6 Le langage C#
Comme nous l’avons vu, la plate-forme .NET n’impose a priori aucun langage de
programmation. En effet, toute compilation conduit à un ensemble de types suivant la
spécification du système de types commun .NET (CTS) et le code se retrouve sous
forme de langage intermédiaire (MSIL). Malgré cela un nouveau langage nommé C#
[Gunnerson and Wienholt ] [Ben Albahari et al. 2002] est apparu en même temps que la
plate-forme. Ce langage a fait également l’objet d’une normalisation auprès de l’ECMA et
de l’ISO. C’est le langage de référence de la plateforme .NET puisqu’il permet d’en ma-
nipuler tous les concepts : des instructions du MSIL au système de types .NET (classes,
structures, énumérations, interfaces, délégués, tableaux, opérateurs, constructeurs, des-
156 CHAPITRE 6. LA PLATE-FORME .NET
– Passage par référence : dans ce mode, c’est une référence vers un objet qui est
envoyée au client. Une modification de l’objet soit du coté serveur, soit du coté client
entraı̂nera la modification de l’objet désigné dans le domaine d’application où réside
l’objet. Ce type d’objet implémente la classe MarshalByRefObject. On parle alors
de service .NET remoting ou encore de composant .NET Remoting.
Un composant .NET Remoting peut être activé de différentes manières. La création
d’une instance du composant sur le serveur sera soit à la charge du serveur lui-même (on
parle alors de service «activé par le serveur») soit par le client (on parle de service «activé
par le client»). Le cycle de vie des services Remoting est également variable. On distingue
ainsi trois types de service .NET Remoting :
– Les services « activés par le serveur à requête unique » (mode SingleCall)
Pour chaque invocation d’une méthode du service par un client, une nouvelle instance
spécifique du composant est créée pour répondre à la requête. C’est le serveur qui
est en charge de l’instanciation du composant.
– Les services « activés par le serveur et partagés» (mode Singleton) La même
instance du composant serveur est utilisée pour répondre aux différentes requêtes
des différents clients. Ce mode permet un partage des données entre les clients. C’est
le serveur qui est en charge de l’instanciation du composant.
– Les services « activés par le client » Dans ce mode c’est au client de demander
explicitement la création d’une instance du composant dans l’application serveur
par un appel explicite à l’opérateur new. Le composant serveur répondra alors aux
différentes requêtes d’un même client
Fichier EchoContract.cs
namespace Div.ServiceContract {
[Serializable]
public class QuotientEtReste {
public int Quotient;
public int Reste;
}
Ces deux classes définissent la base du «contrat» entre le client et le service. Lorsque le
service est activé côté serveur (mode SingleCall ou Singleton), seules ces classes seront
partagées par les deux parties.
Pour créer le service lui même il faut ensuite coder une classe implémentant cette
interface et la faire hériter de la classe du classe System.MarshalByRefObjet indiquant
au runtime .NET que les instances de cette classe ne seront pas sérialisées par copie mais
par référence.
namespace Div.ServiceImpl {
using Div.ServiceContract;
Notre service possède deux constructeurs. Le constructeur par défaut (sans paramètre)
est obligatoire si l’on publie le service en mode « activé côté serveur ». En effet ce sera au
runtime du Remoting d’instancier de manière transparente les instances de ce service. Nous
définissons également un constructeur prenant une chaı̂ne de caractère en paramètre et ce
afin que le service soit identifié lorsque nous le testerons. Ce constructeur nous permettra
de voir les différences à l’exécution entre les différents types de cycle de vie du service (lors
de l’instanciation).
Il reste maintenant à créer l’application qui hébergera notre service. Nous allons créer
deux applications console. L’une hébergera le service sous les deux modes « activé côté
serveur » SingleCall et Singleton. La deuxième application hébergera les instances du
service en mode «activé par le client». Il existe deux manières de spécifier au framework ces
paramètres : soit par programmation soit à l’aide de fichier de configuration. L’utilisation
de fichier de configuration est la méthode à privilégier puisqu’elle permet de modifier les
paramètres de déploiement sans recompiler l’application. Nous présentons ici l’approche
par programmation. La deuxième approche par fichier de configuration sera présentée dans
le chapitre suivant .
namespace Div.Host.SAO {
using Div.ServiceContract;
using Div.ServiceImpl;
class DivHost {
[STAThread]
6.3. APPLICATIONS DISTRIBUÉES 161
Tout d’abord nous indiquons au Remoting le canal de transport à utiliser. Nous créons
une instance de canal HTTP sur le port 8081 que nous enregistrons auprès du gestionnaire
de canal ChannelServices. Dans un deuxième temps nous devons enregistrer notre service
auprès du Remoting. C’est le rôle de la méthode RegisterWellKnonwnServiceType pour
les services « activés par le serveur ». Cette méthode prend en paramètre la classe des
futures instances du service, une adresse URI relative au canal de transport utilisé, ainsi que
le mode de publication choisi pour le service. Nous appelons cette méthode une première
fois pour publier le service en mode Singleton et une seconde fois pour publier le service
en mode SingleCall.
Voyons à présent comment la publication du service s’effectue en mode « activé par le
client »
class DivHost {
[STAThread]
static void Main(string[] args) {
// création et enregistrement du canal HTTP 8082
ChannelServices.RegisterChannel(new HttpChannel(8082));
RemotingConfiguration.RegisterActivatedServiceType
(typeof(Div.ServiceImpl.DivService));
Console.WriteLine("Appuyez sur [Entrée] pour quitter.");
Console.ReadLine();
}
}
}
La différence réside dans la manière de publier le service. Cette fois c’est la méthode
RegisterActivatedServiceType qui est utilisée. Elle indique au Remoting qu’il s’agit
d’une classe de service à enregistrer dans le mode « activé par le client » et ne prend en
paramètre que la classe du service.
162 CHAPITRE 6. LA PLATE-FORME .NET
service=(IDivService)
Activator.GetObject(typeof(Div.ServiceContract.IDivService),
"http://localhost:8081/division/singlecall");
Test("2)MODE SingleCall",service);
}
double RESULT1=service.Division(100.0,70.0);
Console.WriteLine("100.0 / 70.0="+ RESULT1);
QuotientEtReste QR=service.DivisionEntiere(100,70);
Console.WriteLine("100 / 70 -> Q="+ QR.Quotient+" R="+QR.Reste);
En mode « activé par le client », l’obtention de la référence distante sur le service est
assez différente. Cette fois c’est la méthode CreateInstance de la classe Activator qui
est utilisée. Celle-ci prend en paramètre l’url (dans un tableau d’objet) de l’application
qui héberge les instances du service. C’est à ce moment que le serveur crée l’instance
6.3. APPLICATIONS DISTRIBUÉES 163
qui servira le client. Le client peut alors appeler différentes méthodes sur cette même
instance du service. Le Remoting utilise un système paramétrable de « bail » permettant
de contrôler le cycle de vie de cette instance. Lorsque le bail expire après un certain temps,
le serveur détruira l’instance. Chaque invocation du client sur le service renouvellera ce bail
pour une durée donnée. Ce mécanisme de bail existe également dans les mode SingleCall
et Singleton.
Evolution future
Un aspect à prendre en compte lorsque l’on utilise le .NET Remoting est le couplage
qu’il créé entre le client et le serveur. En effet ces deux entités doivent partager un même
assembly .NET : celui contenant l’interface du service. Les changements de version du
service impactant l’interface implique également la modification du client. Dans le nouveau
framework Indigo, le découplage entre consommateur et fournisseur de service devient réel
puisque les deux entités partageront non plus une interface, mais plutôt un contrat de
service dans un format standard tel que WSDL.
Dans ce modèle, une application web est un ensemble d’URL dont la racine est une URL
de base. Il englobe donc les applications web générant des pages pour un navigateur et les
services web. Ce modèle de programmation d’applications web est une évolution d’ASP
(Active Server Pages). ASP.NET utilise les avantages du Common Language Runtime et
de la plate-forme .NET pour fournir un environnement fiable, robuste et évolutif aux appli-
cations web. ASP.NET bénéficie aussi des avantages du modèle d’assemblage du Common
Language Runtime pour simplifier le développement d’applications. Il fournit en outre,
des services pour faciliter le développement d’applications (tels que les services de ges-
tion d’état) et des modèles de programmation haut niveau (tels que ASP+ Web Forms et
ASP.NET Services Web).
L’environnement d’exécution HTTP dans la plate-forme .NET est au cœur d’ASP.NET
pour le traitement des requêtes HTTP. Cette machine virtuelle haute performance est
basée sur une architecture de bas niveau similaire à l’architecture ISAPI traditionnellement
fournie par Microsoft Internet Information Services (IIS). Cet environnement d’exécution
HTTP est constitué de ” managed code ” exécuté dans un processus spécifique. Il est
responsable du traitement de toutes les requêtes HTTP, de la résolution de l’URL de
chaque requête vers une application et de la distribution de la requête à l’application pour
traitement complémentaire. ASP.NET utilise le modèle de déploiement de la plate-forme
.NET basé sur les assemblages et permet la mise à jour des applications à chaud. Les
administrateurs n’ont pas besoin d’arrêter le serveur Web ou l’application pour mettre à
jour les fichiers d’application. Ceux-ci ne sont jamais verrouillés de façon à pouvoir être
remplacés même lors de l’exécution de l’application. Lorsque les fichiers sont mis à jour, le
système bascule vers la nouvelle version. Le système détecte les modifications de fichiers,
lance une nouvelle instance de l’application avec le nouveau code et commence à router les
requêtes entrantes vers cette application. Lorsque toutes les requêtes en suspens traitées
par l’instance de l’application existante ont été traitées, cette instance est arrêtée.
un processus distinct et peut même être configuré pour être stocké sur un ordinateur dis-
tinct. Ceci rend l’état session utilisable lorsqu’une application est déployée sur un ensemble
de serveurs web. L’état utilisateur est similaire à l’état session mais en général, il n’ex-
pire jamais et est persistant. L’état utilisateur est donc utile pour stocker les préférences
de l’utilisateur et d’autres informations de personnalisation. Tous les services de gestion
d’état sont implantés en tant que modules HTTP et peuvent en conséquence être facile-
ment ajoutés ou supprimés du pipeline d’une application. Si des services de gestion d’état
autres que ceux fournis par ASP.NET sont nécessaires, ils peuvent être fournis par un
module tiers.
ASP.NET fournit aussi des services de cache pour améliorer les performances. Un cache
de sortie permet d’enregistrer les pages générées en entier et un cache partiel permet de
stocker des fragments de pages. Des classes sont fournies pour permettre aux applications,
aux modules HTTP et aux gestionnaires de requêtes de stocker à la demande des objets
arbitraires dans le cache.
namespace Div {
public class DivWebService: WebService {
[WebMethod]
public double Division(double A, double B) {
return A/B;
}
166 CHAPITRE 6. LA PLATE-FORME .NET
Pour qu’une classe puisse être exposée via un service web, celle-ci doit hériter de la
classe du framework System.Web.WebServices.WebService. De plus chaque méthode qui
devra être publiée devra être précédée de l’attribut [WebMethod] Nous pouvons remarquer
dans notre exemple que la méthode UneMethodeNonPubliee ne sera pas publiée dans
l’interface du service web car sa signature n’est pas précédée de l’attribut [WebMethod].
Ainsi cette méthode ne sera pas accessible aux clients. Nous compilerons le code dans une
bibliothèque DivWS.dll grace à la ligne de commande suivante :
Pour pouvoir tester notre service une page div.asmx utilisant la notion de ”code be-
hind” doit être mise en œuvre. Nous spécifions dans son entête qu’il s’agit d’un web service
ainsi que le nom du fichier source C# correspondant à cette classe à des fins de débogage.
Le fichier div.asmx
A présent, il nous faut déployer notre service au sein d’une application web hébergée
par le serveur IIS de Microsoft. Pour cela nous pouvons utiliser la console d’administration
de Windows afin de créer un nouvelle application. Nous la nommerons par exemple icar.
Dès lors l’application aura comme racine distante l’url http ://localhost/icar. Créons
un répertoire bin dans lequel nous plaçons la bibliothèque compilée précédemment. La page
aspx du service sera placé à la racine de l’application. Le service est alors opérationnel. Il
peut immédiatement être testé en utilisant un navigateur web quelconque et en allant à
l’adresse http ://localhost/icar/div.asmx ?WSDL. Cette page permet de visualiser le
contrat WSDL de notre service. Pour tester le service lui même il faut se rendre à l’adresse
http ://localhost/icar/div.asmx. .NET génére automatiquement un formulaire web
qui permet d’appeler les différentes WebMethod du service web.
Nous allons voir a présent comment consommer un service web. Nous prendrons comme
exemple un service Web assez pratique : celui offert par Google
. c Avant toute chose
il faut disposer de la description WSDL de ce service. Celle-ci se trouve à l’adresse
http ://api.google.com/GoogleSearch.wsdl
Le framework .NET fournit un utilitaire wsdl.exe permettant à partir de cette des-
cription de générer les classes proxy permettant de consommer le service web. Voici la
ligne de commande permettant d’obtenir le code C# du proxy :
Le fichier généré google.cs contient différentes classes : celles du proxy vers le service
Web et les classes de données manipulées par le service.
Voici à présent un programme client simple permettant d’effectuer une correction d’un
mot mal orthographié.
class TestGoogle {
static void Main(string[] args) {
string GOOGLE_KEY="-xxxxx-";
string phrase="Intergicielle";
GoogleSearchService service=new GoogleSearchService();
string rep=service.doSpellingSuggestion(GOOGLE_KEY,phrase);
Console.WriteLine(‘‘Correction: ‘‘+rep);
Console.ReadLine();
}
}
Le programme une fois exécuté affichera le texte corrigé « Intergiciel » Dans notre
exemple nous utilisons l’opération doSpellingSuggestion du Web Service en lui donnant
comme entrée d’une part une clé d’authentification (fournie par Google
c permettant
d’accéder au service web après enregistrement en ligne) et en deuxième paramètre la
phrase à corriger.
6.3.3 L’Asynchronisme
Il existe deux possibilités pour effectuer des appels asynchrones de méthode dans .NET.
La première utilise les appels asynchrones de méthode disponible dans la plate-forme .NET
et la seconde repose sur les files de messages disponible dans la plate-forme Windows. Nous
détaillons ci-après ces deux possibilités.
Appel asynchrone
Pour pouvoir effectuer en .NET des appels asynchrones de méthode, il est nécessaire
d’utiliser le principe des délégués qui fournit pour chacun d’entre eux deux méthodes :
BeginInvoke () et EndInvoke (). BeginInvoke() permet d’effectuer l’appel asynchrone et
EndInvoke () permet de récupérer les résultats de cet appel. La synchronisation entre
l’appel et la lecture du résultat est à la charge du programmeur qui peut :
– effectuer une attente bloquante du résultat en appelant la méthode EndInvoke (),
– être prévenu de la disponibilité du résultat en transmettant lors de l’appel, le code
à exécuter pour récupérer le résultat.
Voici un exemple illustrant ces deux possibilités :
class Exemple {
public delegate int Prototype(int a, int b);
dataset.AcceptChanges();
6.4.2 Sécurité
Les services de sécurité intégrés à la plate-forme permettent de s’assurer que seuls les
utilisateurs autorisés accèdent aux ressources d’un ordinateur et seul le code ayant acquis
les droits suffisants peut exécuter les actions nécessitant ces droits. Ceci améliore la sécurité
globale du système ainsi que sa fiabilité. L’environnement d’exécution étant utilisé pour
charger le code, créer les objets et effectuer les appels de méthodes, il peut contrôler la
6.4. COMPOSANTS ET SERVICES TECHNIQUES 171
Sécurité d’accès utilisateur La sécurité d’accès utilisateur sert à déterminer les droits
associés à l’identité en cours. Il est possible de vérifier ce qu’un appelant a le droit de faire
de nombreuses façons. Dans les applications avec une interface utilisateur, il est possible
d’emprunter l’identité de l’appelant, mais il est aussi possible que le code utilise un compte
de ” service ” spécifique par modification de l’identité en cours d’exécution (modification
de l’objet Principal).
Les droits de l’utilisateur sur l’environnement et la plate-forme sont généralement
contrôlés à l’aide de listes de contrôle d’accès qui sont comparées à l’identité Windows
du thread en cours.
La technologie .NET fournit une infrastructure complète et extensible pour la gestion
de la sécurité d’accès utilisateur y compris des identités, des permissions et la notion
d’objet Principal et de rôles. Pour garantir que les utilisateurs appartenant à un certain
rôle appellent une méthode donnée, il suffit de définir un attribut sur la méthode de classe,
comme indiqué dans le code suivant :
[PrincipalPermission(SecurityAction.Demand, role="Managers")]
public void managersReservedMethod () {
// Ce code ne s’exécutera pas si le Principal associé au
// thread n’a pas ‘‘Managers’’ lorsque IsInRole est appelé
172 CHAPITRE 6. LA PLATE-FORME .NET
[SecurityRole("Managers")]
public void managersReservedMethod () {
// Ce code ne s’exécutera pas si le Principal associé à
// l’utilisateur n’est pas dans le groupe ‘‘Managers’’
}
Sécurité d’accès au code La sécurité d’accès au code permet de définir les droits de
l’assembly, mais il est également possible de décider du lancement ou non du code, en fonc-
tion du code essayant d’y accéder. Par exemple, cela permet d’empêcher que des objets
soient appelés à partir de scripts lancés de manière inopportune par une personne dispo-
sant de privilèges suffisants. Vous pouvez contrôler l’accès au code à l’aide des éléments
suivants :
– le répertoire d’installation de l’application ;
– le hachage cryptographique de l’assembly ;
– la signature numérique de l’éditeur de l’assembly ;
– le site dont provient l’assembly ;
– le nom fort cryptographique de l’assembly ;
– l’URL dont provient l’assembly ;
– la zone dont provient l’assembly.
Les politiques de sécurité peuvent être appliquées à l’entreprise, l’ordinateur, l’uti-
lisateur et l’application. Les zones définies par la technologie .NET sont les suivantes :
Internet, intranet, Poste de travail, NoZone, de confiance et non fiable.
– Protocole SSL (Secure Sockets Layer) recommandé pour les communications avec
le protocole HTTP. Il s’agit d’un standard très répandu et il est généralement
permis d’ouvrir des ports SSL sur les pare-feu.
– IPSec. Ce mécanisme constitue un choix judicieux lorsque les deux points d’entrée
de la communication sont bien connus et sous le contrôle de la même entité admi-
nistrative.
– Réseau privé virtuel (VPN) qui permet d’établir un transport IP point à point
sur Internet (ou d’autres réseaux).
– Sécurisation des données :
– Signature d’un message. Cela permet de détecter les messages falsifiés. Les signa-
tures peuvent être utilisées pour l’authentification au sein d’un même processus.
– Cryptage de l’ensemble d’un message. De cette façon, l’ensemble du message de-
vient illisible en cas de compromission des paquets du réseau. Le cryptage d’un
message à l’aide d’algorithmes appropriés permet également de détecter les mes-
sages falsifiés.
– Cryptage des parties sensibles d’un message si seulement des parties du message
doivent être protégées.
6.4.3 Transaction
Pour gérer des transactions distribuées avec plusieurs bases de données, il n’est pas
possible dans la technologie .NET d’utiliser ADO.NET 6.4.1 et il faut, comme pour la
sécurité utiliser le gestionnaire de transactions disponible dans Enterprises Services (DTC :
Distributed Transaction Coordinator). Pour illustrer la mise en œuvre des transactions
nous allons prendre l’exemple très classique du transfert bancaire entre deux comptes.
L’architecture de l’application est décrite dans la figure 6.9.
Ci-dessous, la partie essentielle du source de l’application :
[Transaction(TransactionOption.RequiresNew)]
public class TdF : ServicedComponent {
// hérite d’un objet Services Interprises
public void Transfert (String ctx_client1, ctx_client2,
int ID_client1, ID_client2, montant) {
Compte client1 = new Compte (); Compte client2 = new Compte ();
// création de la connection à la base de données
client1.Connect (ctx_client1); client2.Connect (ctx_client2);
// connection à la base de données pour lecture du solde du compte
int solde_client1 = client1.ConsulteCompte (ID_client1);
int solde_client2 = client2.ConsulteCompte (ID_client2);
// modification locale des montants des comptes
174 CHAPITRE 6. LA PLATE-FORME .NET
[Transaction(TransactionOption.Required)
public classe Compte : ServiceComponent {
public void Connect (String ctx_client) {
// ouverture de la connexion à la base de donnée
}
public int ConsulteCompte (int ID_client) {
// connexion à la base de données pour lire le solde du compte
}
[autocomplete(true)]
public void MetAJour (int ID_Client, int montant) {
// connexion à la base de données pour modification du solde du compte
// fermeture de la connexion à la base de données
}
}
Il n’est pas nécessaire d’insérer dans le code du programme une manipulation explicite
des transactions, celle-ci se faisant par l’utilisation de l’attribut Transaction sur une classe
(dans l’exemple sur les classe TdF et Compte). Un composant peut adopter cinq compor-
tements différents face à la création ou à l’utilisation d’une transaction. Ils sont décrits
6.5. EXEMPLE : MISE EN ŒUVRE D’UNE APPLICATION COMPL ÈTE 175
L’avantage de déporter le traitement d’un service réside souvent dans le fait que les
clients de ce service ne disposent pas eux-mêmes des fonctionnalités leur permettant d’ef-
fectuer ce traitement sur le terminal sur lequel ils tournent.
sont donnés ici à titre indicatif mais peuvent être mis en œuvre de manière assez générale.
Nous commencerons tout d’abord par définir et implémenter les types de données
échangées par les différentes entités. Dans un deuxième temps, nous définirons l’interface
du service. Ces deux premières bibliothèques constitueront le sous ensemble minimal à
partager entre client et serveur dans notre application. A’ partir de ces deux briques,
nous développerons et déploierons le service .NET Remoting. Cela consistera en une bi-
bliothèque pour l’implémentation du service ainsi qu’une application pour héberger le
service. Ensuite, nous écrirons un client pour ce service afin de le tester. Afin de dépasser
les frontières de l’intranet, nous implémenterons ensuite le service web qui redirigera les
appels clients vers le service .NET remoting. Enfin nous écrirons un client simple de ce
service web.
namespace TTS.Schema {
[Serializable]
XmlType("sentence")]
public class Sentence {
public Sentence(){ }
public Sentence(string lang, string text) {
this.Language=lang;
this.Content=text;
}
XmlAttribute("lang")]
public string Language;
XmlElement("content")]
public string Content;
}
[Serializable]
[XmlType("result")]
public class TTSResult {
public TTSResult(){ }
public TTSResult(byte[] stream) {
this.AudioStream=stream;
}
XmlElement("audio")]
public byte[] AudioStream;
178 CHAPITRE 6. LA PLATE-FORME .NET
}
}
Chaque instance de ces données devra traverser les frontières du service et donc pouvoir
être sérialisé. Les deux classes sont donc marquées par l’attribut C# Serializable. Selon
la configuration dans laquelle sera publié notre service, la sérialisation pourra prendre
plusieurs formes. Si les données sont sérialisées via un formateur binaire, le résultat sera
une série d’octets suivant une spécification propriétaire du Remoting .NET. Dans le cas
ou le formateur de données sera un formateur SOAP, les données seront encodées sous
forme de document XML. Le framework s’occupe par défaut de tout ce qui a trait à
cette sérialisation. Par contre, si l’on veut pouvoir contrôler le format de ce document
XML, nous devons marquer la classe ainsi que ses champs et méthodes par des attributs
de sérialisation. Par exemple une instance de la classe sentence contenant ”EN” dans le
champ Language et ”Bonjour” dans le champ Content sera transformée en XML comme
suit :
<sentence lang="EN">
<content>BONJOUR</content>
</sentence>
Si aucun attribut de contrôle de la sérialisation XML n’avait été employé, le framework aurait
par défaut utilisé les noms de classe, champs et méthode, ce qui formaterait les instances comme
ci-dessous :
<Sentence Language="EN">
<Content>BONJOUR</Content>
</Sentence>
namespace TTS.ServiceInterface {
using TTS.Schema;
using System;
using TTSLibrary;
using TTS.Schema;
using TTS.ServiceInterface;
6.5. EXEMPLE : MISE EN ŒUVRE D’UNE APPLICATION COMPL ÈTE 179
namespace TTS.Service.Remoting {
public class SpeechService: MarshalByRefObject,ISpeechService {
public SpeechService() {
Console.WriteLine("Instance de ’SpeechService’ créée.");
}
Pour rendre le service accessible, nous devons choisir de le déployer dans un conteneur.
Ce conteneur peut être au choix une application, un service Windows ou encore le serveur
web IIS. Nous allons déployer notre service dans une application Console. Cette application
une fois exécutée hébergera les instances du service.
Nous avons vu dans la section précédente comment paramétrer ce déploiement par
programmation. Nous montrons ici comment effectuer ce paramétrage à l’aide d’un fi-
chier de configuration. Cette méthode est beaucoup plus souple puisque le service n’a
pas besoin d’être recompilé afin d’être déployé différemment. L’application hôte consis-
tera uniquement à charger le fichier de configuration et à se bloquer (ici par l’appel à
Console.ReadLine()) afin que l’application soit toujours disponible.
using System;
using System.Runtime.Remoting;
namespace TTS.Service.Remoting {
public class MainClass {
[STAThread]
static void Main(string[] args) {
RemotingConfiguration.Configure("server.config");
Console.WriteLine("Appuyez sur [Entrée] pour quitter.");
Console.ReadLine();
}
}
}
</application>
</system.runtime.remoting>
</configuration>
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Web.Services;
using TTS.Schema;
using TTS.ServiceInterface;
}
}
Notre service web dépend des différentes bibliothèques compilées précédemment. Il faut
placer ces bibliothèques dans le répertoire bin de l’application web hébergement le service
web.
182 CHAPITRE 6. LA PLATE-FORME .NET
using System;
namespace TTS.ClientWebService {
/// <summary>
/// Description résumée de Class1.
/// </summary>
6.6. EVALUATION 183
class Class1 {
/// <summary>
/// Point d’entrée principal de l’application.
/// </summary>
[STAThread]
static void Main(string[] args) {
TTS.WebService.sentence request = new TTS.WebService.sentence();
request.content = "Salut";
request.lang = "FR";
TTS.WebService.result result =
new TTS.WebService.TTSService().Speak(request);
Console.ReadLine();
}
}
}
6.6 Evaluation
6.6.1 .Net et les langages à objets
Comme on l’a vu dans les parties précédentes, .NET offre, grâce à une machine virtuelle,
les mécanismes nécessaires à la représentation de données et aux échanges de données
entre modules écrits dans des langages différents. Le but de cette première partie d’une
évaluation de .NET est de s’assurer que les comportements des principaux langages (VB,
C++, C# et J#) de la plate-forme .NET sont conformes à ceux attendus, en particulier
pour l’un des points essentiels de la programmation objet : la redéfinition et la surcharge.
On parle de redéfinition lorsqu’une méthode possède plusieurs définitions qui peuvent
participer à la liaison dynamique. Le compilateur ne peut pas choisir au moment de la
compilation et met en place une procédure de lookup qui sera déclenchée lors de l’exécution
du programme pour sélectionner la méthode appropriée ; en général, celle qui se trouve
le plus bas dans le graphe d’héritage. Pour la surcharge il s’agit en fait de l’utilisation
d’un même nom de méthode mais pour des méthodes que le compilateur considère comme
différentes et qui ne sont donc pas éligibles en même temps lors de la liaison dynamique. Les
langages à objets font des choix différents ; les redéfinitions peuvent être invariantes, cova-
riantes ou contravariantes et la surcharge est parfois interdite. Pour mettre ces différences
en évidence, Antoine Beugnard9 propose une expérimentation simple.
Nous considérons deux classes : Up, et Down qui est une sous-classe de Up. Chacune
des deux classes offre deux méthodes cv et ctv qui requièrent un paramètre unique. Les
paramètres sont des instances de trois classes : Top, Middle qui est une sous-classe de
Top et Bottom qui est une sous-classe de Middle. Pour tester tous les cas (covariants et
contravariants) nous définissons les classes Up et Down selon la figure 6.11. La méthode cv
parait covariante car les classes où elles sont définies et les classes des paramètres varient
dans le même sens, tandis que la méthode ctv parait contravariante puisque les classes
des paramètres varient dans le sens inverse des classes de définition de ctv.
Notre but n’est pas ici de discuter l’avantage de tel ou tel choix fait par les langages
à objet, mais simplement de montrer qu’ils offrent de nombreuses sémantiques et que le
9
L’ensemble de l’étude est disponible à l’adresse : http ://perso-info.enst-bretagne.fr/ beugnard/papier/lb-sem.shtml
184 CHAPITRE 6. LA PLATE-FORME .NET
framework .Net les respecte. Pour illustrer cela on crée, pour chacun des langages étudié,
un programme client qui instancie les objets puis essaye tous les appels possibles : les deux
méthodes (cv et ctv) étant appelées successivement avec les trois classes de paramètres
possibles (Top, Middle et Bottom) pour les trois objets récepteurs possibles : u de type Up
instancié avec un objet de classe Up, d de type Down instancié avec un objet de classe Down
et ud de type Up mais instancié avec un objet de classe Down. Le dernier cas est autorisé car
Down est une sous-classe de Up. Ce code peut parfois ne pas compiler ou ne pas s’exécuter
lorsque le système de type l’interdit.
Les résultats sont présentés sous la forme d’un tableau de 6 lignes (une
par appel possible) et 3 colonnes (une par objet récepteur possible). Chaque
case contient la classe dans laquelle a été trouvée la méthode effectivement
exécutée. Les sources et les résultats des exécutions peuvent être lus sur
http ://perso-info.enst-bretagne.fr/~beugnard/papier/lb-sem.shtml. Ces ta-
bleaux sont appelés signature du langage (vis-à-vis de la redéfinition et de la surcharge).
Nous reproduisons dans les tables 6.1 à 6.4 les signatures pour C++, C#, J# et
VB.Net. Ce sont les mêmes qu’avec d’autres compilateurs comme gcc ou javac (jusqu’à
la version 1.3) par exemple.
C++ u d ud
cv(Top) Up Compil. error Up
cv(Middle) Up Down Up
cv(Bottom) Up Down Up
ctv(Top) Compil. error Compil. error Compil. error
ctv(Middle) Compil. error Down Compil. error
ctv(Bottom) Up Down Up
On notera avec intérêt que bien que proches, ces langages ont tous une deuxième co-
6.6. EVALUATION 185
C# u d ud
cv(Top) Up Up Up
cv(Middle) Up Down Up
cv(Bottom) Up Down Up
ctv(Top) Compil. error Compil. error Compil. error
ctv(Middle) Compil. error Down Compil. error
ctv(Bottom) Up Down Up
J# u d ud
cv(Top) Up Up Up
cv(Middle) Up Down Up
cv(Bottom) Up Down Up
ctv(Top) Compil. error Compil. error Compil. error
ctv(Middle) Compil. error Down Compil. error
ctv(Bottom) Up Compil. error Up
lonne différente ! La différence entre ces 4 langages est due à des interprétations différentes
de la surcharge. En effet, la troisième colonne montre que cv et ctv sont en fait des
surcharges ; il aurait fallu voir apparaı̂tre des Down dans la troisième colonne pour avoir
de la liaison dynamique et donc des redéfinitions. Ces 4 langages n’acceptent que des
redéfinitions invariantes. Des expériences avec d’autres langages à objets (comme ADA95,
Dylan, Eiffel, OCaml ou Smalltalk) montrent une plus grande variété encore.
Ce qui nous intéresse ici est de conclure que le framework .Net permet de définir plu-
sieurs sémantiques et que les langages existants hors du framework ont la même sémantique
qu’avec le framework (C++ et J#/Java version 1.3).
VB.Net u d ud
cv(Top) Up Up Up
cv(Middle) Up Down Up
cv(Bottom) Up Down Up
ctv(Top) Compil. error Compil. error Compil. error
ctv(Middle) Compil. error Down Compil. error
ctv(Bottom) Up Up Up
Down dans le langage L2 pour former le composant C2. Cette extension, multi-langage est
tout à fait permise par la plate-forme .Net. Enfin, pour compléter l’expérimentation un
client C3 est écrit dans le langage L3. Pour mettre en œuvre cette expérimentation nous
avons utilisé les langages VB, C++ et C# qui forment le trio des langages de base de la
plate-forme .Net. Nous obtenons donc un total de 27 (3*3*3) assemblages possibles. Pour
présenter les résultats, nous construisons un tableau de 9 signatures pour chacun des 3
clients (C3). Les colonnes de chaque tableau représentent les langages d’implantation du
framework de référence C1 et les lignes, les langages d’implantation de l’extension C2.
Chaque cellule du tableau contient la signature observée.
Dans le tableau suivant, le client est en C#.
Extension/Framework C# VB C++
C# C# C# C++
VB C# C# C++
C++ C# C# C++
la signature de C++ (erreur de compilation ligne 1, colonne 2 de la table 6.1) est toujours
présente.
Dans le tableau suivant, le client est en Visual Basic.
Extension/Framework C# VB C++
C# VB VB C++
VB VB VB C++
C++ VB C# C++
Extension/Framework C# VB C++
C# VB/C++ VB/C++ VB/C++
VB C++ C++ C++
C++ C++ C++ C++
L’application utilisée pour ce test est simpliste par ses fonctionnalités mais permet de
mesurer le temps de démarrage d’initialisation de la partie graphique d’un client lourd
(cf. figure 6.14). Elle consiste à charger un formulaire permettant d’enregistrer un utilisa-
teur, photos comprise dans une base. Sans équivoque la version C# avec Winforms a été
nettement plus rapide que la version Java avec Swing.
Pour évaluer celle-ci, l’auteur du papier a choisi d’appeler une fonction permet-
tant de trouver le minima d’un tableau d’entiers. Ce service web a été implanté en
.NET et à l’aide de la plate-forme Java/axis puis en Java avec la plate-forme Glue
(www.themindelectric.com). A été mesuré uniquement (cf. figure 6.15) le temps de réponse
du service sans vouloir décomposer celui-ci entre les différentes activités : empaqueta-
ge/dépaquetage, transfert des données, activation du service, traitement du coté serveur.
6.6. EVALUATION 189
Using System.ServiceModel;
[ServiceContract]
public class AnnuaireWebService {
private AnnuaireService service;
public AnnuaireWebService() {
service=new AnnuaireService();
}
[OperationContact]
public CarteDeVisite Rechercher(string nom) {
return service.Rechercher(nom); ;
}
}
Les contrats peuvent porter sur les classes ou sur les méthodes, mais aussi sur les données
échangées afin de permettre le transport de données structurées. Pour chaque points de publication
d’un service, sans avoir à modifier le code de celui-ci, ni du client qui le consomme il sera possible
de préciser dans un fichier de configuration : le protocole de transport (par exemple : HTTP),
l’adresse du service mais aussi les services techniques associés aux appels (sécurité ou transaction)
basés sur les spécifications du W3C pour les Web services.
La construction d’un client Indigo sera identique (et proche de la manière de procéder actuelle-
ment pour le Web services) : à partir d’un proxy du service généré à partir du fichier de publication
du service, le client pourra accéder au service de la même manière quelque soit le protocole utilisé
(.NET remoting ou SOAP).
6.6. EVALUATION 191
Généricité En C++ les templates sont extrêmement utilisés et le fait qu’ils n’ont pas été im-
planté sous C# a été un des reproches majeurs à sa sortie. Microsoft a écouté les utilisateurs et l’a
mis en place en nommant dorénavant la technique Generic. Pour contourner l’absence de généricité,
les programmeurs C# utilisaient actuellement des collections d’objets non typés et sollicitaient
massivement l’usage coûteux du boxing/unboxing et de cast. L’introduction de la généricité de-
vrait permettre d’augmenter le typage statique des programmes et d’augmenter simultanément
l’efficacité de ceux-ci.
La généricité sera utilisée pour simplifier l’écriture des classes de collections qui puissent être
énumerable (c’est à dire interrogeables dans une boucle foreach).
Méthodes génériques Les méthodes anonymes sont aussi une manière d’alléger le code en
particulier dans la gestion des événements. Actuellement, il était nécessaire de déclarer un ’callback
d’événement’ par une méthode qui possédait un nom. Dorénavant, il suffira juste de donner le code
à exécuter :
dal.AjouterCarte(carte);
else
throw new Exception("La carte existe déjà");
}
}
6.7 Conclusion
La terre est envahie de composants COM, et si l’application que vous voulez construire
fait un usage intensif de cet univers là alors .NET est fait pour vous. Vous y trouverez
des outils de développement sophistiqués, des langages de programmation adaptés à vos
besoins, la possibilité de construire un composant en utilisant plusieurs langages de pro-
grammation grâce au langage de type commun. Mais même si vous souhaitez écrire une
nouvelle application .NET peut aussi être fait pour vous, voici quelques apports :
– Point d’accès : c’est le point fort par excellence de l’approche .NET, n’importe quel
client Web peut accéder à un service publié sur le Web. Il n’est pas nécessaire d’avoir
une configuration spécifique, d’installer une pile de protocole, de configurer un pare-
feu (firewall). L’intégration avec les standards du Web est totale, un client XML
et la disponibilité prochaine du protocole SOAP permettront cet usage et rendront
peutêtre inutile la programmation de script CGI, ceux-ci étant remplacés par des
composants .NET.
– Simplicité d’utilisation : .NET a été conçu dès l’origine comme une plate-forme à
composants distribués intégrant les standards de l’Internet XML et SOAP. Pour cela
il n’est pas nécessaire de d’envelopper (wrapper) les composants afin de les rendre
accessibles, il n’est pas non plus nécessaire d’ajouter du code lors du déploiement
pour permettre l’exécution du composant car celui-ci est inclus dans la plate-forme
ou directement associé avec les composants. Un composant est un objet simple qui
contient tout ce qui est nécessaire pour le manipuler : la description de ses interfaces,
la description de ses services, le code pour accéder aux services et les services eux
mêmes. Le déploiement est immédiat. .NET propose aussi avec ADO un modèle très
évolué pour l’accès aux données acceptant le mode déconnecté, permettant l’accès à
différents formats de base de données, etc. Toutes les données peuvent être accéder
immédiatement depuis n’importe quel client de la plate-forme qui se charge de les
transporter et de les convertir.
– Les performances : selon que l’on s’intéresse à la manière dont le service est rendu
ou l’accès au service, le point de vue est diamétralement opposé. En ce qui concerne
l’exécution des services, les performances sont au rendez-vous : code compilé, multi-
processeur, équilibrage de charge, etc. Par contre, les appels inter-service seront lents
par nature. En effet le choix d’utiliser le protocole SOAP afin de pouvoir être accédé
depuis n’importe quel type de station cliente se paye au prix fort. La technologie
proposée permet essentiellement de relier entre eux des composants ayant une grosse
granularité.
– Multiplicité des langages de programmation : .NET ne fait pas le choix d’un langage
de programmation à votre place, il vous laisse libre de choisir le langage que vous
voulez utiliser en fonction de vos usages ou de votre humeur. COM permettait de faire
6.7. CONCLUSION 193
coopérer des composants écrits dans des langages différents, .NET permet d’intégrer
les langages les uns avec les autres. Tous les langages sont a priori égaux, même si les
langages de types procéduraux sont plus proches de la machine virtuelle proposée. Il
par exemple est possible d’utiliser simultanément différents langages (un par classe
par exemple). Le typage est complet et les différents types du Common Language
Runtime, dont par exemple les threads sont disponibles dans les différents langages
de programmation de la plate-forme. Des compilateurs C++, C#, VB et Jscript sont
aujourd’hui disponibles et différentes compagnies ont déjà annoncé qu’elles allaient
sous peu fournir des compilateurs. Il est imaginable que dans peu de temps, des
compilateurs APL, ML, Cobol, Oz, Pascal, Perl, Python, Smalltalk et Java seront
disponible sur la plate-forme .NET.
194 CHAPITRE 6. LA PLATE-FORME .NET
Intergiciel et Construction d’Applications Réparties
c
2006 D. Donsez (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Chapitre 7
La plate-forme dynamique de
services OSGi
7.1 Introduction
L’OSGi 1 Alliance est un consortium industriel fondé en mars 1999 par une quinzaine de
sociétés membres. L’intention originelle de ces sociétés était de définir une spécification ou-
verte pour développer et déployer des télé-services sur des passerelles résidentielles comme
des décodeurs de TV numérique ou des modems-routeurs ADSL. La première version de la
spécification2 était strictement orientée par ce marché de niche. Néanmoins, cette première
spécification s’est rapidement révélée très intéressante pour des applications diverses que
personne n’imaginait à l’origine. Depuis, beaucoup d’autres sociétés se sont jointes au
consortium pour l’élaboration des versions successives de la spécification, dont l’objectif
s’est progressivement élargi dans la direction d’une plate-forme horizontale de déploiement
et d’exécution de services. Les domaines d’application d’OSGi couvrent désormais les
marchés de l’électronique grand public, de l’industrie, des moyens de transport (auto-
1
OSGi était l’acronyme de Open Service Gateway initiative. L’OSGi Alliance interdit désormais l’usage
de cette dénomination.
2
qui est repartie du JSR008 transféré par SUN à l’OSGi Alliance en novembre 1999
196 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
7.2 Motivations
La première motivation de l’OSGi Alliance était de définir un modèle d’administra-
tion d’un parc de passerelles dans lequel l’administration est transparente à l’utilisateur
béneficiaire des services qui lui sont offerts. Ce modèle que nous allons détailler est illustré
par la figure 7.1. Une passerelle est généralement un intermédiaire entre un réseau ouvert
(c.a.d. l’Internet) et un réseau local privé qui est généralement dédié à un domaine d’appli-
cation. Le réseau raccorde la passerelle à des équipements de l’environnement de l’usager
final. Ces équipements dont la fonction est très spécialisée et figée, sont communicants
mais ne sont pas généralement prevus pour être raccordés à un réseau ouvert.
! ! !
" "
# $ " %
$ $
'(
# )*
&
Ces services sont généralement liés aux équipements du réseau privé dont ils exploitent
les données enfouies et agissent sur les fonctions. Pour des questions de sécurité, l’opérateur
assure que les équipements éventuellement liés à un fournisseur (compteur électrique du
distributeur d’électricité, moniteur cardiaque de l’hôpital, caméras et centrale d’alarme de
la société de gardiennage) ne peuvent être utilisés que par les services du fournisseur. Les
services (et leurs fournisseurs) peuvent coopérer entre eux dans l’élaboration de services
plus complexes. Par exemple, en cas de malaise détecté par le moniteur cardiaque, l’hôpital
peut récupérer le flux vidéo des cameras opérées par la société de gardiennage.
Les services déployés interagissent également avec les serveurs d’entreprise des four-
nisseurs. L’interaction peut être à l’initiative du serveur ou de la passerelle. Par exemple,
le serveur du distributeur d’électricité procède à un télé-relevé en contactant le service
connecté au compteur électrique. L’autre exemple est celui du service pilotant le moniteur
cardiaque. Si ce dernier détecte une défibrillation du cœur du patient, il contacte le serveur
de l’hôpital qui dépêche aussitôt une équipe de secours.
Bundle
Bundle
Bundle
services
OSGi
Java VM
Système d’exploitation
Matériel
Figure 7.2 – Architecture multi-couche d’une plate-forme OSGi (avec l’aimable autorisation de
Peter Kriens).
La collaboration entre les applications . Les applications sur une plate-forme OSGi
collaborent par le biais de services 2.1. La plate-forme offre aux applications d’une part
un registre de services qui permet de rechercher des objets offrant un contrat de service
donné, et d’autre part, un mécanisme de notification des changements qui se produisent sur
les services. Les changements concernent l’apparition des nouveaux services, la disparition
de services en cours d’utilisation ou bien le changement de terme du contrat. Ce dernier
point permet de réaliser des applications supportant l’arrêt de composants optionnels. Il
constitue aussi une charge de travail supplémentaire pour le développeur d’application
La suite de cette section détaille ces deux points.
Bundle
Le bundle 6 est l’unité de déploiement7 versionnée dans la plate-forme OSGi. Il est
conditionné sous la forme d’un fichier JAR contenant le code binaire des classes, des res-
sources comme des fichiers de configuration ou des images, et des bibliothèques de code
natives (c.a.d. dépendant du processeur et du système d’exploitation). Le fichier de ma-
nifeste du JAR est augmenté d’une certain nombre d’entrées propres à OSGi. Ces entrées
décrivent, entre autres, la classe servant de point d’entrée pour l’activation des services
(Bundle-Activator), des informations factuelles (auteur, vendeur, nom, nom symbolique,
licence, url vers le dépot de l’archive, url vers la documentation, . . .), les paquetages im-
portés (Import-Package) et les paquetages exportés (Export-Package).
L’atome de partage de code (c.a.d. des classes) dans OSGi est le paquetage et chaque
paquetage est versionné. OSGi suppose à priori une compatibilité 8 ascendante vis à vis
des versions antérieures. Les paquetages conditionnés dans le bundle qui n’apparaissent ni
dans l’entrée Import-Package, ni dans l’entrée Export-Package, n’ont qu’une portée locale
au bundle. Ce mécanisme assure un espace de nommage privé n’interférant pas avec ceux
des autres bundles.
La spécification 3 d’OSGi a introduit la possibilité de démarrer un bundle alors que des
paquetages qu’il utilisera ne sont pas exportés par un autre bundle au moment de l’activa-
tion. Ceci est fort utile pour les applications à plugin pour lesquels les plugins peuvent être
dynamiquement installés bien après l’activation du noyau central de l’application. C’est,
par exemple, le cas des codecs pour Java Media Framework, le canevas audio-vidéo de
Java. L’entrée DynamicImport-Package liste les patrons 9 de paquetages qui sont attendus
après l’activation.
Résolution des dépendances Le bundle passe à l’état RESOLVED quand les paquetages
qu’il importe (listé dans Import-Package), sont tous exportés par des bundles installés et
résolus sur la plate-forme. Une fois, dans cet état, le bundle est prêt pour exporter les
paquetages listés dans l’entrée Export-Package du manifeste. La résolution est retardée le
plus possible jusqu’à l’activation d’un bundle important les paquetages exportés par le
précédent. Quand un paquetage identique est exporté par plusieurs bundles, un seul de ces
6
baluchon en français
7
La spécification 4 introduit le concept de fragment bundle qui permet de conditionner un bundle sous la
forme de plusieurs JAR. Un bundle fragment est généralement utilisé pour conditionner des ressources lo-
calisées ou des bibliothèques natives dépendant du système d’exploitation et du processeur. La spécification
introduit également le concept de extension bundle qui permet de compléter le Boot-class-path sans passer
par le mécanisme d’import-export.
8
La spécification 4 introduit la notion d’intervalle de versions pour limiter la compatibilité ascendante.
9
par exemple, de la forme com.acme.plugin.* pour les plugins de la société ACME.
200 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
bundles devient l’exportateur effectif et les autres se comportent comme des importateurs.
La désignation de l’exportateur effectif se fait par rapport au plus haut numéro de version
de paquetage puis au plus petit identifiant d’installation de bundles 10 .
Activation L’activation d’un bundle devient alors possible. L’activation consiste pour
la plate-forme à instancier un seul objet de la classe donnée par l’entrée Bundle-Activator
du manifeste. La classe doit implémenter l’interface BundleActivator qui régit le cycle de
vie du bundle. La méthode start(BundleContext) est alors invoquée avec en paramètre
un objet BundleContext qui représente à la fois le contexte du bundle et le contexte de la
plate-forme. La méthode start(BundleContext) peut rechercher des services, en enregistrer
et si besoin démarrer des threads. En cas d’exception levée lors de l’exécution la méthode
start(BundleContext), le bundle retourne à l’état RESOLVED. Sinon il se trouve dans l’état
ACTIVE. L’état STARTING est l’état temporaire dans lequel est le bundle tant que l’exécution
de la méthode start(BundleContext) n’est pas terminée.
Mise à jour La mise à jour d’un bundle provoque l’arrêt, la réinstallation, la résolution
puis la réactivation en continu. Il arrive qu’une mise à jour se termine sur un échec. Le
bundle se retrouve dans l’état INSTALLED dans le cas où des nouveaux paquetages sont
requis et sont non disponibles dans la plate-forme, ou à l’état RESOLVED si la réactivation
du bundle avec la méthode start(BundleContext) s’est soldée par une exception.
Bibliothèques natives
Un bundle OSGi peut livrer également des bibliothèques de fonctions natives. Ces
bibliothèques sont décrites dans le manifeste grâce à l’entrée Bundle-NativeCode. Cette
entrée décrit le chemin dans le fichier JAR, le processeur cible et le système d’exploitation
requis. Une bibliothèque peut être liée à la JVM quand le bundle est activé et déliée
quand le bundle est arrêté. Le chargeur vérifie préalablement la compatibilité du processeur
et du système d’exploitation de la plate-forme avant de lier la bibliothèque. Cependant
seuls les objets créés par le bundle peuvent invoquer les fonctions de la bibliothèque liée.
L’invocation se fait via le mécanisme des JNI (Java Native Interface). L’invocation de
10
La spécification 4 introduit de nouvelles contraintes pour guider la résolution comme par exemple, les
groupements de paquetage qui ne peuvent être importés/exportés qu’ensemble.
7.3. LA PLATE-FORME DE DÉPLOIEMENT ET D’EXÉCUTION DE SERVICES JAVA201
Transition explicite
Transition automatique
install
update
refresh
INSTALLED
INSTALLED
STARTING
STARTING
resolve
refresh
update
start
uninstall
STARTING
STARTING
RESOLVED
RESOLVED
stop
uninstall
STOPPING
STOPPING
UNINSTALLED
UNINSTALLED
ces fonctions natives depuis un autre bundle doit obligatoirement passer par un objet de
service jouant le rôle de mandataire (cf. 2.3.1).
Déploiement en cascade
La plate-forme OSGi ne se charge que de l’installation d’un seul bundle à la fois.
Si un bundle dépend d’autres bundles pour l’importation de paquetages ou l’usage de
services, il est nécessaire d’installer préalablement les bundles fournissant les paquetages
ou les services requis. Ces bundles peuvent à leur tour requérir l’installation d’autres
bundles et ainsi de suite. L’installation de bundles en cascade reste à la charge d’outils
tiers de déploiement. Cependant, un très récent RFE à la spécification propose un service
de déploiement en cascade qui se base sur une description de bundles en terme de pa-
quetages importés et exportés et en terme de services requis et fournis. Ce RFE s’appuie
sur le BundleRepositoryService de l’OBR de la plateforme OSGi OSCAR. D’autres outils
proposent des mécanismes de points de reprise (checkpoint) pour défaire un déploiement
en chaı̂ne ayant échoué.
7.3.2 Service
OSGi suit le paradigme de la programmation orientée service dynamique
([Bieber and Carpenter 2002], voir aussi 2.1). Popularisé par le développement des ser-
vices Web 4.1, la programmation orientée service considère d’une part que tout service
respectant un contrat demandé est substituable à un autre, et d’autre part que le choix
d’un candidat parmi un liste de services respectant le contrat est décidé le plus tard pos-
sible à l’exécution. Le client récupère la liste des services en interrogeant un registre de
services. Un fournisseur enregistre son service auprès du registre de services en y associant
un contrat qualifié. La programmation orienté service dynamique considère de plus que les
services utiles à un client peuvent apparaı̂tre à tout moment et que les services en cours
202 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
d’utilisation peuvent disparaı̂tre à tout moment. Il convient que le client soit sensible (en
anglais aware) à ces changements.
Dans la plate-forme OSGi, les services sont le moyen pour les bundles de coopérer
entre eux11 . Le contrat de service dans OSGi est à la fois syntaxique et quantitatif selon
la classification de [Beugnard et al. 1999] (voir aussi 2.1.3). Il est constitué par une ou
plusieurs interfaces Java qualifiées par un ensemble de propriétés (typées) obligatoires ou
optionnelles. Les interfaces servent à la négociation syntaxique tandis que les propriétés
servent à la négociation de qualité de service. Dans l’exemple fictif de la figure 7.4, le
contrat est défini par l’interface PrintService complétée par des propriétés dont les noms
sont listés dans l’interface Constants. L’interface Job n’est pas une interface de service mais
néanmoins sert aux paramètres et aux retours des méthodes de l’interface de service. Le
contrat de service d’une imprimante peut être à la fois l’interface PrintService et l’interface
FaxService. D’autre part, seuls deux services d’impression remplissent le contrat de qualité
de service défini ici par la géo-localisation au premier étage.
package org.device.print;
%
! "!! #!! !!
% $ & %
%
! "!! #!! !!
! "!! #!!
java.lang.Runnable
crond.pattern=* 10 | * * * * * *
R
Application
Application 11
R
Fournisseur
Fournisseur A A
Cron java.lang.Runnable
Cron
crond.pattern=* 5 | * * * * * *
Deamon
Deamon
Application
Application 22
R
R
Fournisseur
Fournisseur B B
java.lang.Runnable
Application
Application 33
R
R
Fournisseur
Fournisseur C C
7.4.1 Activation
L’entrée Bundle-Activator du manifeste désigne la classe qui sert de point d’entrée pour
l’activation et pour l’arrêt du bundle. Cette classe publique doit implémenter l’interface
BundleActivator. Pour activer le bundle, la plate-forme crée un chargeur de classe (class
loader ) [Liang and Bracha 1998] propre au bundle, puis crée une seule instance de la classe
d’activation et enfin invoque la méthode start(BundleContext) sur cette instance. Pour
arrêter le bundle, la plate-forme invoque la méthode stop(BundleContext) sur l’instance
puis détruit le chargeur de classes du bundle 14 .
Le rôle de l’instance de la classe d’activation est généralement de créer les objets qui
utiliseront les services fournis par d’autres bundles et qui fourniront à leur tour des services.
La méthode start(BundleContext) se déroule donc en général de la manière suivante :
1. recherche des services nécessaires ou optionnels,
2. liaison avec les services sélectionnés,
3. création des objets implémentant les interfaces Java d’un contrat,
4. enregistrement de ces objets comme des services avec les propriétés de courtage,
5. positionnement d’écouteurs (listener ) sur les événements de services, des bundles et
de la plate-forme.
L’autre rôle de l’instance de la classe d’activation est celui de déactiver les services
fournis, de relacher les références vers les objets de service utilisés et de libérer les ressources
utilisées (threads, descripteur de fichiers, sockets, etc.) au cours des invocations de services.
La méthode stop(BundleContext) se déroule donc à contre-sens de la manière suivante :
1. retrait des écouteurs
2. désenregistrement des services enregistrés,
3. suppression des objets de services
4. relâche des services auxquels il est lié,
5. libération de ressources,
14
La destruction du chargeur provoque le déchargement et la recyclage (garbage collection) de toutes les
classes qu’il a chargées.
7.4. PROGRAMMATION DES SERVICES OSGI 205
Le paramètre passé à l’invocation de ces 2 méthodes est le contexte du bundle. Cet objet
qui implémente l’interface BundleContext, permet au bundle d’obtenir des informations
sur lui-même et d’interagir avec le registre de services interne à la plate-forme. Il permet
également de demander à la plate-forme l’installation, l’activation, l’arrêt, la mise à jour et
la désinstallation d’un autre bundle. Pour des raisons de sécurité, ce privilège est néanmoins
réservé aux bundles d’administration qui ont la permission AdminPermission.
package com.lexmark.printer.laser.impl;
import org.osgi.framework.*;
package org.eclispe.texteditor.impl;
import org.osgi.framework.*;
}
}
this.interfaceName=interfaceName;
this.ref2servantMap=new HashMap();
}
Set printers;
MyServiceListener listener;
face technique (ConfigMBean15). Les propriétés déclarées dans les éléments property sont
utilisées pour qualifier le service fourni. Le fournisseur de service peut être une usine de
services (non présentée dans l’exemple) qui instancie des services personnalisés à chaque
composant usager au lieu du singleton commun à tous.
Le composant usager du service (à gauche de la figure) est décrit par le descripteur
de gauche. Les éléments references indiquent que le composant référence plusieurs ser-
vice obligatoires d’impression (PrintService) et un service optionnel de journalisation
(LogService). L’attribut cardinality indique si la liaison est simple ou multiple et si elle
est obligatoire ou optionnelle. Le caractère multiple de la liaison signifie que le composant
peut utiliser un ensemble de services PrintService. Le caractère obligatoire signifie que le
composant est instancié dès qu’un service PrintService est disponible et il est détruit dès
que le dernier PrintService est désenregistré. Le caractère optionnel indique que le com-
posant peut fonctionner sans nécessairement référencer le service décrit. L’attribut target
restreint le courtage à un sous-ensemble de services PrintService au moyen du expression
LDAP. Les méthodes de rappel (callback ) pour le contrôle de la liaison sont décrites par les
attributs bind /unbind. Elles sont invoquées lors de l’instanciation ou de la destruction du
composant et lors de l’apparition ou de la disparition des services intéressants le compo-
sant. Ces méthodes sont cependant optionnelles comme dans le cas de la référence vers un
service de journalisation. Le composant peut alors récupérer la ou les référence(s) vers le
service au travers de son contexte. Les méthodes optionnelles activate /desactivate sont
les méthodes de rappel pour le contrôle du cycle de vie. La méthode activate est invoqué
au moment de l’instanciation juste après la création des liaisons et la méthode deactivate
est invoqué au moment de la destruction juste avant la création des liaisons. L’attribut
policy indique qu’un service qui disparaı̂t est substituable dynamiquement par un autre
service équivalent. La politique statique interdit toute substitution après l’activation du
composant.
Les descripteurs de composants du bundle sont conditionnés et livrés dans le bundle.
Leurs chemins sont listés dans l’entrée Service-Component du manifeste.
Travaux apparentés
# $ # $
# #
% & '$ ( & '$
# ()* + #
( , 2 , '$
-. - /0- 00 #
1 2 , '$
#
( , 2 !"
2 2 ( , , '$
'$ # $
# 34 5 # ( , '$
3 , # & 6 7 '$
" 1 #' $
#' $
'$
#' $
Word
Word HPPrinter
HPPrinter
Processor
Processor
34 5 cmp.xml
cmp.xml
% [org.device.print.PrintService, (
& com.hp.printer.ConfigMBean] &
()* +
!"
dans des bundles différents. D’autres travaux s’intéressent à l’ingénierie des conteneurs
dynamiques pour les services OSGi par l’utilisation de la programmation orientée aspect
ou bien encore par l’injection de code sur des POJOs (Plain Old Java Objects).
Console
port Web
tcp 443
Server
! "#
$
% & &# # ' " () *'
" + & ' , -- . + ' "
" & / +$ " " 0& "
" & / +$ " & 0& "
$
! "#
Passerelle enfouie
GPS
GPS Inertial
Inertial Position
Position POI
POI WireAdmin
WireAdmin
Receiver
Receiver Sensor
Sensor Correlator
Correlator Locator
Locator Binder
Binder
PP PP C
C C
C PP C
C
WireAdmin
P C P C P C
Wire
Wire Admin
Admin Service
Service Impl
Impl
Center. . .), la communication (téléphones, routeur ADSL. . .), la domotique (alarme anti-
intrusion, régulation du chauffage, volets roulants, etc.) ou bien encore l’électroménager
(lave-linge, réfrigérateur. . .). UPnP Forum a publié une première version des protocoles
réseaux requis (UPnP DA) et un certain nombre de définitions standard de périphériques
(devices) et de leurs services associés.
Universal Plug and Play (UPnP) Device Architecture (DA) spécifie les protocoles pour
des réseaux spontanés de périphériques (devices) . Les protocoles UPnP traitent :
– la détection et le retrait dynamique des périphériques,
– leur description et celles des services qu’ils fournissent,
– l’utilisation par les points de contrôle (PDA, télévision, télécommande RF. . .) des
services fournis
– la notification des changements de valeurs des variables d’état associées aux services.
UPnP DA a les mêmes objectifs que JINI [Waldo 1999] proposé par SUN. Cependant,
UPnP DA n’est pas attaché à un langage particulier (comme JINI avec Java). Les pro-
tocoles de UPnP DA s’appuient sur XML, SOAP 1.0 et HTTP au dessus de TCP, UDP
et UDP Multicast. La prochaine version d’UPnP DA devrait s’orienter vers les protocoles
vers les standards actuels des services Web. La proposition DPWS (Device Profile for
Web Services) candidate à UPnP v2 s’appuie sur SOAP 1.2, WSDL 1.1, XML Schema,
WS-Addressing, WS-MetadataExchange, WS-Policy, WS-Security, WS-Discovery et WS-
Eventing.
Le chapitre UPnP Device Driver de la spécification OSGi traite la manière de
développer des périphériques UPnP et des points de contrôle UPnP au dessus une plate-
forme OSGi. Ce chapitre décrit d’une part l’API org.osgi.service.upnp dont l’interface
principale UPnPDevice représente un périphérique, et d’autre part un élément de la pas-
serelle, l’UPnP Base Driver, qui assure le pont entre les points de contrôle UPnP et les
périphériques UPnP présents sur le réseau IP adhoc, et les bundles hébergés par la plate-
forme20 .
L’interface de service UPnPDevice décrit le périphérique. Cette interface contient prin-
cipalement les références vers des objets UPnPService qui décrivent les services associés
au périphérique. Cette interface UPnPService n’est jamais enregistrée comme un service
OSGi. A son tour, l’objet UPnPService décrit la liste de variables d’état au moyen d’ob-
jets UPnPStateVariable et la liste des actions au moyen d’objets UPnPAction. L’invoca-
tion d’une action sur le périphérique UPnP se traduit par l’invocation de la méthode
Dictionary invoke(Dictionary args) sur l’objet UPnPAction correspondant. L’API com-
porte également l’interface de service UPnPEventListener. Un point de contrôle qui souhaite
être informé des changements d’état des variables enregistre un service UPnPEventListener.
Les bundles (dont l’UPnP Base Driver ) qui fournissent des services UPnPDevice, doivent
alors se lier à ce service UpnPEventListener afin d’effectuer les notifications. La demande
de notification peut concerner soit un périphérique particulier, soit tous les périphériques
d’un même type ou bien soit tous les services d’un même type.
20
L’Alliance avait spécifié un service pour JINI avec des objectifs similaires dans la version 3 de la
spécification OSGi. Cependant ce service a été retiré de la version 4 faute d’un soutien des équipementiers.
220 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
Cas d’utilisation
Cette section présente quatre mises en œuvre possibles d’OSGi UPnP Device Service.
)!) ! "#
)!) ! *" !
! "#
$ % & '! ' (
Périphérique UPnP Cette mise en œuvre permet d’utiliser Java et OSGi pour
développer des périphériques UPnP. La figure présente un décodeur de TV numérique
embarquant une passerelle OSGi et une machine virtuelle Java J2ME/CDC. Le bundle
périphérique contrôle le matériel de la STB comme la carte tuner et la carte son. Ce
bundle enregistre tout d’abord un service UPnPDevice. A ce moment, le bundle UPnP Base
Driver se lie à ce service et annonce l’apparition d’un nouveau périphérique dans le réseau
ad hoc. Les points de contrôle externes à la passerelle peuvent alors invoquer des actions
qui seront convertit en appel de méthode invoke(Dictionary) sur l’objet UPnPAction par
le bundle UPnP Base Driver. Pour chaque point de contrôle externe à la passerelle qui
souhaite être notifié des changements de valeur dans le périphérique, le bundle UPnP Base
Driver enregistre un service UPnPEventListener.
Passerelle UPnP avec les micro-mondes La dernière mise en œuvre propose d’uti-
liser la passerelle OSGi pour créer des ponts entre un réseau UPnP (basé sur IP) et des
réseaux non-IP (comme X10, IEEE1394, OneWire TM , ModBusTM . . .). Ce dernier type
de réseaux est appelé « micro-monde ». Le niveau fonctionnel des équipements raccordés
est généralement très faible car la partie communication de l’équipement est fortement
contrainte par son coût. La mise en œuvre est très semblable à la précédente car le bundle
qui réalise le pontage avec le micro-monde enregistre un service UPnPDevice pour chaque
équipement découvert dans le micro-monde 21 . De manière similaire à la seconde mise en
œuvre, le code du service UPnPDevice peut être déployé au moment de la découverte. Le
21
Certains réseaux comme X10 n’offrent pas de mécanismes de découverte ou de sondage. Les adresses des
équipements du micro-monde doivent donc être entrées manuellement par l’installateur des équipements
via l’interface de configuration de la passerelle.
222 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
$ $ &
$ $ %&
! "# ! !
bundle UPnP Base Driver assure quand à lui le pontage entre les services UPnPDevice
enregistrés et le réseau UPnP.
$ $ &
$ $ %&
! "# ! !
Pour terminer cette section, UPnP DA peut être également utilisé comme support pour
découvrir et contrôler les passerelles OSGi présentes sur un réseau ad hoc. La passerelle
est décrite alors comme un périphérique. Pour cela, elle embarque seulement un bundle
d’administration fournissant un service UPnPDevice.
Le marché des passerelles OSGi compte déjà plusieurs fournisseurs commerciaux (IBM
SMF, ProSyst mBedded, Siemens VDO TLA. . .) et des implémentations open-source ma-
tures (Oscar, Felix, Knoplerfish, Equinox). La différence entre ces plates-formes tiennent
surtout aux outils (développement, administration de parc, déploiement), aux bundles dis-
ponibles implémentant des services standard ou des services propriétaires, et aux supports
par les machines virtuelles du marché pour les J2ME/CLDC, J2ME/CDC et J2SE.
De façon général, tout développeur d’application gagne à conditionner ses applications
sur la forme de bundle OSGi afin d’éviter l’enfer du CLASSPATH 22 lors du déploiement
des applications. La très récente annonce du JSR 277 Java Module System confirme cette
tendance. Il constate que les problèmes de déploiement des applications Java (J2SE et Java
EE) sont liées au manque de méta-informations décrivant les dépendances des unités de
déploiement. Le nouveau modèle de conditionnement que vise à définir ce JSR risque fort de
ressembler à la spécification 4 de OSGi sortie en Octobre 2005. La disponibilité de ce JSR
dans l’édition standard de la plate-forme est prévue pour sa version 7 (appelée Dolphin). Ce
JSR a été très récemment complété par le JSR 291 Dynamic Component Support for Java
SE qui propose le modèle d’exécution d’OSGi R4 comme modèle d’exécution dynamique
pour les applications Java. Si ces deux JSR sont adoptés et intégrés à la plate-forme Java,
une bonne partie de la spécification du noyau de la plate-forme OSGi R4 sera absorbée
par l’environnement d’exécution de Java.
Et côté serveur ? Jusqu’à présent, OSGi est synonyme de serveurs embarqués ou enfouis.
Cependant, avec son adoption par le projet Eclipse [Gruber et al. 2005] et récemment
par plusieurs projets de l’Apache Software Foundation [Rodriguez 2005], OSGi pourrait
bien être demain la plate-forme de référence pour construire des serveurs d’application
d’entreprise dynamiques et flexibles [Désertot et al. 2006].
Et la plate-forme Microsoft .NET alors ? Le grand concurrent de Java pourrait-il ser-
vir à construire une plate-forme dynamique de services semblable à OSGi ? Comme Java,
.NET repose sur une machine virtuelle, la CLR (voir chapitre 6, section 6.2.1), qui exécute
un code portable, le MSIL (voir chapitre 6, section 6.2.2), chargé dynamiquement. Cepen-
dant, le grain de déchargement des classes est le domaine d’application et les références
directes entre domaines d’applications sont interdites. La réponse semble être non, pour
l’instant, avec les versions actuelles de la CLR [Escoffier et al. 2006]. Mais on peut parier
que Microsoft corrigera le tir si le besoin de dynamicité se fait sentir dans les domaines de
marché encore vierges que vise déjà OSGi.
22
par analogie avec le DLL Hell de Microsoft DCOM
224 CHAPITRE 7. LA PLATE-FORME DYNAMIQUE DE SERVICES OSGI
Intergiciel et Construction d’Applications Réparties
c
2006 (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Chapitre 8
Conclusion
1
Queensland University of Technology, Brisbane (Australie)
2
Université Joseph Fourier, Grenoble
3
Université de Nice - Sophia Antipolis
4
Université Pierre et Marie Curie et INRIA
5
Scalagent Distributed Technologies
6
France Telecom R&D
228 ANNEXES
Intergiciel et Construction d’Applications Réparties
c
2006 M. Dumas, M.-C. Fauvet, A. Aı̈t-Bachir (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Annexe A
La version HTML de ce document, ainsi que les ressources qu’il utilise sont accessibles
via : http ://www-clips.imag.fr/mrim/User/marie-christine.fauvet/icar06/
Avant de commencer ce TP copier l’archive webserv prat 1.tar, puis extraire les fichiers
qu’elle contient. Cela aura pour effet de créer l’arborescence et les fichiers nécessaires au
TP (voir section A.8.1).
Objectifs :
Nous proposons de mettre en pratique le cours dédié aux services Web par le biais
d’une activité qui consiste à programmer et implanter des services web simples.
A.1 Préambule
Attention :
Les utilisateurs d’autres systèmes d’exploitation que unix/linux doivent adapter en
conséquence les notations utilisées ci-après.
Outils à installer :
Pour ce TP, les outils qui doivent avoir été installés sur la machine sont listés ci-après.
Les versions utilisées pour ces outils sont valides au 31 juillet 2006. Elles sont bien sûr
amenées à évoluer avec le temps.
1. JDK 1.5
2. Tomcat 5.5 (à rendre accessible via le port 8080)
3. Beehive
4. Ant 1.6.5
5. Axis 1.4
Les variables d’environnement listées ci-après doivent avoir été correctement posi-
tionnées (consulter les documentations d’installation, et positionner les variables en fonc-
tion de l’installation locale) :
– AXIS HOME
– AXIS LIB
230 ANNEXE A. LES SERVICES WEB EN PRATIQUE
– AXISCLASSPATH
– ANT HOME
– BEEHIVE HOME
– CATALINA HOME
– CATALINA LIB
– CATALINACLASS
– JAVA HOME
– CLASSPATH
NB : Les services Web seront déployés dans un serveur Apache Tomcat sur la machine
locale accessible via le port 8080.
--------------------------------------------
4. Aller dans le répertoire webserv prat 1/ws time et dans le fichier index.html vérifier
les lignes signalées ci-après :
-----------------index.html---------------
<a href="web/TimeNow.jws?wsdl">WSDL</a>
<a href="web/TimeNow.jws?method=getTimeNow">appel opération</a>
------------------------------------------
1. Dans le répertoire webserv prat 1/ws client créer un fichier java nommé
ClientTimeNow.java (voir son contenu, sectionA.8.3). Respecter le nom.
2. Compiler le fichier : $ javac ClientTimeNow.java
3. Avant d’exécuter la classe compilée il faut avoir bien défini dans la variable d’environ-
nement CLASSPATH le chemin d’accès au répertoire où le fichier ClientTimeNow.class
a été placé.
4. Dans le répertoire de ClientTimeNow.class, exécuter l’instruction suivante :
$ java ClientTimeNow. A la date près, le résultat doit ressembler à ce qui suit :
$ java ClientTimeNow
Bonjour d’Autrans ! ! Ici il est : Sun Mar 05 17 :38 :49 CET 2006
Le message d’erreur :
- Unable to find required classes (javax.activation.DataHandler and
javax.mail.internet.MimeMultipart). Attachment support is disabled. est
sans conséquence.
232 ANNEXE A. LES SERVICES WEB EN PRATIQUE
4. Aller dans le répertoire webserv prat 1/ws hello et dans le fichier index.html vérifier
les lignes signalées ci-après :
-----------------index.html---------------
<a href="web/Hello.jws?wsdl">WSDL</a>
<a href="web/Hello.jws?method=sayHelloWorldInParam&name=you">
appel opération</a>
------------------------------------------
demander
envoyer heure
envoyer
salut + heure
Figure A.1 – Interactions entre les services sur la base d’opérations bidirectionnelles.
Voir le corrigé pour le service et pour le client. Penser à modifier en conséquence les
paramètres concernés dans le fichier build.properties.
PerfectGlass Entrepôt
envoyer disponibilité
Figure A.2 – Interactions entre les services sur la base d’opérations unidirectionnelles.
envoyer disponibilité
envoyer devis / rejet devis
A.8 Ressources
A.8.1 Arborescence de webserv prat 1/ (telle que fournie dans l’énoncé)
webserv_prat_1/
webserv_prat_1/EntrepotLocal/
236 ANNEXE A. LES SERVICES WEB EN PRATIQUE
webserv_prat_1/ws_hello/
webserv_prat_1/ws_hello/WEB-INF/
webserv_prat_1/ws_hello/WEB-INF/server-config.wsdd
webserv_prat_1/ws_hello/WEB-INF/src/
webserv_prat_1/ws_hello/WEB-INF/src/build.xml
webserv_prat_1/ws_hello/WEB-INF/src/build.properties
webserv_prat_1/ws_hello/WEB-INF/web.xml
webserv_prat_1/ws_hello/WEB-INF/src-ws/
webserv_prat_1/ws_hello/WEB-INF/src-ws/web/
webserv_prat_1/ws_hello/index.html
webserv_prat_1/ws_client/
webserv_prat_1/PerfectGlassLocal/
webserv_prat_1/Entrepot/
webserv_prat_1/Entrepot/WEB-INF/
webserv_prat_1/Entrepot/WEB-INF/server-config.wsdd
webserv_prat_1/Entrepot/WEB-INF/src/
webserv_prat_1/Entrepot/WEB-INF/src/build.xml
webserv_prat_1/Entrepot/WEB-INF/src/build.properties
webserv_prat_1/Entrepot/WEB-INF/web.xml
webserv_prat_1/Entrepot/WEB-INF/src-ws/
webserv_prat_1/Entrepot/WEB-INF/src-ws/web/
webserv_prat_1/Entrepot/index.html
webserv_prat_1/ws_time/
webserv_prat_1/ws_time/happyaxis.jsp
webserv_prat_1/ws_time/WEB-INF/
webserv_prat_1/ws_time/WEB-INF/server-config.wsdd
webserv_prat_1/ws_time/WEB-INF/src/
webserv_prat_1/ws_time/WEB-INF/src/build.xml
webserv_prat_1/ws_time/WEB-INF/src/build.properties
webserv_prat_1/ws_time/WEB-INF/web.xml
webserv_prat_1/ws_time/WEB-INF/src-ws/
webserv_prat_1/ws_time/WEB-INF/src-ws/web/
webserv_prat_1/ws_time/index.html
webserv_prat_1/PerfectGlass/
@WebService
public class TimeNow {
@WebMethod
public String getTimeNow() {
Date d=new Date();
String t=d.toString();
return t;
}
}
A.8. RESSOURCES 237
import javax.xml.namespace.QName;
// l’URI a contacter
String endpointURL = "http://localhost:8080/TimeNowWS/web/TimeNow.jws";
// Le service à executer
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpointURL) );
// l’operation du service
call.setOperationName( new QName("TimeNow", "getTimeNow") );
// L’appel
String ret = (String) call.invoke( new Object[] { } );
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.WebParam;
@WebService
public class Hello {
@WebMethod
public String sayHelloWorldInParam(@WebParam String name ) {
try {
} catch (Exception e) {
return "Erreur " + e.toString();
} } }
238 ANNEXE A. LES SERVICES WEB EN PRATIQUE
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
args = options.getRemainingArgs();
if ((args == null) || (args.length < 1)) {
textToSend = "l’inconnu";
} else {
textToSend = args[0];
}
// L’appel
String ret = (String) call.invoke( new Object[] { textToSend } );
System.out.println(ret);
} catch (Exception e) {
System.err.println(e.toString()+" ici");
}
}
}
A.8. RESSOURCES 239
System.out.println(ret);
} catch (Exception e) {
System.err.println(e.toString()+" ici");
}
}
}
public Produit() {
}
try {
String res ;
Produit v = new Produit () ;
Produit a = new Produit ();
v.setRef("verres") ;
v.setQuantite(200);
a.setRef("assiettes") ;
a.setQuantite(4500);
System.out.print (question);
return (lire_int());
}
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
String adresse ;
nom = Saisie.lire_String ("Donner un nom : ");
age = Saisie.lire_int ("Donner un age : ") ;
adresse = Saisie.lire_String ("Donner une adresse : ") ;
call.registerTypeMapping(Personne.class, personneXML,
new org.apache.axis.encoding.ser.BeanSerializerFactory(
Personne.class, personneXML),
new org.apache.axis.encoding.ser.BeanDeserializerFactory(
Personne.class, personneXML));
System.out.println(ret);
} catch (Exception e) {
System.err.println(e.toString()+" ici");
}
}
}
public Personne () {
A.8. RESSOURCES 243
}
244 ANNEXE A. LES SERVICES WEB EN PRATIQUE
Intergiciel et Construction d’Applications Réparties
c
2006 M. Riveill, D. Emsellem (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Annexe B
.NET en pratique
3
Web Matrix : http ://www.asp.net/webmatrix/
4
Mono : http ://www.go-mono.org
5
SharpDevelop : http ://sourceforge.net/projects/sharpdevelop/
6
Visual Studio Express : http ://www.microsoft.com/france/msdn/vstudio/express/default.mspx
7
MSDN AA : http ://www.microsoft.com/france/msdn/abonnements/academic/default.mspx
B.3. LES OBJETS MÉTIERS 247
CarteDeVisite (CarteDeVisite.cs)
using System;
using System.Xml;
using System.Xml.Serialization;
namespace MonPremierTP {
[Serializable][XmlType("carte-de-visite")]
public class CarteDeVisite {
[XmlElement("nom")] public string Nom;
[XmlElement("prenom")] public string Prenom;
[XmlElement("email")] public string Email;
[NonSerialized()]
public string Telephone;
}
}
Nous verrons ultérieurement comment les différentes fiches seront initialisées par des
données issues de la base de données. La description et l’utilisation des différents attributs
sont présentés dans la section B.6.5.
Pour génerer la bibliothèque CarteDeVisite.dll, la commande est la suivante :
csc /target:library CarteDeVisite.cs
AnnuaireDAL (AnnuaireDAL.cs)
// volontairement le namespace n’est pas répéter...
// à vous de l’indiquer et d’utiliser les bonnes directives ’using’
[assembly:AssemblyVersion("0.1.0.0")]
public class AnnuaireDAL {
public AnnuaireDAL() {} // constructeur
public CarteDeVisite ExtraireCarte(string nomRecherche) {
// opération de recherche d’une carte dans la BD
return null;
}
public void AjouterCarte(CarteDeVisite carte){
// opération d’insertion d’une nouvelle carte
}
}
248 ANNEXE B. .NET EN PRATIQUE
IAnnuaireService (IAnnuaireService.cs)
public interface IAnnuaireService {
CarteDeVisite Rechercher(string nom);
}
public interface IAnnuaireAdminService {
CarteDeVisite Rechercher(string nom);
void Ajouter(CarteDeVisite carte);
}
Une fois ces interfaces définies, il ne nous reste qu’à les implanter en utilisant la couche
d’accès aux données décrite précédemment.
AnnuaireService (AnnuaireService.cs)
public class AnnuaireService : IAnnuaireService {
// constructeur de la classe
public AnnuaireService() {}
// implémentation de la méthode métier
public CarteDeVisite Rechercher(string nom) {
AnnuaireDAL dal=new AnnuaireDAL();
return dal.ExtraireCarte(nom);
}
}
return dal.ExtraireCarte(nom);
}
public void Ajouter(CarteDeVisite carte) {
AnnuaireDAL dal=new AnnuaireDAL();
// on vérifie que la carte n’existe pas déjà
CarteDeVisite test=dal.ExtraireCarte(carte.Nom);
if (test==null)
dal.AjouterCarte(carte);
else
throw new Exception("La carte existe déjà");
}
}
– La balise exception permet d’informer sur le(s) type(s) d’exception(s) que la fonc-
tion peut lever. La propriété cref du tag permet de spécifier le type d’exception
documenté.
– Il existe bien d’autres balises : <c>, <code>, <example>, <include>, <list>, <para>,
<permission>, <remarks>, ¡see>, <seealso>. A vous de découvrir leur utilisation en
lisant la documentation.
Voici un exemple de programme commenté avec ces balises :
// xml_AnnuaireDAL.cs
// à compiler par : csc /doc:xml_AnnuaireDAL.xml
[assembly:AssemblyVersion("0.2.0.0")]
/// Mon texte pour la classe AnnuaireDAL
/// <summary>
/// Description de la classe AnnuaireDAL .
/// </summary>
public class AnnuaireDAL {
/// <remarks>
/// Des commentaires plus longs peuvent e^tre associés à un type ou un membre
/// gr^
ace à la balise remarks</remarks>
/// <example> Cette classe permet d’isoler la partie métier de la persistance
/// des données, elle comporte essentiellement deux méthodes.
/// <code>
/// public CarteDeVisite ExtraireCarte(string nomRecherche)
/// public void AjouterCarte(CarteDeVisite carte)
/// </code>
/// </example>
/// <summary>
/// Constructeur de la classe AnnuaireDAL.
/// </summary>
public AnnuaireDAL() {} // constructeur
/// <summary>
/// Une propriété introduite juste pour montrer la balise "value".
/// </summary>
/// <value>donne toujours 0492965148</value>
public int UnePropriete {
get {
return 0492965148;
}
}
}
ildasm.exe annuaire.dll
using System;
using System.IO;
using System.Reflection;
Compilez cette classe (csc <nom de fichier>.cs) et exécutez le code, puis faites une
lecture attentive du source. Avez-vous tout compris ?
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
using System.Xml.Serialization;
class Serialisation {
static void Main(string[] args) {
CarteDeVisite uneCarte = new CarteDeVisite();
uneCarte.Nom = "RIVEILL";
uneCarte.Prenom = "Michel";
uneCarte.Email = "riveill@unice.fr";
B.6. PREMIÈRES MANIPULATIONS EN C# 253
Console.WriteLine ("\t\torigine\t\tbinaire\t\tXml");
string Nom;
string Prenom;
string Email;
string Telephone;
Par exemple la table obtenue peut avoir le format décrit dans la figure B.2.
3. une fenêtre d’édition comportant 3 modes : ’dessin’, ’html’ et ’code’ est à votre
disposition (’all’ est la fusion du mode ’html’ et ’code’). En mode ’dessin’ mettez sur
la page une TextBox (saisie de chaine de caractère), un DataGrid et un Button.
4. en mode ’html’ vous pouvez renommer les identificateurs des différents éléments.
Par exemple pour moi, le TextBox a "nom" comme identificateur, le DataGrid a
"listeCartes" comme identificateur et le Button a "Button" comme id et "Chercher"
comme Text.
5. écrire la méthode ExtraireCarteSQL à l’aide de l’utilitaire fournit par Web Matrix
(procédure décrite ci-après).
Pour écrire la méthode ExtraireCarteSQL à l’aide de l’utilitaire présent dans Web
matrix il faut être en mode ’code’, puis faire glisser la zone SELECT Data Method de la
colonne ToolBox à l’endroit ou vous voulez insérer la méthode. Une boite de dialogue
s’ouvre. Elle permet de sélectionner la base de données, puis de construire la requête
SELECT souhaitée.
Pour cela, sélectionnez les bonnes colonnes, remplissez la clause WHERE pour obtenir la
requête décrite dans la figure B.4.
Après avoir cliqué sur le bouton suivant, testez la requête écrite. Si elle convient, donnez
le nom de la méthode et demandez le résultat sous la forme d’un DataSet.
Pour terminer l’exemple, il reste à programmer la méthode appelée lorsque le bouton
sera sélectionné. Pour cela, en mode ’dessin’, cliquez 2 fois sur le Button. La fenêtre d’édition
bascule en mode ’code’ et l’on peut saisir le code associé à l’évènement ’clic sur le Button’.
Le code de cette méthode est le suivant :
Petites explications : la première ligne met à jour la liste des données du DataSet en
appelant la méthode ExtraireCarteSQL construite dans l’étape 6 précédente, le paramètre
est le texte de la TextBox contenue dans la page. Les deux lignes mettent à jour le DataSet
et le rendent visible.
Attention : cette méthode, certes très efficace en terme de programmation ne respecte
pas du tout l’architecture initiale, elle a juste permis d’utiliser la base de données créée et
de nous familiariser avec Web Matrix pour la création de page web dynamique. La suite
du TP reprend la construction pas à pas des différentes couches de l’application cartes de
visite.
carte.Nom = requete.Tables["Table"].Rows[0]["nom"];
carte.Prenom = requete.Tables["Table"].Rows[0]["prenom"];
carte.Email = requete.Tables["Table"].Rows[0]["email"];
carte.Telephone = requete.Tables["Table"].Rows[0]["telephone"];
return carte;
}
AnnuaireServiceRemoting (AnnuaireServiceRemoting.cs)
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
}
public void Ajouter(CarteDeVisite carte) {
annuaire.Ajouter(carte);
}
}
Server (Server.cs)
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof (AnnuaireServiceRemoting),
"TcpService",
WellKnownObjectMode.Singleton);
using System;
using System.Web.Services;
using System.Xml.Serialization;
public AnnuaireWebService() {
service=new AnnuaireService();
}
[WebMethod]
public CarteDeVisite Rechercher(string nom) {
return service.Rechercher(nom);
}
}
Client local
class Client {
static void Main (string[] args) {
// creation de l’objet
IAnnuaireService annuaire = new AnnuaireService ();
// creation du proxy
IAnnuaireService annuaire = (IAnnuaireService) Activator.GetObject
(typeof (IAnnuaireService),
"tcp://127.0.0.1:7000/TcpService");
Solution 1 Créer un client léger qui appelle le service précédemment créé, via le proxy
généré précédemment. Voici par exemple, la partie code d’une telle page.
Pour pouvoir mettre au point le programme, l’entête de la page (onglet ’all’) doit
contenir la directive debug ainsi que le chargement du proxy généré :
La partie ’html’ de la page contient un TextBox pour saisir le nom et quatre Label :
deux pour afficher le texte ”prénom” et ”email” et deux autres pour afficher les valeurs
associées.
...
<asp:TextBox id="nom" runat="server"></asp:TextBox>
...
<asp:Label id="LabelPrenom" runat="server" text="Prénom : " \
visible="False"></asp:Label>
<asp:Label id="LabelPrenomValeur" runat="server" text="Label" \
visible="False"></asp:Label>
...
<asp:Label id="LabelEmail" runat="server" text="Email : " \
visible="False"></asp:Label>
<asp:Label id="LabelEmailValeur" runat="server" text="Label" \
visible="False"></asp:Label>
Solution 2 Créer un client léger qui utilise directement la dll précédemment produite.
Ceci peut être fait très facilement en déplaçant la dll dans un répertoire bin, puis en
utilisant la directive ’import’ pour charger le ’namespace’ du TP :
Attention : si votre dll utilise des modules, il faut aussi déplacer dans ce répertoire
les différents modules.
Le code est le suivant (très voisin de l’étape précédente... en l’absence de la directive
import, il est aussi possible d’utiliser les noms de classe complet) :
LabelPrenomValeur.Text = carte.Prenom;
LabelEmailValeur.Text = carte.Email;
LabelPrenom.Visible = true;
LabelPrenomValeur.Visible = true;
LabelEmail.Visible = true;
LabelEmailValeur.Visible = true;
}
– Il est possible de construire d’autres classes d’accès aux données afin d’utiliser une
sauvegarde dans un fichier XML et non plus une base de données.
– Créer d’autres tables afin d’avoir par exemple pour chaque personne son compte
bancaire afin d’effectuer des transfert entre compte de manière transactionnelle.
– Mettre en place un mécanisme d’authentification afin que seule les personnes auto-
risées puissent accéder au données sensible.
264 ANNEXE B. .NET EN PRATIQUE
Intergiciel et Construction d’Applications Réparties
c
2006 D. Donsez (version du 19 janvier 2007 - 10:31)
Licence Creative Commons (http://creativecommons.org/licenses/by-nc-nd/ 2.0/fr/deed.fr)
Annexe C
OSGI en pratique
Annexe D
D.1 Préliminaires
D.1.1 Logiciels nécessaires
Les logiciels suivants sont nécessaires pour mettre en œuvre cet atelier :
– JDK >= 1.4
– Ant 1.6.x
Par ailleurs, il est nécessaire de se munir de l’archive aokell.zip et d’extraire les fichiers
qu’elle contient. On y trouvera une version précompilée de AOKell 2.0 et la librairie de jour-
nalisation Monolog2 . L’archive contient deux sous-répertoires principaux : oo et comp. Le
premier contient les fichiers pour la section D.2 dans laquelle nous illustrons l’écriture d’un
1
http://dream.objectweb.org
2
http://monolog.objectweb.org
268ANNEXE D. AOKELL : UNE RÉALISATION RÉFLEXIVE DU MODÈLE FRACTAL
contrôleur pour des membranes à base d’objets, tandis que le sous-répertoire comp contient
les fichiers pour la section D.3 dans laquelle nous illustrons l’écriture d’un contrôleur pour
des membranes à base de composants.
[java] ===================================================
[java] ==== Hello World with the API ====
[java] ===================================================
[java] CLIENT created
[java] SERVEUR created
[java] Interfaces du composant Client
[java] 0 name-controller
[java] 1 super-controller
[java] 2 component
[java] 3 r
[java] 4 binding-controller
[java] 5 lifecycle-controller
[java] 6 s
[java] Server: print method called
[java] at cs.impl.ServerImpl.print(ServerImpl.java:31)
[java] at aokell.generated.cs.impl.ServiceImplementedInterface.print
[java] at cs.impl.ClientImpl.run(ClientImpl.java:33)
[java] at aokell.generated.java.lang.RunnableImplementedInterface.run
[java] at aokell.generated.java.lang.RunnableBoundableInterface.run
[java] at cs.Main.main(Main.java:53)
[java] at hw.Main.main(Main.java:33)
[java] Server: begin printing...
[java] ->hello world
[java] Server: print done.
Après les trois premières lignes contenant le message de bienvenue, nous trouvons les
deux lignes indiquant la création des composants Client et Serveur, la liste des interfaces de
contrôle du composant Client et l’exécution proprement dite de l’application avec l’appel
de la méthode print. Notons en particulier, les sept interfaces du composant Client : cinq
de contrôle et deux métier. Cette liste sera étendue par la suite avec une interface de
contrôle de journalisation.
Dans le premier cas (couplage faible), présenté section D.2.1, il n’y a pas de modification
du code de la partie métier par le contrôleur qui se contente de fournir de nouvelles
fonctionnalités. Il est “simplement” enregistré auprès de la membrane et on y accède via
les méthodes d’introspection getFcInterface et getFcInterfaces de l’interface Component.
Dans le second cas (couplage fort), présenté section D.2.2, en plus d’être enregistré
auprès de la membrane, le contrôleur peut modifier le comportement de la partie métier
en y ajoutant du code ou en modifiant le code existant. Pour cela, il est nécessaire d’écrire
un aspect.
package org.objectweb.fractal.aokell.dream.lib.control.logger;
import org.objectweb.dream.control.logger.LoggerController;
import org.objectweb.dream.control.logger.LoggerControllerRegister;
import org.objectweb.fractal.aokell.lib.type.InterfaceTypeImpl;
import org.objectweb.fractal.api.type.InterfaceType;
/**
* @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
*/
public interface LoggerControllerItf
extends LoggerController, LoggerControllerRegister {
Notons les deux constantes NAME et TYPE que nous ajoutons dans cette interface. Il s’agit
d’une pratique qui n’a aucun caractère obligatoire mais qui permet de définir le nom et le
type (au sens Fractal) associés à cette interface de contrôle.
[java] ===================================================
[java] ==== Hello World with the API ====
[java] ===================================================
[java] CLIENT created
[java] SERVER created
[java] Interfaces du composant Client
[java] 0 name-controller
[java] 1 super-controller
274ANNEXE D. AOKELL : UNE RÉALISATION RÉFLEXIVE DU MODÈLE FRACTAL
[java] 2 component
[java] 3 m
[java] 4 binding-controller
[java] 5 lifecycle-controller
[java] 6 logger-controller
[java] 7 s
[java] 20 ao^
ut 2006 18:29:32 org.objectweb.util.monolog.wrapper.java
Log.Logger log
[java] INFO: Component SERVEUR called
[java] Server: print method called
[java] at cs.impl.ServerImpl.print(ServerImpl.java:31)
[java] at aokell.generated.cs.impl.ServiceImplementedInterface.print
[java] at cs.impl.ClientImpl.run(ClientImpl.java:44)
[java] at aokell.generated.java.lang.RunnableImplementedInterface.run
[java] at aokell.generated.java.lang.RunnableBoundableInterface.run
[java] at cs.Main.main(Main.java:53)
[java] at hw.Main.main(Main.java:33)
[java] Server: begin printing...
[java] ->hello world
[java] Server: print done.
Bilan
Nous venons de définir un contrôleur de journalisation pour des membranes à base
d’objets. Nous avons pour cela défini une interface de contrôle, une implémentation pour
le contrôleur associé puis nous avons enregistré ce contrôleur dans la définition des mem-
branes primitives. Ainsi, chaque nouveau composant primitif créé se verra équipé d’un
contrôleur de journalisation.
Exercice D.2 Faire en sorte que les composants associés aux membranes loggablePrimi-
tive soient automatiquement enregistrés auprès du contrôleur de journalisation (indice :
méthode initFcCtrl et interface cachée /content).
import org.objectweb.fractal.aokell.dream.lib.control.logger.DreamLogType;
import org.objectweb.fractal.aokell.glue.SpoonHelper;
import spoon.processing.AbstractProcessor;
import spoon.reflect.declaration.CtClass;
import spoon.template.Substitution;
import spoon.template.Template;
/**
* @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
*/
public class LoggerControllerProcessor
extends AbstractProcessor<CtClass> {
final private static Template t = new LoggerControllerTemplate();
public void process(CtClass ct) {
SpoonHelper.insert( ct, DreamLogType.class, t );
} }
Figure D.2 – Code du processeur Spoon pour le code glu du contrôleur de journalisation.
Comme nous pouvons le constater le code glu défini dans le template ne fournit pas
directement le code de contrôle mais délègue sa réalisation à l’instance implémentant
le contrôleur. Il s’agit d’une bonne pratique issue de la programmation par aspects qui
consiste à découpler la logique d’intégration (l’aspect ou dans notre cas, le processeur et
le template Spoon) de la réalisation concrète de la fonctionnalité à intégrer.
package org.objectweb.fractal.aokell.glue;
import org.objectweb.dream.control.logger.Loggable;
import org.objectweb.fractal.aokell.dream.lib.control.logger.
LoggerControllerItf;
import org.objectweb.fractal.aokell.lib.control.component.ComponentItf;
import org.objectweb.fractal.aokell.lib.util.FractalHelper;
import spoon.template.Local;
import spoon.template.Template;
/**
* @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
*/
public class LoggerControllerTemplate
implements Template, LoggerControllerItf {
@Local
public LoggerControllerTemplate() {}
@Local
private ComponentItf _fcComp;
private LoggerControllerItf getLoggerC() {
return (LoggerControllerItf)
FractalHelper.getFcInterface(_fcComp,"logger-controller"); }
Figure D.3 – Code du template Spoon pour le code glu du contrôleur de journalisation.
278ANNEXE D. AOKELL : UNE RÉALISATION RÉFLEXIVE DU MODÈLE FRACTAL
Exercice D.3 Faire en sorte que le code glu injecte également l’interface Loggable.
<definition name="org.objectweb.fractal.aokell.lib.membrane.primitive.Primitive">
<controller desc="mComposite"/>
</definition>
Figure D.5 – Définition de l’architecture des membranes de contrôle pour les composants primitifs.
<definition name="org.objectweb.fractal.aokell.lib.control.logger.
LoggerControllerType">
<interface
name="//logger-controller"
signature="org.objectweb.fractal.aokell.lib.control.logger.
LoggerControllerItf"
role="server" />
</definition>
Le type du composant comprend une seule interface serveur de signature LoggerCon-
trollerItf et de nom //logger-controller. Nous pouvons maintenant définir son
implémentation dans le fichier LoggerController.fractal en héritant de la définition
de son type :
<definition name="org.objectweb.fractal.aokell.lib.control.logger.
LoggerController"
extends="org.objectweb.fractal.aokell.dream.lib.control.logger.
LoggerControllerType" >
<content class="org.objectweb.fractal.aokell.lib.control.logger.
BasicLoggerControllerImpl" />
<controller desc="mPrimitive" />
</definition>
Le composant est implémenté par la classe BasicLoggerControllerImpl et est associé
à une membrane mPrimitive.
D.3. CONTRÔLEUR DE JOURNALISATION ORIENTÉ COMPOSANT 281
Bilan
Nous venons de définir un contrôleur de journalisation pour des membranes à base de
composants. La déclaration de ce nouveau contrôleur a été faite statiquement en modifiant
la description Fractal ADL du composite associé aux membranes primitives. Tout nouveau
composant primitif créé sera équipé de ce contrôleur.
Au delà de cette modification statique, AOKell permet d’introspecter dynamiquement
une membrane existante pour en modifier l’architecture. Il aurait donc été envisageable d’y
ajouter une instance du contrôleur de journalisation. Néanmoins, la difficulté d’une telle
manipulation réside dans le type du composite correspondant à la membrane primitive.
Comme nous l’avons vu, ce composite doit exporter toutes les interfaces de contrôle de la
membrane. Donc, ajouter une nouvelle interface de contrôle dynamiquement revient à faire
282ANNEXE D. AOKELL : UNE RÉALISATION RÉFLEXIVE DU MODÈLE FRACTAL
Annexe E
Joram : un intergiciel de
communication asynchrone
E.1 Introduction
Dans cette section, nous présentons les principales caractéristiques des systèmes à
messages et les motivations pour leur usage, avant une brève introduction à JMS et à
Joram, sujets qui seront développés dans les sections suivantes.
Application X Application Y
API JMS API JMS
Application Z
Message Queue
4
Application C
5
receive
Application B
Gestion des
messages 1a subscribe
Application A
receive
publish
2
. 3a 1b
Topic 3b
B C
Application C
Gestion des subscribe
abonnements
receive
Comme toutes les autres réalisations de plates-formes JMS, Joram est structuré en
deux parties : une partie «serveur Joram» qui gère les objets JMS (Queues, Topics ,
connexions, etc.) et une partie « client Joram » qui est liée à l’application cliente JMS.
Comme nous le verrons plus loin en détaillant l’architecture de Joram, le serveur Joram
peut être mis en œuvre de façon centralisée ou de façon distribuée. La communication
entre un client Joram et un serveur Joram s’appuie sur le protocole TCP/IP. Une va-
riante, présentée plus loin, consiste à utiliser le protocole HTTP/SOAP pour les clients
JMS développés dans un environnement J2ME. La communication entre deux serveurs Jo-
ram peut utiliser différents types de protocoles selon les besoins (TCP/IP, HTTP, SOAP,
communication sécurisée via SSL). Client et serveurs peuvent être sur des machines phy-
siques différentes ; ils peuvent partager la même machine et s’exécuter dans des processus
différents ou partager le même processus.
Serveur JORAM
(queues, topics , . .)
agent agent
MOM distribu
Plate-forme JORAM
En résumé, Joram est une plate-forme JMS disponible en open source, dont la
réalisation s’appuie sur un intergiciel à messages qui exploite une technologie d’agents
distribués, présentée brièvement en section 4. La structure générale de la plate-forme Jo-
ram est représentée sur la figure 4.
JNDI
Destination
Client JMS
ConnectionFactory
+ MessageProducer
Connection
+ MessageConsumer
Session
Les étapes et les objets caractérisant une application JMS sont résumés dans la figure
6.
E.2.3 Messages
Les systèmes de messagerie manipulent les messages comme des entités comportant un
entête et un corps. L’en-tête contient l’information utile pour l’identification et le routage
du message ; le corps contient les données utiles à l’application. Le modèle de message
JMS répond aux critères suivants :
– Il fournit une interface de message unique.
– Il supporte les messages contenant du texte, des objets Java ou des données XML.
ou non2 ) ; il est fixé par la méthode d’envoi de message en fonction des paramètres
spécifiés.
– JMSMessageId : ce champs contient un identificateur qui identifie de manière unique
chaque message envoyé par un système de messagerie. Il est fixé par la méthode
d’envoi de message et peut-être consulté après envoi par l’émetteur.
– JMSTimeStamp : ce champs contient l’heure de prise en compte du message par le
système de messagerie, il est fixé par la méthode d’envoi de message.
– JMSReplyTo : ce champs contient la Destination à laquelle le client peut
éventuellement émettre une réponse. Il est fixé par le client dans le message.
– JMSExpiration : ce champs est calculé comme la somme de l’heure courante (GMT)
et de la durée de vie d’un message (time-to-live). Lorsqu’un message n’est pas
délivrée avant sa date d’expiration il est détruit ; aucune notification n’est définie
pour prévenir de l’expiration d’un message.
– JMSCorrelationId, JMSPriority, JMSRedelivered, JMSType, etc.
Propriétés
Les propriétés permettent à un client JMS de sélectionner les messages en fonction de
critères applicatifs. Un nom de propriété doit être de type String ; une valeur peut être :
null, boolean, byte, short, int, long, float, double et String. Un client peut
ainsi définir des filtres en réception dans l’objet MessageConsumer à l’aide d’une chaı̂ne
de caractère dont la syntaxe est basée sur un sous-ensemble de la syntaxe d’expression de
conditions du langage SQL.
Corps du message
JMS définit cinq formes de corps de messages :
– StreamMessage : un message dont le corps contient un flot de valeurs de types
primitifs Java ; il est rempli et lu séquentiellement.
– MapMessage : un message dont le corps est composé d’un ensemble de couples noms-
valeurs.
– TextMessage : un message dont le corps est une chaı̂ne de caractère (String).
– ObjectMessage : un message dont le corps contient un objet Java sérialisable.
– BytesMessage : un message dont le corps est composé d’un flot d’octets ; ce type de
message permet de coder un message conformément à une application existante.
Objet Connection
Un objet Connection représente une connexion active avec le système de messagerie.
Cet objet supporte le parallélisme, c’est-à-dire qu’il fournit plusieurs voies de commu-
nication logiques entre le client et le serveur. Lors de sa création, le client peut devoir
s’authentifier. L’objet Connection permet de créer une ou plusieurs sessions. Lors de sa
création une connexion est dans l’état stoppé 3 . Elle doit être explicitement démarrée pour
recevoir des messages. Une connexion peut être temporairement stoppée puis redémarrée ;
après son arrêt, il peut y avoir encore quelques messages délivrés ; arrêter une connexion
n’affecte pas sa capacité à transmettre des messages. Du fait qu’une connexion nécessite
l’allocation de nombreuses ressources dans le système de messagerie, il est recommandé de
la fermer lorsqu’elle n’est plus utile.
Objet Session
Un objet JMS Session est un contexte mono-thread pour produire et consommer des
messages. Il répond à plusieurs besoins :
– Il construit les objets MessageProducer et MessageConsumer.
– Il crée les objets Destination et Message.
– Il supporte les transactions et permet de grouper plusieurs opérations de réception
et d’émission dans une unité atomique qui peut être validée (resp. invalidée) par la
méthode Commit (resp. Rollback).
– Il réalise l’ordonnancement des messages reçus et envoyés.
– Il gère l’acquittement des messages.
Bien qu’une session permette la création de multiples objets MessageProducer et
MessageConsumer, ils ne doivent être utilisés que par un flot d’exécution à la fois (contexte
mono-threadé). Pour accroı̂tre le parallélisme un client JMS peut créer plusieurs ses-
sions, chaque session étant indépendante. Dans le mode Publish/Subscribe, si deux sessions
s’abonnent à un même sujet, chaque client abonné (TopicSubscriber) reçoit tous les mes-
sages émis sur le Topic . Du fait qu’une session nécessite l’allocation de ressources dans le
système de messagerie, il est recommandé de la fermer lorsqu’elle n’est plus utilisée.
Objet MessageConsumer
Un client utilise un objet MessageConsumer pour recevoir les messages envoyés à
une destination particulière. Un objet MessageConsumer est créé en appelant la méthode
CreateConsumer de l’objet Session avec un objet Destination en paramètre. Un objet
MessageConsumer peut être créé avec un sélecteur de message pour filtrer les messages à
consommer. JMS propose deux modèles de consommation (synchrone et synchrone) pour
les messages. Dans le modèle synchrone un client demande le prochain message en uti-
lisant la méthode Receive de l’objet MessageConsumer (mode de consommation Pull).
Dans le mode asynchrone il enregistre au préalable un objet qui implémente la classe
MessageListener dans l’objet MessageConsumer ; les messages sont alors délivrés lors de
leur arrivée par appel de la méthode onMessage sur cet objet (mode de consommation
Push).
3
afin de ne pas perturber l’initialisation de l’application par l’arrivée de messages.
E.2. LE MODÈLE DE PROGRAMMATION JMS 293
Objet MessageProducer
Un client utilise un objet MessageProducer pour envoyer des messages à une destina-
tion. Un objet MessageProducer est créé appelant la méthode CreateProducer de l’objet
Session avec un objet Destination en paramètre. Si aucune destination n’est spécifiée
un objet Destination doit être passé à chaque envoi de message (en paramètre de la
méthode Send). Un client peut spécifier le mode de délivrance, la priorité et la durée de
vie par défaut pour l’ensemble des messages envoyés par un objet MessageProducer. Il
peut aussi les spécifier pour chaque message.
Construction de messages
byte[] data
BytesMessage message = session.createByteMessage();
message.writeBytes(data);
Utilisation d’un TextMessage
StringBuffer data
TextMessage message = session.createTextMessage();
message.setText(data);
message.writeString(" &");
message.writeDouble(doubleValue);
message.writeLong(longValue);
Point-to-Point
Publish/Subscribe
// Sending (publishing) of all of these message types is done in the same way:
publisher.publish(message);
// Receiving of all of these message types is done in the same way. When the
// client subscribed to the Topic, it registered a message listener. This listener
// will be asynchronously notified whenever a message has been published to
// the Topic. This is done via the onMessage() method in that listener class.
// It is up to the client to process the message there.
void
onMessage(Message message) throws JMSException {
// unpack and handle the messages we receive.
...
}
BytesMessage
byte[] data;
int length;
length = message.readBytes(data);
TextMessage
StringBuffer data;
data = message.getText();
MapMessage // Note : l’ordre de lecture des champs est quelconque.
String name = message.getString("Name");
double value = message.getDouble("Value");
long time = message.getLong("Time");
StreamMessage // Note : l’ordre de lecture des champs
// doit etre identique a l’ordre d’ecriture.
String name = message.readString();
double value = message.readDouble();
long time = message.readLong();
ObjectMessage
obj = message.getObject();
E.3. ARCHITECTURE DE JORAM 297
Application
Serveur JORAM
Client Application
Joram Serveur JORAM
Q1 TCP/IP
Client
Joram
Application
M M
Client Q2 O MOM distribu Q3 Application
Joram
O Client
M M Joram
Application
Ta
Client
Joram
MOM
Q Queue
Application
embarque
Tb Client
T Topic ˙ l ger ¨
Serveur JORAM
objets temporaires JMS créés par le client Joram lors de l’établissement d’une connexion
logique entre le client et le serveur Joram. Sur le serveur, chaque client est représenté
par un objet Proxy. Cet objet persistant est créé par le serveur lors de la création d’un
utilisateur. Il remplit deux fonctions essentielles :
– La gestion des communications entre le client et le serveur.
– L’acheminement des messages vers / depuis la destination (ici une queue de mes-
sages).
Send Receive
Client Joram
Sender Receiver
Client Joram
Flot de donn es
(mission — rception)
Session Accus de r ception Session
Connection Connection
1 4
2 5
Message
MOM
Proxy-P Message
Proxy-C
MOM
4
Store & Forward 3
Message 5
MOM Queue
Serveur JORAM
Send Receive
Client Joram
Publisher Subscriber
Client Joram
Session Session
Connection Connection
1 2
4 5
Message
MOM
Proxy-P Proxy-C
3 Message
Message MOM
MOM Topic 3
Serveur JORAM
Client Client
ProducteurMessage Consommateur
JMS
Client Client
Joram Joram
Message
Message JMS
MOM
Px-P Q Px-C
T
Message
Message
MOM T MOM
MOM Scalagent
Dans cette figure, les flots liés à l’opération de production sont représentés par des
flèches pleines. Les flots de l’opération de consommation sont représentés par des flèches
pointillées. On ne représente pas les flots liés aux accusés de réception. Dans le cas d’une
communication de type Publish/Subscribe le dialogue est réalisé directement entre les deux
objets Proxy.
Dans cette configuration la simplicité des échanges est une conséquence directe de la
co-localisation de l’objet Queue et des objets Proxy. Un autre élément de simplicité est
lié aux opérations d’administration (i.e. création des utilisateurs, des destinations, etc.)
qui sont regroupées sur un site unique. L’inconvénient majeur de cette solution est son
manque de disponibilité et une capacité d’extension réduite. Une défaillance du serveur
signifie un arrêt du système global. Par ailleurs le nombre de requêtes et d’objets gérés
par le serveur est limité par sa capacité de calcul et de stockage.
Message Message
MOM Px2 MOM
Px1 Px3
Message
MOM
Q1 Q2 Q3 Message
MOM
MOM Scalagent
la plate-forme Joram qui permet, à plusieurs niveaux, de définir les choix de conception
appropriés :
– Organisation des serveurs et clients JMS, et placement des objets JMS. Comme
cela est illustré dans la figure 7, La structure de la plate-forme Joram est de type
snowflake, ce qui donne à l’architecte du système une grande liberté pour décider
de l’emplacement des serveurs et de la répartition des clients sur les serveurs pour
répondre aux besoins de l’application (par exemple pour servir un ensemble de clients
géographiquement proches dans une approche de type serveur de proximité, ou bien
pour respecter certaines contraintes de sécurité). Multiplier le nombre de serveurs
pour une meilleure couverture géographique des clients distribués a un coût d’ex-
ploitation (en terme de machines) qui doit être mis en balance avec la performance
et la fiabilité des communications pour des clients très éloignés d’un serveur. Le di-
mensionnement des serveurs (calcul, mémoire, stockage) est également un point clé
pour la performance et la disponibilité de l’ensemble du système.
– Placement des objets de communications (Queues et Topics ). Sauf dans des cas très
particuliers, on peut noter qu’il est souhaitable de rapprocher les objets Destination
des clients consommateurs. Cette stratégie a un impact positif sur les performances
et la disponibilité des clients.
– Evolution du système et passage à l’échelle. Cette propriété fait référence à la capa-
cité de faire évoluer le système pour répondre aux évolutions de l’application. Joram
fournit ainsi des fonctions d’administration qui permettent d’ajouter et/ou de retirer
un serveur depuis un point central d’administration.
– Protocoles de communication. Plusieurs protocoles de transport sont disponibles
pour la connexion client - serveur et pour les connexions entre serveurs : TCP/IP,
HTTP, SOAP, SSL, etc.
– Paramètres de qualité de service (persistance, sécurité). La fiabilité et la mise en
sécurité des communications a un coût. C’est pourquoi Joram donne la possibilité
de retenir ou non ces options selon les besoins.
– Niveau de disponibilité grâce à la clustérisation et à la réplication (voir plus loin en
section 4).
E.4. JORAM : FONCTIONS AVANCÉES 303
Peu de systèmes aujourd’hui permettent d’agir simultanément sur ces divers paramètres
et de construire ainsi la plate-forme de messagerie adaptée à un environnement particulier.
Le niveau de flexibilité de Joram est de ce point de vue un atout incontestable par rapport
aux produits concurrents.
Topic répliqué
Un « Topic clustérisé » est répliqué sur plusieurs serveurs. Notons que cette forme de
réplication s’applique aussi bien à des serveurs fortement couplés (grappe de machines)
qu’à des serveurs géographiquement distribués. Le principe de fonctionnement du Topic
clustérisé est illustré dans la figure 12.
Sub Sub
Sub
2c
2b 3a Sub
Message
Sub Message Message
N_3
Message
Message T 3b
2a T
N_2 2 3
Message Message
T Message 1b Sub
Message
N_1
1
1a
Pub
Message Sub
Dans cet exemple un Topic (noté T) est répliqué sur trois serveurs N 1 , N2 et N3 . Des
clients JMS connectés à ces serveurs sont supposes s’être abonnés au Topic T. Chaque
serveur est responsable de la gestion des abonnements réalisés par ses propres clients. Une
application cliente sur le nœud N1 publie un message correspondant au Topic T (flot noté
1 dans la figure 12). Le nœud N1 diffuse le message à ses abonnés locaux (flots 1a et 1b)
et fait suivre le message aux nœuds N 2 et N3 (flots 2 et 3). Par la suite, chacun d’entre
eux diffuse le message reçu à ses clients locaux (flots 2i et 3j).
La mutualisation des messages entre les nœuds permet de réduire le trafic. Par ailleurs,
une panne d’un serveur n’affecte que les clients connectés à ce serveur. Le reste de l’appli-
cation continue à fonctionner. L’utilisation d’un Topic clustérisé est transparent du point
de vue du programmeur d’application mais requiert beaucoup d’attention de la part de
l’administrateur du système pour être efficace.
Queue répliquée
Le principe des « queues clustérisées » est un peu différent (voir figure 13). Plusieurs
exemplaires du même objet Queue sont localisés sur des serveurs indépendants. Chacune
de ces copies est accessible uniquement aux clients connectés au serveur correspondant. Si
la charge sur une copie dépasse un certain seuil, les messages reçus ensuite sur cette copie
sont redirigés vers un autre exemplaire de la même queue géré par un autre serveur. Le
seuil est un paramètre configurable qui peut prendre en compte divers critères tels que :
nombre de messages en attente, nombre de requêtes de lecture en attente, délai d’attente
dépassé pour une requête en lecture, etc. Il est important de noter que la sémantique des
queues de message n’est pas modifiée, à savoir qu’un message donné n’est consommé que
par un seul client JMS).
Serveur JORAM
Clustered_Q_1
Message
JMS
Q
Q
Producteurs Consommateurs
Serveur JORAM
Message
JMS
Q
Q
Clustered_Q_2
Comme dans le cas des Topic , le concept de queue clustérisée permet d’améliorer les
performances et le niveau de disponibilité sans impact sur la programmation de l’applica-
tion.
E.4. JORAM : FONCTIONS AVANCÉES 305
Serveur JORAM
Matre
Q1 Ta Connexion
primaire
Q Queue Application
Serveur
JORAM HA Client
Joram
T Topic
Connexion
Q1 Ta apr s reprise
Esclave
Serveur JORAM
Queues et Topics sont répliqués sur des serveurs Joram s’exécutant sur une grappe
de machine. Le serveur maı̂tre exécute les requêtes des clients et propage les opérations
vers le serveur esclave qui réplique le traitement localement. En cas de panne du serveur
maı̂tre, les clients établissent une nouvelle connexion vers le serveur esclave et continuent
leur travail sans interruption. Ce fonctionnement permet un haut niveau de continuité de
service au prix d’une redondance du serveur Joram. La version actuelle du serveur Joram
HA utilise les mécanismes de JGroups.
Passerelle JMS
La passerelle JMS permet à une application JMS gérée par Joram de communiquer
avec une destination gérée par une autre plate-forme JMS (appelée xMQ dans la figure
15) de façon transparente du point de vue du programmeur d’application.
Client XMQ
MessageConsumer
Destination Destination
Passerelle xMQ
Le lien entre une plate-forme Joram et une plate-forme xMQ est réalisé par le biais
d’un objet destination Joram spécifique, appelé destination passerelle (qui peut être une
Queue ou un Topic ), qui est le représentant de la destination finale. L’objet « destination
passerelle » joue deux rôles complémentaires :
– En tant que destination gérée par Joram, il reçoit les messages produits par les
clients producteurs et les requêtes des clients consommateurs gérés par la plate-
forme Joram.
– En tant que représentant d’un objet destination externe, il se comporte comme un
client JMS géré par la plate-forme xMQ pour propager les messages et requêtes vers
la destination finale.
TToom
mccaat t
TCP-Proxy
Soap-Proxy
T Q
JCA 1.5
Joram est conforme aux spécifications de l’architecture JCA 1.5 (J2EE Connector
Architecture), qui décrit la manière d’intégrer des ressources externes dans un serveur
d’application J2EE. Les fonctions suivantes sont disponibles :
– Gestion du cycle de vie de la ressource : création, démarrage, arrêt.
– Gestion des connexions avec les composants EJB
308ANNEXE E. JORAM : UN INTERGICIEL DE COMMUNICATION ASYNCHRONE
Connecteurs Joram
Les connecteurs Joram offrent des passerelles pour réaliser l’interopérabilité avec des
applications externes en utilisant des protocols de transport normalisés. Deux connecteurs
sont disponibles aujourd’hui :
– Passerelle mail : cette fonction permet d’émettre et recevoir des messages JMS en
utilisant le protocole SMTP. La fonction est mise en oeuvre par le biais d’objets
Queues et Topics spécialisés pour réaliser cette interopérabilité. Ces objets sont
configurés et installés comme les destinations normales.
– Passerelle FTP : cette fonction est identique à la précédente pour le protocole FTP.
C’est un dispositif utile lorsque les messages JMS à transporter sont de très grande
taille.
E.4.4 Sécurité
Pour sécuriser les échanges (entre serveurs et entre un serveur et ses clients), Joram
utilise, à la demande, des connexions SSL afin d’authentifier les acteurs et de chiffrer
les messages. La gestion des pare-feux pose des problèmes particuliers. La stratégie re-
commandée consiste à configurer les pare-feux pour autoriser l’usage des ports requis par
Joram. Cette solution s’applique autant pour les communications client - serveur que
pour les communications entre serveurs. Une solution alternative consiste à utiliser, dans
la plate-forme Joram, des protocoles couramment acceptés par les pare-feux (HTTP et
SOAP). La section 4.3.2 a montré comment SOAP peut être utilisé pour sécuriser la liaison
entre clients et serveurs. Les serveurs Joram disposent également d’un module de com-
munication fondé sur HTTP pour permettre les communications entre serveurs traversant
un pare-feu.
Agent Agent
Agent Agent
SendTo SendTo
React React
Le cœur du serveur est un moteur d’exécution qui contrôle le flot d’exécution des agents.
Ce flot est unique et prend la forme d’une boucle qui consiste, pour chaque notification, à
exécuter la méthode associée à la réaction de l’agent destinataire. Le flot exécute aussi le
code des modules de persistance et d’atomicité. Le serveur comporte également des flots
d’exécution associés à la gestion des communications au sein d’un sous-système appelé le
bus local. Les bus locaux des différents serveurs coopèrent pour fournir une mise en œuvre
distribuée d’un bus à message distribué. Ce bus à messages global permet la communication
entre les différents agents, qu’ils soient hébergés par le même serveur ou non. Un bus local
est constitué de deux types de composants : un « canal » et un ensemble de « composants
réseau ».
– Le canal est chargé de distribuer les messages en provenance du moteur d’exécution
vers les composants réseau et vice-versa.
– Les composants réseau sont responsables de l’émission de messages provenant du
canal à destination des serveurs distants. Il existe différents composants réseau cor-
respondant à différents protocoles de communication (par exemple TCP/IP, HTTP,
SOAP, etc.). Par ailleurs, il est possible d’ajouter à un composant réseau des mo-
dules logiciels pour la mise en œuvre de propriétés complémentaires (par exemple
sécurité, ordonnancement causal, etc.).
La structure d’accueil est configurable et fournit différentes politiques de fonctionne-
ment des agents hébergés, en particulier l’atomicité des réactions (i.e. stratégie du « tout
310ANNEXE E. JORAM : UN INTERGICIEL DE COMMUNICATION ASYNCHRONE
ou rien »), et la persistance de l’état des agents (i.e. un changement d’état correspond
à la complétion d’une réaction) Un ensemble d’opérations d’administrations élémentaires
permettent de créer et de configurer des serveurs d’agents dans un environnement Internet.
E.6 Conclusion
Joram est un système de messagerie caractérisé par deux propriétés majeures :
– Il est conforme à la spécification JMS1.1. Cette conformité a été démontrée dans le
cadre de la campagne de certification J2EE 1.4 réalisée en liaison avec le serveur
d’application JOnAS6 . Il est donc possible d’utiliser Joram comme environnement
d’exécution de toute application distribuée qui suit l’interface de programmation
définie par JMS.
6
La spécification JMS est un élément de la spécification J2EE.
E.6. CONCLUSION 311
Composant de
Messagerie asynchrone
Interoprabilit avec intgr dans un
applications patrimoniales serveur J2EE
Connexion de
clients lgers
Coop ration entre
serveurs dapplication
Connexion de
Client lourds
Communication entre
applications JMS
télécommunications, etc. Parmi les usages connus 7 on peut citer l’EDI, l’intégration de
données, l’administration de systèmes et d’applications répartis.
Les travaux en cours sur Joram visent deux objectifs complémentaires :
– Etendre son champ d’application, en particulier dans le domaine des systèmes em-
barqués. Cet objectif requiert de pouvoir réaliser une version « légère » du ser-
veur Joram pour répondre aux contraintes de ressources de nombreuses classes
d’équipements tels que carte à puce, lecteur RFID, contrôleur industriel, etc (en
particulier pour ce qui concerne la mémoire RAM et la mémoire persistante de type
mémoire flash) . Une approche, parmi d’autres, consiste à déporter une fonction store
and forward dans la librairie client. Cette nouvelle structure permettrait également
de mettre en œuvre, sous certaines conditions, des liaisons pair à pair (peer to peer )
entre clients Joram sans avoir recours à un serveur tiers.
– Améliorer les performances et les fonctions d’administration de la plate-forme Jo-
ram. Ces deux aspects sont présentés globalement dans la mesure où ils présentent
des dépendances fortes. La performance est un paramètre difficile à maı̂triser car
il dépend de critères multiples, tels que volume et taille des messages, nombre de
clients, nombre de connexions, etc. L’optimisation d’une plate-forme Joram relève à
la fois de l’évolution de certains mécanismes internes (gestion de la concurrence, de la
mémoire et de la persistance pour ne citer que ceux là), et du bon dimensionnement
de la plate-forme pour un usage déterminé (par exemple les ressources des serveurs,
le placement des objets de communication, etc.). Parmi les pistes explorées, l’usage
d’un système de gestion de base de données pour gérer la persistance des messages
est envisagé. Une piste à plus long terme concerne l’usage de mécanismes auto-
adaptatifs pour permettre à une plate-forme Joram de recueillir des informations
sur son comportement afin de s’adapter « spontanément » à des défaillances (auto-
réparation), à des baisses de performance (auto-optimisation), ou plus simplement
à des changements de configuration (connexion / déconnexion d’un serveur).
Un axe de travail complémentaire concerne l’utilisation de Joram comme moteur d’un
bus de services d’entreprise (ou ESB pour Enterprise Service Bus). L’objectif est d’enrichir
Joram avec des fonctions de transformation de données (fondées sur une représentation
XML) et de routage par le contenu pour répondre aux besoins des utilisateurs en matière
d’intégration d’applications.
Pour conclure, notons que Joram est une brique d’une plate-forme technologique plus
large qui vise à fournir les fondations techniques pour la construction et le déploiement de
solutions d’intégration dans le monde Internet/Java (voir figure 19).
Comme cela a été exposé dans la section 5, Joram a été réalisé en s’appuyant sur
la technologie à base d’agents développée par ScalAgent. En d’autres termes on peut
considérer que Joram constitue une personnalité d’un système réparti à agents qui met
en œuvre l’API JMS. Notons que l’ensemble de c’est l’ensemble du produit « agents +
Joram » qui est est disponible en logiciel libre, comme indiqué dans la figure 19.
Sur la même base technologique, ScalAgent a développé des briques d’intégration
complémentaires qui s’appuient sur deux technologies émergentes : les composants et les
architectures logicielles. L’intérêt pour les composants est une réalité aujourd’hui comme
7
Comme la plupart des produits diffusés en logiciel libre, les retours connus sur les usages de Joram
sont peu nombreux et, en règle générale, peu détaillés.
E.6. CONCLUSION 313
le montre l’usage croissant des architectures à base d’EJB ou .NET. Par ailleurs les com-
posants constituent des unités de programmation à gros grain qui facilitent la réutilisation
et la construction d’applications par assemblage (i.e. sans recours à un langage de pro-
grammation). Cet aspect, qui vise à simplifier et raccourcir le cycle de développement des
applications, n’est pas encore intégré dans les modèles tels que EJB et .NET. Cette ca-
pacité de construction d’applications par assemblage de composants est exploitée par les
approches fondées sur les architectures logicielles. La définition des composants, de leurs
interfaces et des leurs dépendances fonctionnelles est décrite dans un langage déclaratif, ap-
pelé ADL (pour Architecture Description Language) qui prend la forme d’un IDL étendu.
La compilation d’une description ADL fournit une représentation logique de l’application
distribuée. Ce référentiel est ensuite exploité dans toutes les étapes du cycle de vie de
l’application pour automatiser et contrôler les opérations d’administration telles que le
déploiement, la surveillance et la reconfiguration de l’application.
intelligent
B timent
Telecoms
Energie
RFID
Solutions d intgration
Applications Canevas de
Moteur d ESB J2EE/JMS Mdiation
Transformation de donn es API JMS
Routage par le contenu Collecte de donnes,
pr-traitement ,
Transfert vers applications
(en cours) JORAM
Queues, Topics
API Agent
MOM calagent
communication asynchrone fiable
Cette approche a été suivie par ScalAgent pour développer une infrastructure
d’intégration de données d’entreprise (désignée globalement sous le terme « infrastruc-
ture de médiation »). Cette offre regroupe les couches hautes de la figure 19 et comprend
les éléments suivants :
– un ensemble de composants de médiation pour la collecte, le traitement et la re-
montée d’informations d’usage produites par des équipements, services ou appli-
cations délocalisés. Ces composants peuvent être personnalisés et assemblés pour
décrire des flots de données entre des équipements et des applications métiers. A
l’exécution, les composants sont mis en œuvre par des agents exécutés sous le contrôle
d’un ensemble de serveurs d’agents.
– Un ensemble d’outils graphiques pour construire, déployer et administrer des solu-
314ANNEXE E. JORAM : UN INTERGICIEL DE COMMUNICATION ASYNCHRONE
E.7 Références
Documentation en ligne sur Joram
– Page d’accueil : http ://joram.objectweb.org
– Manuel de l’utilisateur : http ://joram.objectweb.org/doc/index.html
– Wiki Joram : https ://wiki.objectweb.org/joram/
Publications concernant les bases technologiques de Joram
Vivien Quéma, Roland Balter, Luc Bellissard, David Féliot, André Freyssinet and Serge
Lacourte. Asynchronous, Hierarchical and Scalable Deployment of Component-Based Ap-
plications. In Proceedings of the 2nd International Working Conference on Component
Deployment (CD’2004), Edinburgh, Scotland, may 2004
Roland Balter, Luc Belissard and Vivien Quéma. A Scalable and Flexible Operation
Support System for Networked Smart Objects. In Proceedings of the 2nd Smart Objects
Conference (SOC), Grenoble, France, May 15-17, 2003
Vivien Quéma, Roland Balter, Luc Bellissard, David Féliot, André Freyssinet and Serge
Lacourte. Administration and Deployment Tools in a Message-Oriented Middleware. In
the 4th ACM/IFIP/USENIX International Middleware Conference (Middleware), Poster
session, Rio de Janeiro, Brazil, June 16-20, 2003.
Vivien Quéma, Luc Bellissard and Philippe Laumay. Application-Driven Customiza-
tion of Message-Oriented Middleware for Consumer Devices. In Proceedings of the Work-
shop on Software Infrastructures for Component-Based Applications on Consumer Devices,
in association with EDOC’02, Lausanne (Switzerland), September 16th, 2002.
L. Bellissard, N. De Palma, A. Freyssinet, M. Herrmann and S. Lacourte. An Agent
Platform for Reliable Asynchronous Distributed Programming. Symposium on Reliable
Distributed Systems (SRDS’99), Lausanne (Suisse), October 20th-22th, 1999.
R. Balter, L. Bellissard, F. Boyer, M. Riveill and J.Y. Vion-Dury. Architecturing and
Configuring Distributed Applications with Olan. In Proceedings of the IFIP International
Conference on Distributed Systems Platforms and Open Distributed Processing (Middle-
ware’98), The Lake District (England), September 15th-18th, 1998.
L. Bellissard, N. de Palma and M. Riveill. Dynamic Reconfiguration of Agent-Based
Applications. ACM European SIGOPS Workshop, Sintra (Portugal), September 7th-10th
1998.
Documentation relative à JMS
E.7. RÉFÉRENCES 315
Annexe F
F.1 Introduction
Cette annexe décrit le fonctionnement et l’utilisation de Speedo, un système de ges-
tion de la persistance d’objets Java. Cette description s’appuie sur le modèle de persis-
tance proposé dans le cadre des spécifications JDO (Java Data Objects) [JSR-000012 2004,
JSR-000243 2006], mises en œuvre par Speedo.
Le sujet de la persistance est au cœur des applications informatiques pratiquement
depuis l’origine de la discipline. Pérénniser les informations manipulées par une applica-
tion par delà son exécution ou les problèmes de pannes afférents à son environnement
d’exécution matériel et logiciel est une propriété essentielle. Les disques durs représentent
le support le plus courant pour gérer les informations pérennes, avec des abstractions lo-
gicielles de plus ou moins haut niveau pour les manipuler, que ce soit des systèmes de
fichiers ou des systèmes de gestion de bases de données.
Il se trouve que les formats des données manipulées dans les langages de programma-
tion et leur représentation dans le support pérenne sont dans tous les cas différents. Le
couplage entre l’espace mémoire lié à l’environnement d’exécution d’un langage et l’espace
mémoire pérenne est central dans le problème de la persistance. Dans le seul contexte du
langage Java, plusieurs propositions ont été faites pour proposer des solutions le plus trans-
parente possible du point de vue du programmeur, que ce soit au travers de spécifications
standard [Sun JSR-000153 2003, JSR-000220 2006, JSR-000012 2004, JSR-000243 2006]
ou de produits spécifiques [Oracle 1994, Hibernate 2002, Enhydra 2002, Castor 1999]. Le
produit Speedo [Chassande-Barrioz 2003] proposé dans le cadre du consortium Objectweb
[ObjectWeb 1999] se situe dans cette mouvance.
de Java : JDO2 [JSR-000243 2006] et EJB3 [JSR-000220 2006]. Ces standards ont eu des
évolutions différentes :
– JDO : l’objectif de JDO est de fournir un standard de persistance indépendant
des supports de stockage (c’est à dire non lié aux seules bases de données rela-
tionnelles), et qui s’applique aussi bien dans le cadre de Java de base [J2SE 2005]
que de Java pour l’entreprise [J2EE 2005]. Deux versions 1 [JSR-000012 2004] et 2
[JSR-000243 2006] de la spécification se sont succédées, sachant que les évolutions
concernent soit des ajouts fonctionnels, soit des différences dans la spécification des
informations de projection vers les bases de données relationnelles. Le modèle de
programmation n’est notamment pas remis en cause entre ces deux versions.
– EJB : les spécifications EJB font partie de l’environnement Java pour l’entreprise
[J2EE 2005]. Elles incluent un support de la persistance à travers les entity beans.
Ces spécifications ont eu trois évolutions majeures, sachant qu’à chaque changement
de version, le modèle de programmation a été complètement changé. Par ailleurs, la
dernière mouture de ces spécifications propose un cadre de gestion de la persistance
Java valide aussi bien pour Java de base [J2SE 2005] que pour Java pour l’entreprise
[J2EE 2005]. Ce standard reste fortement lié aux bases de données relationnelles.
Fonctionnellement, Speedo s’apparente aux clients lourds des bases de données à objets
puisqu’il met en œuvre des techniques de gestion de cache évoluées ou encore de gestion
de concurrence. Il s’appuie sur un certain nombre d’autres canevas comme le montre la
figure F.1, au nombre desquels on retrouve :
– Perseus [Chassande-Barrioz and Dechamboux 2003] : c’est un canevas qui identi-
fie différents composants concourants à la mise en œuvre de systèmes d’échange
de données entre un support de stockage et une mémoire d’exécution d’applica-
tion, prenant en compte des comportements transactionnels ou encore des contextes
d’exécution concurrents. Il définit aussi les liens qui régissent les interactions entre
ces différents composants. Parmi ces composants, nous pouvons citer le gestionnaire
de concurrence, le système de journalisation ou encore le gestionnaire d’E/S.
– MEDOR [Team 2000] : c’est un canevas qui permet de manipuler des requêtes en-
semblistes à travers des arbres combinant des opérations algébriques. Il permet de
s’adapter à différents moteurs d’exécution de requêtes, comme par exemple un mo-
teur SQL.
– JORM [Team 1998] : ce canevas offre un moyen de projeter des objets vers différentes
structures de stockage. Il permet notamment de supporter les références entre objets
et la navigation à travers ces références pour ce qui concerne le déclenchement d’E/S.
Par ailleurs, l’architecture de Speedo et d’autres canevas de l’écosystème technique
s’appuie sur Fractal [Team 2003] ce qui permet d’expliciter l’architecture logicielle de l’en-
semble. L’intérêt est qu’il est alors possible d’adapter l’environnement Speedo pour chan-
ger certains comportement, et notamment amener de nouvelles politiques de gestion de la
concurrence, de gestion de remplacement des objets dans le cache, etc.
qui ne mettent pas en cause la portabilité des applications et qui sont autant d’atouts
vis-à-vis d’autres solutions ou produits de persistance équivalents.
Gestion de la concurrence
Les solutioons de persistance actuelles délèguent la gestion de la concurrence d’accès
au système de gestion de base de données sous-jacent. Cela a l’avantage de permettre à
d’autres applications n’utilisant pas Speedo d’accéder à la base de données en cohérence
avec les applications Speedo.
Si cette contrainte est levée, Speedo supporte la gestion de la concurrence directement
en mémoire au niveau des objets Java, ce qui permet d’éviter nombre d’accès inutiles à
la base de données pour effectuer des prises de verrou. Là aussi différentes politiques sont
proposées comme des politiques pessimiste ou optimiste, avec gestion des situations de
dead locks.
JDO est une spécification qui définit un modèle de programmation transparent pour la
persistance d’objets Java vers un système de stockage potentiellement transactionnel. Les
produits JDO sont donc des outils intergiciels de projection d’objets langage vers diverses
structures de stockage. Les avantages de JDO sont les suivants :
F.2. GESTION D’OBJETS PERSISTANTS AVEC JDO : LES PRINCIPES 321
un autre objet représentant cet état comme le montre la figure F.3. Cette démarche per-
met à Speedo de mettre en œuvre des politiques de concurrence en mémoire indépendantes
de celles fournies par le support de stockage sous-jacent. Evidemment, ces choix d’archi-
tecture sont globalement transparents pour le programmeur, sauf dans la phase de mise
au point de programme où le programmeur a accès aux champs persistant indirectement
dans l’objet qu’il a défini : il lui faut les chercher dans l’objet state référencé par son objet
persistant. Enfin, certaines politiques de concurrence sont incompatibles avec le processus
de mise au point (pas de référence vers l’objet état) : c’est le cas de la politique optimiste
gérée en mémoire par Speedo.
Principe de l’enhancement
JDO permet d’utiliser des classes Java et de rendre leurs instances persistantes. Ce-
pendant l’ajout de la propriété de persistance à une classe nécessite de lui appliquer un
traitement post compilation appelée enhancement. Lors de cette étape, le fichier .class
subit des transformations dépendant de sa persistance décrite dans un descripteur JDO
(fichier .jdo). Après cette opération les classes ainsi transformées peuvent être utilisées
dans une application JDO ou dans une application n’utilisant pas JDO.
Toute compilation (initiale ou recompilation suite à une modification des sources)
des classes à rendre persistantes nécessite de lancer l’enhancer (programme modifiant
les classes pour prendre en compte la persistance). L’étape d’enhancement peut être stan-
dard ou particulière à une implantation JDO. Afin d’obtenir de meilleures performances
et offrir un modèle de programmation plus intéréssant, Speedo a son propre enhancer.
Celui-ci s’utilise via une tâche Ant (cf. http ://ant.apache.org/).
étape est réalisée par l’enhancer de Speedo. Durant cette étape, les classes persistantes
sont modifées et de nouvelles classes sont générées par Speedo afin de gérer la persistance.
Compilation
.java .class
Enhancement
.class
.jdo
Speedo fournit une tâche Ant pour le le lancement de l’enhancement. Comme pour
toute tâche Ant, il faut d’abord la définir avant de l’utiliser. La définition d’une tâche
Ant se fait en utilisant la tâche taskdef. Ci dessous, un exemple de target d’initalisation
définissant les tâches Speedo :
F.3. CHAINE DE PRODUCTION DE PROGRAMME 325
<target="init">
<!-- repertoire contenant les sources des classes persistantes -->
<property name="src" value="${basedir}/src"/>
<!-- repertoire ou sont mis les classes compilées ou enhancées -->
<property name="build" value="${basedir}/build"/>
<!-- Repertoire de la distribution Speedo -->
<property name="speedoDist" value="${basedir}/../speedo"/>
<target="compile" depends="init">
<mkdir dir="${build}"/>
<!-- compilation -->
<javac srcdir="${src}" destdir="${build}" debug="on">
<classpath refid="classpath"/>
<include name="**/*.java"/>
</javac>
<!-- enhancement -->
<speedo-jdo src="${src}" output="${build}" />
</target> .
init:
compile:
[mkdir] Created dir: /usr/local/home/developper/workspace/speedo/output/test
/jdo
[javac] Compiling 94 source files to /usr/local/home/developper/workspace/sp
eedo/output/test/jdo
[speedo-jdo] [INFO] Computing classes requiring enhancement ...
[speedo-jdo] [INFO] Completing the meta data...
[speedo-jdo] [INFO] Auto O/R Mapping...
[speedo-jdo] [INFO] Building Jorm Meta Information...
[speedo-jdo] [INFO] Generating Jorm files...
[speedo-jdo] [INFO] Generating Speedo files...
[speedo-jdo] [INFO] Serializing Meta Info...
[speedo-jdo] [INFO] Enhancing Persistent classes...
[speedo-jdo] [INFO] Enhancing Persistent aware classes...
[speedo-jdo] [INFO] Compiling Java files...
[speedo-jdo] [INFO] All Done in 8s 282ms
BUILD SUCCESSFUL
Total time: 34 seconds
{prompt}
Cette trace montre notamment les différentes étapes de l’enhancer Speedo. Le pro-
cessus d’enhancement Speedo étant gourmand en mémoire, il peut être nécessaire de
gonfler la mémoire allouée au processus Ant l’exécutant (problème de Out of memory à
l’enhancement). Pour cela, il faut définir ou modifier la variable d’environnement ANT OPTS
pour qu’elle contienne les paramètres suivant : -Xms130m -Xmx130m -Xss256k. Dans ce cas,
la mémoire mise à disposition de la tâche Ant est de 130 Mo. On peut augmenter ce chiffre
suivant le besoin.
Installation de Speedo
Pour pouvoir utiliser Speedo, la première étape est de récupérer le logiciel. Pour
télécharger le logiciel, il faut aller sur le site Web de Speedo : http ://speedo.objectweb.org.
Ensuite, il faut naviguer par le lien download du menu Project Links présent sur la gauche
F.4. PROGRAMMATION D’OBJETS JAVA PERSISTANTS 327
de la page du site. Cliquer ensuite sur la livraison à télécharger. Par exemple, on peut
choisir la livraison binaire Speedo-jdo 1.4.5 bin.zip de la personalité JDO de Speedo.
{prompt} cd /root/Speedo-jdo_1.4.5
{prompt} ant
Buildfile: build.xml
archives:
archives-speedo-jdo.jar:
[speedo-jdo.jar] Building zip: /root/Speedo-jdo_1.4.5/speedo-jdo.jar
archives-speedo-jdo_light.jar:
[speedo-jdo.jar] Building zip: /root/Speedo-jdo_1.4.5/speedo-jdo_light.jar
archives-speedo-jdo_client.jar:
[speedo_client.jar] Building zip: /root/Speedo-jdo_1.4.5/speedo-jdo_client.jar
archives-speedo-jdo_ra.rar:
[speedo_ra.rar] Building jar: /root/Speedo-jdo_1.4.5/speedo-jdo_ra.rar
archives-jonas:
[speedo-jdo_for_jonas_ra.rar] Building zip: /root/Speedo-jdo_1.4.5/speedo-jdo_fo
r_jonas_ra.jar
[speedo-jdo_for_jonas_ra.rar] Building jar: /root/Speedo-jdo_1.4.5/speedo-jdo_fo
r_jonas_ra.rar
archives-weblogic:
[speedo-jdo_for_weblogic_ra.rar] Building jar: /root/Speedo-jdo_1.4.5/speedo-jdo
_for_weblogic_ra.rar
BUILD SUCCESSFUL
Total time: 36 seconds
{prompt}
Cela permet de construire les libariries permettant d’utiliser Speedo. Ces librairies
contienent des fichiers de configuration correspondant à des configurations par défaut
dédiées à l’environnement d’exécution cible (cf. section F.4.1). Les librairies générées se
retrouvent dans le répertoire /root/Speedo-jdo 1.4.5.
code du connecteur JCA Speedo, ainsi que tous les fichiers de configuration dans une
archive .rar. C’est ce que propose le fichier speedo-jdo ra.rar. Ce fichier contient
notamment un fichier de configuration spécifique au serveur d’application. Deux fi-
chiers spécifiques sont proposés : un pour Jonas et l’autre pour Weblogic de BEA.
Les autres modes d’intégration ne sont pas décrits ici. Là encore, les fichiers de confi-
guration peuvent être modifiés dans l’arborescence avant création des librairies. Il
s’agit respectivement des fichiers jonas-ra.xml et weblogic-ra.xml dans le répertoire
/root/Speedo-jdo 1.4.5/etc. Il permettent principalement de définir le nom JNDI
sous lequel le connecteur Speedo est enregistré dans le serveur correspondant.
Cette commande permet d’activer l’application Java dont le point d’entrée est défini
par la classe com.org.AppClass. On suppose que le code de cette application est accessible
par le chemin défini par app path. Il peut s’agir d’un répertoire ou d’une autre archive
Java.
entre une instance persistante et une instance non persistante sont la possession d’un
identifiant et la connectivité à la solution JDO.
La façon de récupérer une PMF dépend du cadre d’utilisation du pilote JDO. Dans
un serveur d’application J2EE et si le driver JDO est installé comme un resource adapter
(cf. spécification JCA) alors la PMF est récupérée par l’application en utilisant le service
JNDI. Pour plus de détails sur l’intégration J2EE / JDO, il faut se référer au chapitre F.7.
Dans une application Java standard, la classe JDOHelper (incluse dans les APIs JDO)
doit être utilisée comme dans l’exemple suivant :
F.4. PROGRAMMATION D’OBJETS JAVA PERSISTANTS 331
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
...
Properties p = new Properties();
ClassLoader cl = getClass().getClassLoader();
InputStream is = cl.getResourceAsStream("myJdoConf.properties");
if (is == null) {
throw MyException("fichier de configuration introuvable");
}
p.load(is);
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(p); .
import javax.jdo.PersistenceManager;
...
PersistenceManager pm = pmf.getPersistenceManager();
try {
...
} catch (...) {
...
} finally {
if (pm != null && !pm.isClosed()) {
pm.close();
}
} .
Tout PM récupéré doit absolument être fermé après utilisation et ceci quels que soient
les événements survenus au cours de l’exécution (exceptions, annulation de transaction,
..). En effet, le PM mobilise généralement des ressources physiques ou des ressources cri-
tiques qui, si elles ne sont pas libérées, sont une source d’ennuis majeurs pour la suite de
l’exécution de l’application (c’est une des sources majeure de problème lorsqu’on utilise
JDO).
Il est donc important que les PMs soient libérés. La fermeture d’un PM se fait en
appelant la méthode close. L’exemple précédent illustre le type de séquence de code
permettant de libérer les ressources dans tous les cas.
332 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Identifiant applicatif
Pour un identifiant applicatif, une classe spécifique doit être définie. Elle doit être
composée du même ou des mêmes champs de la classe persistante composant l’identifiant.
Dans la classe identifiant, les champs doivent être déclarés public. Les champs composant
l’identifiant peuvent être automatiquement calculés (cf. section F.4.3).
Dans l’exemple suivant, la classe ClientId, contenant les champs public nom et prenom,
est la classe identifiant associée à la classe persistante Client. Il est clair qu’on retrouve
les mêmes champs dans les deux classes associées.
Dans le descripteur JDO, le nom de la classe d’identifiant associée est spécifié par
l’attribut objectid-class au niveau de la balise de la classe. Chacun des champs persistants
utilisés dans l’identifiant doit par ailleurs être marqué comme primary-key :
Lorsque l’identifiant d’une classe persistante est composé d’un seul champ applicatif,
JDO propose une simplification au développeur en fournissant des classes pré écrites pour
certains types de champ. Dans ce cas là, il n’est pas nécessaire de spécifier la classe d’iden-
tifiant associée à la classe persistante car elle est déduite par le type du champ marqué
unique comme primary-key :
F.4. PROGRAMMATION D’OBJETS JAVA PERSISTANTS 333
package javx.jdo.identity;
Toutes les classes pouvant servir d’identifiant sont définies dans le package
javax.jdo.identity. Le type de l’unique champ peut être : byte, char, short, int, long ou
tout type objet. Cependant, pour des raisons de conception, il est tout à fait possible de
définir sa propre classe contenant le champ unique.
Les classes définies par le développeur et servant d’identifiant pour une classe persis-
tante sont soumises à un certain nombre de contraintes :
– Un constructeur public sans argument est obligatoire.
– Un constructeur avec un unique argument de type String est obligatoire. Le pa-
ramètre de type String est le résultat de la méthode toString et doit permettre de
reconstituer un identifiant à partir de sa forme String.
– Les méthodes equals et hascode doivent être implémentées en utilisant les champs
de l’identifiant.
– La méthode toString doit être surchargée afin de renvoyer une image sous la forme
d’un chaine de caractères du contenu de l’identifiant. Le résultat de cette méthode
toString doit pouvoir être donné au constructeur avec le paramètre String décrit
précédemment.
– Les champs composant l’identifiant doivent être définis de la même manière dans
la classe persistante et dans la classe d’identifiant. Dans la classe d’identifiant ces
champs doivent être public.
– Pour les classes persistantes avec héritage, toutes les classes concrètes d’un arbre
doivent avoir le même identifiant que la classe concrète la plus générale.
Par exemple, prenons 3 classes persistantes A, B et C telles que B et C sont des sous
classes de A et AId est la classe d’identifiant de A :
– Si A est concrète alors B et C doivent utiliser AId comme classe d’identifiant. Cepen-
dant, certaines implémentations conseillent d’utiliser des classes BId et CId pour
respectivement les classes B et C où BId et CId sont des sous classes de AId ne
contenant pas de nouveaux champs.
– Si la classe A est abstraite, et B et C sont concrètes, alors une sous classe de A (B
ou C) peut avoir une classe d’identifiant différente de AId, c’est à dire BId ou CId.
Cette classe d’identifiant doit alors être une sous classe de AId et peut contenir de
nouveaux champs pour composer l’identifiant.
334 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Cette section montre bien que la manipulation d’identifiant applicatif est laborieuse.
Par ailleurs, l’utilisation de tels identifiants reste difficile car le programmeur n’est pas
à l’abri d’un changement des informations d’un identifiant, ce qui pose de nombreux
problèmes, comme par exemple la perte de validité de toutes les références vers l’objet
changeant d’identité.
Identifiant système
Les identifiants systèmes, définis par la catégorie datastore, regroupent tous les iden-
tifiants dont les valeurs ne sont pas attribuées par l’application. La valeur de l’identifiant
n’est pas exposée dans un champ de la classe persistante. Il faut utiliser les APIs de JDO,
notamment le PM pour récupérer un tel identifiant. La stratégie de calcul de l’identifiant
se définit dans le descripteur JDO au moyen de la balise datastore-identity comme dans
l’exemple suivant :
Dans cet exemple, la stratégie est uuid-hex, c’est à dire un identifiant de type UUID
sous la forme hexadécimale. Les stratégies possibles sont détaillées dans la section qui
suit. Il est clair que l’utilisation de type d’identifiant est beaucoup plus simple pour le
programmeur car elle offre une transparence totale : pas de champ d’identifiant à déclaré,
pas de valeurs à gérer. Par ailleurs, le système garantit l’immutabilité des identifiants de
ce type, évitant les problèmes d’incohérence comme celui de la validité des références.
Les champs persistants d’une classe d’identifiant ou les identifiants système peuvent
recevoir une valeur générée. La génération de la valeur peut suivre sept stratégies possibles :
– native : L’identifiant est choisi par l’implémentation JDO.
– identity : L’identifiant est choisi par le support de stockage lui-même. Par exemple,
on peut vouloir utiliser directement l’identifiant fourni par une base de données à
objets.
– sequence : L’identifiant est calculé en utilisant une séquence. La séquence peut être
une séquence gérée par le support de stockage (par exemple une séquence SQL) ou
par le pilote JDO lui-même.
– autoassign : L’identifiant est projeté sur une colonne SQL auto incrémentée.
– increment : L’identifiant est une valeur entière calculée à partir de la valeur maximum
des identifiants des autres instances d’une classe. Cet identifiant ne doit être utilisé
que lorsque l’application JDO est la seule à insérer des nouvelles données en base.
En effet, pour des raisons de performance, l’implémentation JDO ne calcule que la
valeur maximum qu’au démarrage. Elle gère cette valeur en mémoire par la suite.
– uuid-string : L’identifiant est une chaı̂ne de 16 caractères générés par
l’implémentation JDO. La valeur générée est unique et est au format UUID.
F.4. PROGRAMMATION D’OBJETS JAVA PERSISTANTS 335
Un objet Java appartenant à une classe persistante est non persistant à sa création par
le programmeur. Deux situations différentes vont amener à ce qu’il devienne effectivement
persistant :
– Le programmeur affecte dans un objet qui est déjà persistant une référence (ou
relation) vers un objet qui ne l’est pas. L’objet ainsi référencé devient alors persistant
et par transitivité tous les objets qu’il référence lui-même. Nous parlons alors de
persistance par attachement.
– Le programmeur utilise les APIs JDO pour rendre l’objet persistant. Nous parlons
dans ce cas de persistance explicite. Dans ce cas, le même principe de transitivité
que décrit précédemment s’applique.
Lorsqu’une instance devient persistante, elle obtient un identifiant. Dans le cas d’iden-
tifiant applicatif, il est nécessaire que le ou les champs composant l’identifiant aient été
préalablement affectés par l’application. Les champs objet doivent notamment être non
nul. Dans le cas d’un identifiant système, l’implémentation JDO calcule la valeur de l’iden-
tifiant au moment où l’objet devient persistant. Selon la catégorie d’identifiant système,
ce processus peut mettre en œuvre un accès au support de stockage (par exemple lors de
l’utilisation d’une séquence SQL).
Persistance explicite
import javax.jdo.PersistenceManager;
...
PersistenceManager pm = pmf.getPersistenceManager();
Banque b = new Banque("Banque de France");
// ici ’b’ n’est pas persistant : c’est un objet Java normal
pm.currentTransaction().begin();
pm.makePersistent(b);
// ici ’b’ est devenu persistant
pm.currentTransaction().commit();
// ici ’b’ est persistant de manière "durable"
pm.close(); .
La persistance par transitivité, ou par attachement n’utilise pas l’API JDO. Comme on
l’a dit, le principe est d’affecter une instance non encore persistante à un champ d’une ins-
tance déjà persistante (cas d’une relation monovaluée entre objets persistants). Cela peut
aussi se faire de façon indirecte dans le cas de relations multi-valuées. En effet, dans ce cas,
l’objet référençant référence d’abord un objet collection qui référence à son tour les objets
qui deviennent persistants. L’objet collection intermédiaire n’est pas considéré comme un
objet persistant (légère perte d’orthogonalité vis-à-vis du langage Java). L’exemple sui-
vant montre un tel cas de propagation de la persistance à travers la relation multi-valuée
agences qui est une collection qui référence toutes les agences d’une banque (définie par
la classe Banque) :
import javax.jdo.PersistenceManager;
...
PersistenceManager pm = pmf.getPersistenceManager();
Banque b = new Banque("Banque de France");
pm.currentTransaction().begin();
pm.makePersistent(b);
// ici ’b’ est persistant
Agence a = new Agence("agence principale");
// ici ’a’ n’est pas persistant
b.getAgences().add(a);
// ici ’a’ est devenu persistant
pm.currentTransaction().commit();
pm.close(); .
F.4. PROGRAMMATION D’OBJETS JAVA PERSISTANTS 337
L’instance a est devenue persistante au moment ou elle a été ajoutée dans la collection
agences possédée par l’instance persistante b. Le fonctionnement est identique pour une
référence simple.
La transitivité de la propriété de persistance est aussi valable pour les objets référencés
par une instance qui devient persistante de manière explicite ou par attachement. Ainsi
lorsqu’une instance devient persistante, c’est toute la grappe d’objets atteignables par cet
objet, qui le devient :
import javax.jdo.PersistenceManager;
...
PersistenceManager pm = pmf.getPesistenceManager();
Banque b = new Banque("Banque de France");
Agence a = new Agence("agence principale");
b.getAgences().add(a);
// ici ni ’b’ ni ’a’ ne sont persistants
pm.currentTransaction().begin();
pm.makePersistent(b);
// ici ’b’ et ’a’ sont devenus persistants
pm.currentTransaction().commit()
pm.close(); .
Destruction explicite
import javax.jdo.PersistenceManager;
...
PersistenceManager pm = pmf.getPersistenceManager();
Banque b = new Banque("Banque de France");
pm.currentTransaction().begin();
pm.makePersistent(b);
pm.currentTransaction().commit();
pm.close();
// ici b designe un objet persistant (present dans l’espace de stockage)
...
pm = pmf.getPersistenceManager();
pm.currentTransaction().begin();
pm.deletePersistant(b);
pm.currentTransaction().commit();
pm.close();
// ici l’objet designe par b n’a plus de representant dans l’espace de stoclage
// en memoire b reference un objet Java non persistant representant une banque
// avec les valeurs qu’avait l’objet persistant avant sa destruction
Destruction ensembliste
La suppression à l’aide des requêtes consiste à définir une requête JDO désignant
l’ensemble des objets persistants à supprimer. Cette fonction de la spécification JDO 2
n’est actuellement pas supportée par Speedo.
Le premier point concerne les références vers des objets détruits. Contrairement à Java,
les destructions d’objets persistants sont pilotées directement par le programmeur. Il est
ainsi possible de détruire un objet persistant alors qu’il est référencé par d’autres objets
persistants. Deux cas se présentent :
– L’objet détruit n’est référencé que par des objets persistants ayant une image dans
le support de stockage mais n’étant pas représentés en mémoire. Lors de la prochaine
activation de ces objets en mémoire, les références devenues invalides à la destruc-
tion de l’objet référencé sonts transformées en pointeurs nul. On se retrouve alors
avec les mêmes comportement que lorsqu’un programmeur utilise une référence Java
nulle (par exemple, génération d’une NullPointerException lors d’une tentative de
déréférencement).
– L’objet détruit est référencé en mémoire par des objets Java persistants. Dans ce
cas, les liens vers l’objet détruit reste valide jusqu’à la validation de la transac-
tion dans laquelle la destruction intervient. dans tout autre contexte transactionnel
(notamment les subséquents), on retombe sur la sémantique décrite dans le point
précédent.
Même si ce comportement n’est pas en ligne avec Java, la sémantique reste néanmoins
claire. Il reste que ce comportement doit être bien compris par le programmeur JDO.
Dans un modèle de données, les classes persistantes sont reliées par des références.
Le graphe peut être connexe ou partiellement connexe. Lorsque l’application requiert une
instance persistante, elle peut souvent nécessité en plus de ce simple objet, un graphe
d’objet connexe à cet objet. La spécification JDO 2 permet de préciser ce graphe par la
notion de fetch-plan, afin par exemple d’optimiser le chargement des données. Un fetch-plan
se définit par un ensemble de fetch-groups.
340 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Les fetch-groups
Un fetch-group est toujours associé à une classe et définit l’ensemble des champs qui
doivent être chargés. Un fetch-group peut aussi se définir à partir d’un autre fetch-group.
Deux fetch-groups existent par défaut : all et default. Le fetch-group default charge les
champs primitifs seulement, tandis que le fetch-group all charge tous les champs. Les
fetch-groups se définissent dans le descripteur de persistance JDO. Un fetch-group est
nommé. La portée de ce nom est globale. Il est donc important de choisir des noms de
fetch-groups complètement qualifiés.
Voici le modèle de donnée utilisé pour les exemples de fetch-group :
Personne
Adresse Pays
<fetch-group name="monfg">
<fetch-group name="detail">
<field name="enfants#element.nom"/>
</fetch-group name="detail"> .
<fetch-group name="monfg2">
<fetch-group name="detail">
<field name="enfants" fetch-depth="1"/>
</fetch-group name="detail"> .
Les fetch-plans
Un unique fetch-plan est associé à chaque PersistenceManager. Il est utilisé pour les
requêtes, les méthodes getObjectById, refresh et detach. Le fetch-plan peut être redéfini à
tout moment par l’application. Un fetch-plan peut contenir des fetch-groups de différentes
classes. Les fetch-groups peuvent se composer ou plus exactement s’additionner.
L’interface javax.jdo.FetchPlan offre les méthodes suivantes :
342 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Ces cinq méthodes permettent de définir le contenu du fetch-plan en listant les noms
des fetch-groups qui le composent.
L’attribut fetchsize définit le nombre maximal d’instances qui seront récupérées par le
fetch-plan. Deux valeurs particulières sont définies par la spécification. FETCH SIZE GREEDY
indique que toutes les instances désignées par le fetch-plan doivent être récupérées
immédiatement. FETCH SIZE OPTIMAL indique que l’implémentation est libre d’optimiser le
chargement des instances désignées.
F.5.1 Introduction
Les requêtes sont définies dans le code de l’application ou dans le descripteur de persis-
tance. Lorsqu’elles sont définies dans le code de l’application, il est possible de construire
dynamiquement des requêtes, ce qui apporte une grande puissance d’expression.
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.util.Iterator;
import java.util.List;
...
PersistenceManager pm = pmf.getPersistenceManager();
pm.currentTransaction().begin();
Query q = pm.newQuery(Client.class);
q.setFilter("this.adresse.endsWith(suffix)");
q.declareParameters("String suffix");
try {
List l = (List) q.execute("Paris");
for (Iterator it = l.iterator(); it.hasNext();) {
Client c = (Client) it.next();
System.out.println("client: " + c);
c.setAdresse(c.getAdresse() + " France");
}
} finally {
q.closeAll();
}
pm.currentTransaction().commit();
pm.close(); .
La requête sélectionne les instances de la classe Client (et ses sous-classes) dont
l’adresse se termine par une chaı̂ne de caractères spécifiée en paramètre. La valeur du
paramètre est "Paris". L’utilisation d’un bloc try finally permet de s’assurer de la
fermeture des itérateurs ouverts sur la collection résultante. En effet, c’est le moyen d’in-
diquer au pilote JDO qu’il peut relâcher les ressources liées à cette requête (resultSet,
connection, ...).
Une requête JDO est un objet qui implémente l’interface javax.jdo.Query. Une ins-
tance de Query se récupère par l’interface PersistanceManager via les méthodes newQuery.
La requête est à définir/personnaliser en utilisant les méthodes setXXX. Les méthodes
execute lancent l’évaluation de la requête et retournent par défaut une liste d’objet (objet
de type java.util.List). Les objets retournés par la requête sont sensibles au fetch plan
courant afin de récupérer des graphes d’objets.
Les requêtes JDO peuvent être utilisées classiquement dans une transaction, mais aussi
en dehors d’une transaction si la propriété nonTransactionalRead est activée (voir la section
sur les transactions).
Méthode Description
setClass(Class) La classe servant de domaine initial d’interrogation.
Par défaut, c’est aussi la classe résultat. Par défaut
les sous classes sont incluses.
setFilter(String) String définissant une expression booléenne comme
filtre de sélection. C’est un peu l’équivalent d’une
clause WHERE en SQL.
declareParameters(String) String déclarant le nom et le type des paramètres uti-
lisés dans le filtre. Le séparateur de paramètre est la
virgule. L’ordre de déclaration correspond exactement
à l’ordre des paramètres dans des méthodes ’execute’.
declareVariables(String) String déclarant les variables utilisées dans le filtre.
Les variables servent principalement à désigner le
contenu d’une collection. Le séparateur entre les
déclarations est le point virgule
declareImports(String) String déclarant le nom complet des classes. Cela
permet d’utiliser le nom court des classes pour la
déclaration des variables, des paramètres ou lors
d’opération de cast. Le séparateur d’import est le
point-virgule.
setResultClass La classe accueillant la projection. Par défaut, le
résultat est la classe initiale.
setOrdering(String) Ordonnancement des résultats
setResult(String) La projection de la requête. Par défaut, la projection
est composée des instances de la classe initiale (”DIS-
TINCT this”). Cette chaı̂ne de caractère permet de
définir une projection plus complexe.
setGroupBy(String) Définit la clause comment les résultats doivent être
groupés pour les opérateurs d’agrégation.
setUnique(boolean) Indique si le résultat est composé d’un seul élément.
Si oui alors l’objet renvoyé par la méthode execute est
directement la valeur. Si non alors la méthode execute
retourne une java.util.List des résultats.
setRange(long pre, long der) Permet de limiter la cardinalité du résultat et de faire
de la pagination. Le premier entier un indique l’in-
dex du premier élément à sélectionner. Le deuxième
entier indique la position du premier élément non
sélectionné.
setIgnoreCache(boolean) Indique si les données modifiées dans le cache L1
doivent être prises en compte dans la requête.
setUnmodifiable() Permet de rendre la requête non modifiable.
F.5. REQUÊTES ENSEMBLISTES 345
PersistenceManager pm = pmf.getPersistenceManager();
pm.currentTransaction().begin();
Query q = pm.newNamedQuery(Client.class, "maRequete1");
try {
List l = (List) q.execute("Paris");
for(Iterator it = l.iterator(); it.hasNext();) {
Client c = (Client) it.next();
}
} finally {
q.closeAll();
}
pm.currentTransaction().commit();
pm.close(); .
Tous les mots clés doivent être écrits soit tout en majuscule, soit tout en minuscule.
L’ordre des clauses défini par la grammaire doit être respecté. La définition d’une requête
sous forme d’une chaı̂ne de caractères est identique à la définition d’une requête en utilisant
346 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
les méthodes set... de l’interface Query. Cela signifie que les valeurs des différentes clauses
de la grammaire ci-dessus, correspondent exactement aux valeurs mises par les méthodes
set... de l’interface Query.
Les opérandes de l’expression sont des chemins dans les graphes de données ou des
paramètres de la requête. Les chemins débutent par la classe initiale ou par une variable et
se terminent par un champ persistant. La navigation prend en compte le polymorphisme
des classes. Pour les champs multi-valués (collection, list, map, ...), il faut utiliser une
variable afin de nommer le contenu. Il est aussi possible d’appeler certaines méthodes :
F.5. REQUÊTES ENSEMBLISTES 347
Méthode Description
contains(Object) S’applique aux types Collection pour définir des va-
riables ou des conditions d’appartenance.
S’applique aux types Map
– get(Object),
– containsKey(Object),
– containsValue(Object),
Query q = pm.newQuery(Department.class,
"this.emps.contains(e) && e.salary > :sal");
Collection emps = (Collection) q.execute(new Float(30000)); .
Sélection des départements dont le nom est contenu dans un paramètre de type collec-
tion.
Les requêtes JDO peuvent renvoyer un ou plusieurs objets. Les APIs JDO permettent
à l’utilisateur de spécifier la cardinalité du résultat grâce à la méthode setUnique. Ainsi,
lorsque la requête est marquée avec setUnique(true), cela signifie qu’il y a un seul résultat.
Par conséquent, afin de simplifier l’utilisation de l’API, la méthode execute renvoie direc-
tement la valeur attendue ou null si la requête ne produit aucun résultat. S’il y a plus
d’une valeur alors une exception JDOUserException est générée.
Voici un exemple sélectionnant un employé par son nom. L’application s’attend à
trouver un seul employé pour un nom donné même si ce n’est pas l’identifiant :
Une requête JDO peut renvoyer un très grand nombre de résultats. Une quantité de
données trop importante peut réduire considérablement les performances. Pour éviter cela,
les APIs JDO permettent de limiter la taille du résultat en spécifiant une fenêtre sur les
éléments requis du résultat. Cette fenêtre est définie par l’index du premier élément requis
et l’index du dernier. Ce mécanisme permet en particulier de faire de la pagination de
résultats en exécutant la requête pour chaque page avec les indexes correspondants.
Pour définir les limites du résultat il faut spécifier l’attribut ’range’ de la requête :
setRange(long from, long toExcl). La première valeur est l’index du premier élément à re-
tourner, tandis que la deuxième valeur est l’index du premier élément à ne pas sélectionner.
Ces indexes suivent exactement les principes des index des tableaux en Java. Cependant
les limites minimum et maximum sont respectivement 0 et Long.MAX VALUE.
Voici un exemple de requête affichant la liste des noms des employés par page :
350 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Lorsque le résultat contient des doublons, il est possible de les éliminer en ajoutant le
mot clé DISTINCT de la manière suivante : q.setResult("DISTINCT name").
Quand le résultat de la requête est composé d’une seule colonne, La collection renvoyée
par la méthode execute contient directement les valeurs. En revanche, si le résultat est
un n-uplet composé de plusieurs colonnes, la collection retournée contient soit un tableau
d’objets, soit une instance de classe spécifiée par la méthode setResultClass.
Voici un premier exemple ne spécifiant pas de classe de résultat :
class Info {
public String name;
public Float salary;
public Employee reportsTo;
}
On note la correspondance entre les noms des colonnes dans le résultat avec les noms
des champs dans la classe résultat. Dans l’exemple, afin de garantir cette correspondance,
l’utilisation d’un alias est nécessaire pour la troisième colonne.
Une variante de cette solution consiste à définir un constructeur dans la classe résultat
Info et de l’utiliser dans la clause result :
class Info {
public String name;
public Float salary;
public Employee reportsTo;
public Info(String n, Float s, Employee e) {
this.name = n ; this.salary = s ; this.reportsTo = e;
}
}
...
q.setResult("new Info(name, salary, boss)");
q.setResultClass(Info.class); .
En résumé, les classes résultantes respectent l’une des deux contraintes suivantes :
– Il existe un constructeur dont la signature est compatible avec les types des colonnes
du résultat.
– Pour chaque nom de colonne du résultat, une des trois sous-contraintes qui suit est
vérifiée :
– Il existe un champ public de même nom et de même type.
– Il une méthode setter correspondante.
352 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
– Il existe une méthode put(Object name, Object value) traitant ce champ. Ce der-
nier cas permet d’utiliser une Map comme conteneur de résultat.
Voici un tableau résumant la forme du résultat en fonction des attributs result,
resultClass et unique :
Dans cet exemple, l’agrégation agissant sur tous les employés, la méthode execute
renvoie automatiquement la valeur sans passer par une liste car l’unicité du résultat est
garantie.
Il est possible d’utiliser des opérateurs d’agrégation dans plusieurs expressions de la
clause result, comme dans l’exemple suivant :
Comme dans SQL, il est possible de grouper les résultats pour les calculs d’agrégats.
Ainsi voici un exemple donnant la moyenne des salaires par département :
Query q = pm.newQuery(Employee.class);
q.setResult("avg(salary), dept.name");
q.setGrouping("dept.name");
Collection results = (Collection) q.execute();
Iterator it = results.iterator(); it.hasNext();) {
Object[] info = (Object[]) it.next();
Float average = (Float) info[0];
String deptName = (String) info[1];
...
} .
Il est possible de filtrer le résultat obtenu après le group by. Voici un exemple renvoyant
les moyennes des salaires par département ayant plus d’un employé :
F.6. PROJECTION VERS UNE BASE DE DONN ÉES RELATIONNELLE 353
Query q = pm.newQuery(Employee.class);
q.setResult("avg(salary), dept.name");
q.setGrouping("dept.name having count(dept.name) > 1");
Collection results = (Collection)q.execute();
for (Iterator it = results.iterator(); it.hasNext();) {
Object[] info = (Object[]) it.next();
Float average = (Float) info[0];
String deptName = (String) info[1];
...
} .
Cette section donne un aperçu de l’étendue des possibilités offertes par le langage de
requête de JDO (et les APIs programmatiques associées) pour effectuer des traitements
ensemblistes. La puissance est équivalente à SQL mais si cela ne suffit pas, il est aussi pos-
sible d’exécuter des requêtes SQL natives dans le cas où une base relationnelle est utilisée
comme support de stockage. Il est clair que cette possibilité doit être utilisée avec parci-
monie car elle rompt une propriété essentielle de l’approche middleware : l’indépendance
et la portabilité vis-à-vis du support de stockage.
Avec ce mode d’identifiant, toutes les formes de projection de l’héritage ne sont pas
supportées par Speedo. De plus amples explications sont données dans la section F.6.6
concernant la projection d’un arbre d’heritage.
class AdresseId {
public String rue;
public String ville;
public AdresseId(String str) {
int idx = s.indexOf(’$’);
this.rue = s.substring(0, idx);
this.ville = s.substring(idx + 1);
}
public String toString() {
return rue + "$" + ville;
}
} .
L’identifiant géré par Speedo est une valeur entière de type java.lang.Long ou long.
Les valeurs sont calculées par un générateur lui-même persistant. Afin de supporter le
polymorphisme, ce nombre entier est décomposé en deux parties. Les 20 bits de poids fort
servent à identifier la classe (soit plus d’un millon de possibilités), tandis que les 44 bits
de poids faible servent à identifier l’instance dans la classe (soit plus de 8 000 millards de
possibilités). Pour utiliser cet identifiant, il suffit de ne rien définir car c’est le mode par
défaut, comme dans l’exemple suivant :
F.6. PROJECTION VERS UNE BASE DE DONN ÉES RELATIONNELLE 355
<class name="A">
<field name="f1"/>
<field name="f2"/>
</class> .
Cependant, il est tout à fait possible de stocker une classe sur plusieurs tables. On
distingue alors une table principale et des tables secondaires. Chaque table secondaire doit
définir une jointure avec la table principale.
L’exemple ci-dessus décrit une classe A ayant trois champs persistants. La classe est
projetée sur 2 tables A TP (A Table Principale) et A TS (A Table Secondaire). La table
secondaire A TS est liée à la table principale par la jointure A TP.ID = A TS.FK ID.
Lorsqu’une classe est projetée sur plusieurs tables, il faut définir la jointure entre les
tables secondaires et la table principale. Il est possible de spécifier plus précisément des
informations sur la colonne comme dans l’exemple suivant :
<class>
<field name="f2">
<column="TS1_F2" table="A_TS1" sql-type="VARCHAR(32)">
</field>
...
</class> .
class A {
long aid; //identifiant de la classe A
B unB; // référence vers la classe B
}
class B {
long bid; //identifiant de la class B
} .
La clé étrangère peut être dans la table correspondant à la classe ayant la référence,
c’est à dire le champ unB :
TA TB
IDA FK_IDB IDB
Dans le cas où la classe référencée possède un identifiant composite (avec plusieurs
champs), le schéma de base est celui décrit dans la figure F.8.
TA TB
IDA FK_IDB1 FK_IDB2 IDB1 IDB2
Pour chacune des colonnes composant l’identifiant, il faut définir le nom de la colonne
correspondant dans la table TA. L’attribut name définit le nom de la colonne dans TA (soit
le nom de la clé étrangère) alors que l’attribut target définit le nom de la colonne dans la
table TB (soit le nom de la clé primaire).
Pour les relations bidirectionnelles, le modèle suivant est utilisé comme exemple :
class A {
long aid; //identifiant de la classe A
B unB; // référence vers la classe B
}
class B {
long bid; //identifiant de la class B
A unA; //référence vers la classe A
} .
TA TB
IDA FK_IDB IDB
Dans le cas d’une relation bidirectionnelle, afin de ne pas alourdir de manière inutile la
description, la projection est déclarée d’un coté seulement. De l’autre, il suffit d’indiquer à
l’aide de l’attribut mapped-by le nom du champ de la référence opposée (attribut inverse
dans le descripteur JDO). La déclaration de la projection est la suivante :
F.6. PROJECTION VERS UNE BASE DE DONN ÉES RELATIONNELLE 359
class A {
long aid; //identifiant de la classe A
Collection<B> mesB; //référence des B
}
class B {
long bid; //identifiant de la class B
} .
La déclaration consiste à définir le nom de la colonne pour les éléments. Par convention
ce nom de colonne est dans la table de l’élément référencé (ici B, soit la table TB).
Pour une projection avec table de jointure (cf. figure F.10), la déclaration est la sui-
vante :
360 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
TA TA TB
IDA FK_IDA FK_IDB IDB
Le principe est de déclarer une table et sa jointure vers la table principale. Cette table
secondaire peut être déclarée au niveau de la classe ou seulement pour un champ comme
ici. De la même manière que le cas sans jointure, il faut définir le nom de la colonne
correspondant à l’élément.
class A {
long aid; //identifiant de la classe A
Collection<B> mesB; //référence des B
}
class B {
long bid; //identifiant de la class B
A unA; //référence un A
} .
Ici c’est la déclaration la plus simple qui a été choisie en définissant la projection de
la référence mono valuée (champ unA) et en indiquant que le champ inverse (champ mesB)
était à déduire de la projection du premier champ. Pour une projection avec table de
jointure, la déclaration est la suivante :
class A {
long aid; //identifiant de la classe A
Collection<B> mesB; //référence des B
}
class B {
long bid; //identifiant de la class B
Collection<A> mesA; //référence des A
} .
Le principe général des relations bidirectionnelles est de définir la projection d’un seul
sens de la relation. Pour l’autre sens (l’autre référence), il suffit d’indiquer le nom du champ
362 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
class A {
String id;
String f1;
}
class B extends A {
String f2;
} .
TA
ID F1 F2
– Filtrée : Une table contient tous les champs (ceux de la classe mère et ceux de la
classe fille). L’unique table contient des instances de la classe mère et des instance
de la classe fille. Ce mode de projection est illustré dans la figure F.11.
TB
ID F1 F2
– Horizontale : Chaque classe a sa propre table contenant tout les champs herités et
les champs de la classe courante. La table ne contient que des instances de la classe
qui lui est associée. Ce mode de projection est illustré dans la figure F.12.
TA TB
ID F1 ID F2
Projection filtrée
Avec une projection par filtrage, la classe fille et la classe mère sont stockées dans la
même table. La table est definie au niveau de la classe parente. La projection des champs
n’est faite qu’une seule fois. Pour distinguer les classes des instances, un filtre est necéssaire.
Speedo sait supporter des filtres evolués. Cependant, la majorité des utilisateurs utilise un
simple discriminant. Pour le discriminant trois cas sont possibles :
– Le discriminant est inclus dans l’identifiant de la classe. Cette stratégie est la plus
performante. En effet, une référence vers un objet d’une classe de l’arbre d’heritage
inclue le type exact de l’instance désignée. Ceci évite des requêtes eventuellement
complexes. Quand l’identifiant est constitué de champs persistants, le discriminant
est l’un d’entre eux. En revanche, l’identifiant est geré par Speedo, incluant l’iden-
tifiant de la classe. Voici un exemple où le champ id2 est le discriminant. La valeur
364 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
"discrim A" désigne les instances de la classe A, tandis que la valeur "discrim B"
désigne les instances de la classe B :
– Le discriminant n’est pas inclu dans l’identifiant de la classe. Cette solution est moins
performante. Voici un exemple :
– Le discriminant n’est pas un champ persistant mais une colonne de la table. Cette
colonne ne correspond pas à un champ persistant. Sa valeur est constante pour
une classe donnée. C’est la valeur de cette constante qui discrimine les classes des
instances. Cette stratégie est intéressante si on ne veut pas se préoccuper de la valeur
du discriminant en ayant un identifiant applicatif. Souvent la valeur du discriminant
est le nom de la classe persistante. Voici un exemple :
F.6. PROJECTION VERS UNE BASE DE DONN ÉES RELATIONNELLE 365
L’identifiant géré par Speedo inclut un discriminant. C’est le meilleur choix quand
l’identifiant n’est pas basée sur des valeurs applicatives.
Projection horizontale
Avec une stratégie de projection horizontale, chaque classe a sa propre table contenant
tous les champs herités. En effet, dans le descripteur de la classe fille, il faut redéfinir
la projection des champs hérités. Ils doivent être projetés sur des structures de la table
courante. Actuellement, Speedo supporte ce type de projection uniquement dans le cas où
l’identifiant contient un discriminant. C’est le cas de l’identifiant geré par Speedo. Voici
un exemple :
On notera qu’afin de distinguer les champs hérités des champs de la classe courante,
les noms des champs hérités sont préfixés par le nom de la classe.
Projection verticale
Avec la stratégie de projection verticale, chaque classe a sa propre table contenant
uniquement les champs de la classe courante. Cette table a en plus une jointure avec la
table de la classe mère. Actuellement Speedo supporte uniquement le cas où l’identifiant
inclut un discriminant. Ceci est le cas le plus performant. En effet, cela evite de faire des
jointures avec toutes les classes filles pour déterminer l’instance exacte. Voici un exemple :
366 ANNEXE F. SPEEDO : UN SYSTÈME DE GESTION DE LA PERSISTANCE
Annexe G
[Aalst et al. 2005] Aalst, W., Dumas, M., Ouyang, C., Rozinat, A., and Verbeek, H. (2005). Chore-
ography Conformance Checking: An Approach Based on BPEL and Petri Nets. BPMCenter
Report BPM-05-25, BPMcenter.org.
[Aalst et al. 2003] Aalst, W., van Dongen, B., Herbst, J., Maruster, L., Schimm, G., and Wei-
jters, A. (2003). Workflow Mining: A Survey of Issues and Approaches. Data and Knowledge
Engineering, 47(2):237–267.
[Agha 1986] Agha, G. A. (1986). Actors: A Model of Concurrent Computation in Distributed
Systems. In The MIT Press, ISBN 0-262-01092-5, Cambridge, MA.
[Aldrich et al. 2002] Aldrich, J., Chambers, C., and Notkin, D. (2002). ArchJava: Connecting
software architecture to implementation. In Proceedings of the 24th International Conference on
Software Engineering (ICSE’02), pages 187–197. ACM Press.
[Alexander 1964] Alexander, C. (1964). Notes on the Synthesis of Form. Harvard University Press.
[Alexander et al. 1977] Alexander, C., Ishikawa, S., and Silverstein, M. (1977). A Pattern Lan-
guage: Towns, Buildings, Construction. Oxford University Press. 1216 pp.
[Almes et al. 1985] Almes, G. T., Black, A. P., Lazowska, E. D., and Noe, J. D. (1985). The Eden
system: A technical review. IEEE Transactions on Software Engineering, SE-11(1):43–59.
[Alonso et al. 2003] Alonso, G., Casati, F., Kuno, H., and Machiraju, V. (2003). Web Services.
Concepts, Architectures and Applications. Springer Verlag.
[Altenhofen et al. 2005] Altenhofen, M., Boerger, E., and Lemcke, J. (2005). An execution se-
mantics for mediation patterns. In In Proceedings of the BPM’2005 Workshops: Workshop on
Choreography and Orchestration for Business Process Managament, Nancy, France.
[Alur et al. 2003] Alur, D., Malks, D., and Crupi, J. (2003). Core J2EE Patterns: Best Practices
and Design Strategies, Second Edition. Prentice Hall. 650 pp.
[Ancona et al. 2000] Ancona, D., Lagorio, G., and Zucca, E. (2000). A Smooth Extension of
Java with Mixins. In Proceedings of the European Conference on Object-Oriented Programming
(ECOOP’00), pages 154–178, Sophia Antipolis and Cannes, France.
[Andrews et al. 2003] Andrews, T., Curbera, F., Dholakia, H., Goland, Y., Klein, J., Leymann, F.,
Liu, K., Roller, D., Smith, D., Thatte, S., Trickovic, I., and Weerawarana, S. (2003). Business
Process Execution Language for Web Services, Version 1.1. Standards proposal by BEA Systems,
IBM Corporation and Microsoft Corporation.
[ANSA ] ANSA. Advanced networked systems architecture. http://www.ansa.co.uk/.
[Apache ] Apache. The Apache Foundation Software. http://www.apache.org.
373
374 BIBLIOGRAPHIE
[Arregui et al. 2000] Arregui, D., Pacull, F., and Rivière, M. (2000). Heterogeneous component
coordination: The CLF approach. In EDOC, pages 194–2003. 4th International Enterprise
Distributed Object Computing Conference (EDOC 2000), Makuhari, Japan, IEEE Computer
Society.
[ASM 2002] ASM (2002). ASM: a Java Byte-Code Manipulation Framework. The ObjectWeb
Consortium, http://www.objectweb.org/asm/.
[Avalon ] Avalon. The Apache Avalon Project. http://avalon.apache.org.
[Axis ] Axis. Projet Web Services - Apache - Axis. http://ws.apache.org/axis/.
[B. Stearns 2000a] B. Stearns (2000a). JavaBeans 101 Tutorial, Part I.
http://java.sun.com/developer/onlineTraining/Beans/bean01/index.html.
[B. Stearns 2000b] B. Stearns (2000b). JavaBeans 101 Tutorial, Part II.
http://java.sun.com/developer/onlineTraining/Beans/bean02/index.html.
[Baı̈na et al. 2004] Baı̈na, K., Benatallah, B., Casati, F., and Toumani, F. (2004). Model-Driven
Web Services Development. In Proceedings of the 16th International Conference on Advanced
Information Systems Engineering (CAISE’04), Riga, Latvia. Springer.
[Balter et al. 1991] Balter, R., Bernadat, J., Decouchant, D., Duda, A., Freyssinet, A., Krakowiak,
S., Meysembourg, M., Le Dot, P., Nguyen Van, H., Paire, E., Riveill, M., Roisin, C., Rousset de
Pina, X., Scioville, R., and Vandôme, G. (1991). Architecture and implementation of Guide, an
object-oriented distributed system. Computing Systems, 4(1):31–67.
[Banavar et al. 1999] Banavar, G., Chandra, T., Strom, R., and Sturman, D. (1999). A Case for
Message Oriented Middleware. In 13th Int. Symp. on Distributed Computing (DISC), LNCS
1693.
[Banâtre and Banâtre 1991] Banâtre, J.-P. and Banâtre, M., editors (1991). Les systèmes dis-
tribués : expérience du projet Gothic. InterÉditions.
[Baresi et al. 2004] Baresi, L., Ghezzi, C., and Guinea, S. (2004). Smart monitors for composed
services. In Proceedings of the 2nd Internation Conference on Service-Oriented Computing (IC-
SOC), pages 193–202, New York, NY, USA.
[Barros et al. 2005a] Barros, A., Dumas, M., and Hofstede, A. (2005a). Service interaction pat-
terns. In Proceedings of the 3rd International Conference on Business Process Management,
Nancy, France. Springer. Extended version available at: http://www.serviceinteraction.com.
[Barros et al. 2005b] Barros, A., Dumas, M., and Oaks, P. (2005b). A critical overview of the web
services choreography description language (ws-cdl). BPTrends Newsletter, 3(3).
[Baude et al. 2003] Baude, F., Caromel, D., and Morel, M. (2003). From distributed objects to
hierarchical grid components. In Proceedings of the International Symposium on Distributed
Objects and Applications (DOA’03).
[BCEL ] BCEL. Byte Code Engineering Library. http://jakarta.apache.org/bcel.
[Beehive ] Beehive. Projet Apache - Beehive. http://beehive.apache.org.
[Benatallah et al. 2005a] Benatallah, B., Casati, F., Grigori, D., H.R. Motahari-Nezhad, and
Toumani, F. (2005a). Developing Adapters for Web Services Integration. In Proceedings of
the 17th International Conference on Advanced Information System Engineering, CAiSE 2005,
Porto, Portugal, pages 415–429.
[Benatallah et al. 2005b] Benatallah, B., Dumas, M., and Sheng, Q.-Z. (2005b). Facilitating the
Rapid Development and Scalable Orchestration of Composite Web Services. Distributed and
Parallel Database, 17(1).
BIBLIOGRAPHIE 375
[Ben Albahari et al. 2002] Ben Albahari, Peter Drayton, and Brad Merrill (2002). C# Essentials.
O’Reilly. ISBN: 0-596-00315-3, 216 pages.
[Bertino et al. 2004] Bertino, E., Guerrini, G., and Mesiti, M. (2004). A matching algorithm for
measuring the structural similarity between an xml document and a dtd and its applications.
Information Systems, 29(1):23–46.
[Beugnard 2005] Beugnard, A. (2005). Contribution à l’étude de l’assemblage de logiciels. Habili-
tation à diriger des recherches, Université de Bretagne Sud.
[Beugnard 2006] Beugnard, A. (2006). Method overloading and overriding cause encapsulation
flaw. In Object-Oriented Programming Languages and Systems, 21st ACM Symposium on Applied
Computing (SAC), Dijon, France.
[Beugnard et al. 1999] Beugnard, A., Jézéquel, J.-M., Plouzeau, N., and Watkins, D. (1999). Mak-
ing Components Contract Aware. IEEE Computer, 32(7):38–45.
[Bieber and Carpenter 2002] Bieber, G. and Carpenter, J. (2002). Introduction to Service-Oriented
Programming. http://www.openwings.org.
[Birrell and Nelson 1984] Birrell, A. D. and Nelson, B. J. (1984). Implementing remote procedure
calls. ACM Transactions on Computer Systems, 2(1):39–59.
[Birrell et al. 1995] Birrell, A. D., Nelson, G., Owicki, S., and Wobber, E. (1995). Network objects.
Software–Practice and Experience, 25(S4):87–130.
[Blakeley et al. 1995] Blakeley, B., Harris, H., and Lewis, J. (1995). Messaging and Queueing
Using the MQI: Concepts and Analysis, Design and Development. McGraw-Hill.
[Blow et al. 2004] Blow, M., Goland, Y., Kloppmann, M., Leymann, F., Pfau, G., Roller, D., and
Rowley, M. (2004). BPELJ: BPEL for Java. White paper.
[Bouraqadi 2005] Bouraqadi, N. (2005). FracTalk: Fractal Components in Smalltalk.
csl.ensm-douai.fr/FracTalk.
[Bracha and Cook 1990] Bracha, G. and Cook, W. (1990). Mixin-based inheritance. In Proceed-
ings of the Conference on Object-Oriented Programming: Systems, Languages and Applications
(ECOOP/OOPSLA’90), volume 25 of SIGPLAN Notices, pages 303–311. ACM Press.
[Brinch Hansen 1978] Brinch Hansen, P. (1978). Distributed Processes: a concurrent programming
concept. Communications of the ACM, 21(11):934–941.
[Brownbridge et al. 1982] Brownbridge, D. R., Marshall, L. F., and Randell, B. (1982). The New-
castle Connection — or UNIXes of the World Unite! Software – Practice and Experience,
12(12):1147–1162.
[Bruneton 2001] Bruneton, É. (2001). Un support d’exécution pour l’adaptation des aspects non-
fonctionnels des applications réparties. PhD thesis, Institut National Polytechnique de Grenoble.
[Bruneton et al. 2003] Bruneton, E., Coupaye, T., and Stefani, J. (2003). The Frac-
tal Component Model. Technical report, Specification v2, ObjectWeb Consortium,
http://www.object.org/fractal.
[Bruneton et al. 2002] Bruneton, E., Lenglet, R., and Coupaye, T. (2002). ASM: A code manipu-
lation tool to implement adaptable systems. In Journées Composants 2002 (JC’02).
asm.objectweb.org/current/asm-eng.pdf.
[Burke 2003] Burke, B. (2003). It’s the aspects. Java’s Developer’s Journal.
www.sys-con.com/story/?storyid=38104&DE=1.
376 BIBLIOGRAPHIE
[Buschmann et al. 1995] Buschmann, F., Meunier, R., Rohnert, H., Sommerlad, P., and Stal, M.
(1995). Pattern-Oriented Software Architecture, Volume 1: A System of Patterns. John Wiley
& Sons. 467 pp.
[Cabrera et al. 2002a] Cabrera, F., Copeland, G., Cox, B., Freund, T., Klein, J., Storey, T.,
Langworthy, D., and Orchard, D. (2002a). Web Service Coordination, WS-Coordination.
http://www.ibm.com/developerworks/library/ws-coor/. IBM, Microsoft, BEA.
[Cabrera et al. 2002b] Cabrera, F., Copeland, G., Cox, B., Freund, T., Klein, J.,
Storey, T., and Thatte, S. (2002b). Web service transaction, ws-transaction.
http://www.ibm.com/developerworks/library/ws-transpec/. IBM, Microsoft, BEA.
[CACM 1993] CACM (1993). Communications of the ACM, special issue on concurrent object-
oriented programming. 36(9).
[Cahill et al. 1994] Cahill, V., Balter, R., Harris, N., and Rousset de Pina, X., editors (1994). The
COMANDOS Distributed Application Platform. ESPRIT Research Reports. Springer-Verlag.
312 pp.
[Campbell and Habermann 1974] Campbell, R. H. and Habermann, A. N. (1974). The specifica-
tion of process synchronization by path expressions. In Gelenbe, E. and Kaiser, C., editors,
Operating Systems, an International Symposium, volume 16 of LNCS, pages 89–102. Springer
Verlag.
[Carbon 2004] Carbon (2004). The Carbon Project. carbon.sourceforge.net.
[Carzaniga et al. 1998] Carzaniga, A., Fuggetta, A., Hall, R. S., van der Hoek, A., Heimbigner,
D., and Wolf., A. L. (1998). A characterization framework for software deployment technologies.
Technical Report CU-CS-857-98, Dept. of Computer Science, University of Colorado.
[Castor 1999] Castor (1999). The castor project.
http://www.castor.org/.
[Cervantes 2004] Cervantes, H. (2004). Vers un modèle à composants orienté services pour sup-
porter la disponibilité dynamique. PhD thesis, Université Joseph Fourier (Grenoble 1), Grenoble,
France.
[Cervantes and Hall 2004] Cervantes, H. and Hall, R. S. (2004). A framework for constructing
adaptive component-based applications: Concepts and experiences. In Crnkovic, I., Stafford,
J. A., Schmidt, H. W., and Wallnau, K. C., editors, CBSE, volume 3054 of Lecture Notes in
Computer Science, pages 130–137. Springer.
[Chakrabarti et al. 2002] Chakrabarti, A., de Alfaro, L., Henzinger, T. A., Jurdzinski, M., and
Mang, F. Y. (2002). Interface Compatibility Checking for Software Modules. In Proceedings
of the 14th International Conference on Computer-Aided Verification (CAV), volume 2404 of
LNCS, pages 428–441. Springer-Verlag.
[Chassande-Barrioz 2003] Chassande-Barrioz, S. (2003). The Speedo Project. ObjectWeb.
speedo.objectweb.org.
[Chassande-Barrioz and Dechamboux 2003] Chassande-Barrioz, S. and Dechamboux, P. (2003).
The Perseus Project. ObjectWeb. perseus.objectweb.org.
[Clarke et al. 2001] Clarke, M., Blair, G., Coulson, G., and Parlavantzas, N. (2001). An efficient
component model for the construction of adaptive middleware. In Proceedings of Middleware’01.
[Coulson et al. 2004] Coulson, G., Blair, G., Grace, P., Joolia, A., Lee, K., and Uyema, J. (2004).
A component model for building systems software. In Proceedings of the IASTED Software
Engineering and Applications (SEA’04).
BIBLIOGRAPHIE 377
[Dahl et al. 1970] Dahl, O.-J., Myhrhaug, B., and Nygaard, K. (1970). The SIMULA 67 common
base language. Technical Report S-22, Norwegian Computing Center, Oslo, Norway.
[Dasgupta et al. 1989] Dasgupta, P., Chen, R. C., Menon, S., Pearson, M. P., Ananthanarayanan,
R., Ramachandran, U., Ahamad, M., LeBlanc, R. J., Appelbe, W. F., Bernabéu-Aubán, J. M.,
Hutto, P. W., Khalidi, M. Y. A., and Wilkenloh, C. J. (1989). The design and implementation
of the Clouds distributed operating system. Computing Systems, 3(1):11–46.
[Dijkman and Dumas 2004] Dijkman, R. and Dumas, M. (2004). Service-oriented design: A multi-
viewpoint approach. International Journal of Cooperative Information Systems, 13(4):337–368.
[Dijkstra 1968] Dijkstra, E. W. (1968). The Structure of the THE Multiprogramming System.
Communications of the ACM, 11(5):341–346.
[Dillenseger 2004] Dillenseger, B. (2004). The CLIF Project. ObjectWeb. clif.objectweb.org.
[Donsez 2006] Donsez, D. (2006). Courtage et déploiement dynamiques de composants pour
l’infrastructure d’équipements UPnP. In UbiMob’06: 3emes Journées Francophones Mobilité
et Ubiquité, New York, NY, USA. ACM Press.
[Donsez et al. 2004] Donsez, D., Cervantes, H., and Désertot, M. (2004). FROGi : Déploiement
de composants fractal sur OSGi. In Conférence Francophone sur le Déploiement et la Reconfig-
uration, Grenoble, France, 28-29 Octobre 2004, pages 147–158.
[Dowling and Cahill 2001] Dowling, J. and Cahill, V. (2001). The K-Component architecture
meta-model for self-adaptative software. In Proceedings of Reflection’01, volume 2192 of Lecture
Notes in Computer Science, pages 81–88. Springer-Verlag.
[Dudney et al. 2003] Dudney, B., Asbury, S., Krozak, J. K., and Wittkopf, K. (2003). J2EE
AntiPatterns. Wiley. 600 pp.
[Dumant et al. 1998] Dumant, B., Horn, F., Tran, F. D., and Stefani, J.-B. (1998). Jonathan: an
Open Distributed Processing Environment in Java. In Davies, R., Raymond, K., and Seitz, J.,
editors, Proceedings of Middleware’98: IFIP International Conference on Distributed Systems
Platforms and Open Distributed Processing, pages 175–190, The Lake District, UK. Springer-
Verlag.
[Dumas et al. 2005] Dumas, M., Aalst, W., and Hofstede, A. (2005). Process-Aware Information
Systems: Bridging People and Software through Process Technology. John Wiley & Sons.
[Dustdar et al. 2004] Dustdar, S., Gombotz, R., and Baina, K. (2004). Web Services Interaction
Mining. Technical Report TUV-1841-2004-16, Information Systems Institute, Vienna University
of Technology, Wien, Austria.
[Désertot et al. 2006] Désertot, M., Donsez, D., and Lalanda, P. (2006). A dynamic service-
oriented implementation for java EE servers. In 2006 IEEE International Conference on Services
Computing (SCC 2006), New York, NY, USA. IEEE Computer Society.
[Edd Dumbill and M. Bornstein 2004] Edd Dumbill and M. Bornstein, N. (2004). Mono: A De-
veloper’s Notebook. O’Reilly. ISBN: 0-596-00792-2, 304 pages.
[Elmagarmid 1990] Elmagarmid, A. K., editor (1990). Database Transactions Models for Advanced
Applications. Morgan Kaufmann Publishers.
[Enhydra 2002] Enhydra (2002). Data object design studio.
http://www.enhydra.org/tech/dods/index.html.
[Escoffier and Donsez 2005] Escoffier, C. and Donsez, D. (2005). FractNet: An implementation of
the Fractal component model for .NET. In 2ème Journée Francophone sur Développement de
Logiciels par Aspects (JFDLPA’05). www-adele.imag.fr/fractnet/.
378 BIBLIOGRAPHIE
[Escoffier et al. 2006] Escoffier, C., Donsez, D., and Hall, R. S. (2006). Developing an osgi-like
service platform for .net. In IEEE Consumer Communications and Networking Conference
(CCNC’06) 2006.
[Fassino et al. 2002] Fassino, J.-P., Stefani, J.-B., Lawall, J., and Muller, G. (2002). Think: A soft-
ware framework for component-based operating system kernels. In Proceedings of the USENIX
Annual Technical Conference, pages 73–86.
[Fassino 2001] Fassino, J.-Ph. (2001). Think : vers une architecture de systèmes flexibles. PhD
thesis, École Nationale Supérieure des Télécommunications, Paris.
[Fauvet and Ait-Bachir 2006] Fauvet, M.-C. and Ait-Bachir, A. (2006). An automaton-based ap-
proach for web service mediation. In Workshop on Web Services. 13th International Conference
on Concurrent Engineering, Antibes, France. Invited paper.
[Fauvet et al. 2005] Fauvet, M.-C., Duarte, H., Dumas, M., and Benatallah, B. (2005). Handling
transactional properties in Web service composition. In Proceeding of Web Information Systems
Engineering (WISE), number 3806 in LNCS, New York City. Springer 2005, ISBN 3-540-30017-1.
[Fielding 2000] Fielding, R. (2000). Architectural Styles and the Design of Network-based Software
Architectures. PhD thesis, University of Califormia, Irvine, USA.
[Fleury and Reverbel 2003] Fleury, M. and Reverbel, F. (2003). The JBoss extensible server. In
Proceedings of the 4th ACM/IFIP/USENIX International Conference on Distributed Systems
Platforms and Open Distributed Processing (Middleware’03), volume 2672 of Lecture Notes in
Computer Science, pages 344–373. Springer-Verlag.
[Fox et al. 1998] Fox, A., Gribble, S. D., Chawathe, Y., and Brewer, E. A. (1998). Adapting
to network and client variation using infrastructural proxies: Lessons and perspectives. IEEE
Personal Communications, pages 10–19.
[Frénot and Stefan 2004] Frénot, S. and Stefan, D. (2004). Open-service-platform instrumentation:
JMX management over OSGi. In UbiMob’04: 1eres Journées Francophones Mobilité et Ubiquité,
pages 199–202, New York, NY, USA. ACM Press.
[Fu et al. 2004] Fu, X., Bultan, T., and Su, J. (2004). Analysis of interacting BPEL web services. In
Proceedings of the 13th International Conference on World Wide Web (WWW), pages 621–630,
New York, NY, USA.
[Gaaloul et al. 2004] Gaaloul, W., Bhiri, S., and Godart, C. (2004). Discovering Workflow Trans-
actional Behavior from Event-Based Log. In Proceedings of the OTM Confederated International
Conferences, CoopIS, DOA, and ODBASE 2004, pages 3–18.
[Gamma et al. 1994] Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1994). Design Patterns:
Elements of Reusable Object Oriented Software. Addison-Wesley. 416 pp.
[Goldfard 1990] Goldfard, C.-F. (1990). The SGML Handbook. Oxford Clarendon Press.
[Gong 2001] Gong, L. (2001). A Software Architecture for Open Service Gateways. IEEE Internet
Computing, 6:64–70.
[Gray and Reuter 1993] Gray, J. and Reuter, A. (1993). Transaction Processing: Concepts and
Techniques. Morgan Kaufmann.
[Grimes 1997] Grimes, R. (1997). Professional DCOM Programming. Wrox Press. 592 pp.
[Gruber et al. 2005] Gruber, O., Hargrave, B. J., McAffer, J., Rapicault, P., and Watson, T.
(2005). The Eclipse 3.0 platform: Adopting OSGi technology. IBM Systems Journal, 44(2):289–
300.
BIBLIOGRAPHIE 379
[Kavantzas et al. 2005] Kavantzas, N., Burdett, D., Ritzinger, G., Fletcher, T., Lafon, Y., and Bar-
reto, C. (2005). Web Services Choreography Description Language, Version 1.0. W3 CCandidate
Recommendation.
[Kiczales 1996] Kiczales, G. (1996). Aspect-Oriented Programming. ACM Computing Surveys,
28(4):154.
[Kiczales et al. 1991] Kiczales, G., des Rivières, J., and Bobrow, D. G. (1991). The Art of the
Metaobject Protocol. MIT Press. 345 pp.
[Kiczales et al. 2001] Kiczales, G., Hilsdale, E., Hugunin, J., Kersten, M., Palm, J., and Griswold,
W. G. (2001). An overview of AspectJ. In Proceedings of ECOOP 2001, volume 2072 of LNCS,
pages 327–355, Budapest, Hungary. Springer-Verlag.
[Kon et al. 2002] Kon, F., Costa, F., Blair, G., and Campbell, R. (2002). The case for reflective
middleware. Communications of the ACM, 45(6):33–38.
[Koutsonikola and Vakali 2004] Koutsonikola, V. and Vakali, A. (2004). LDAP: Framework, prac-
tices, and trends. IEEE Internet Computing, 8(5):66–72.
[Kramer 1998] Kramer, R. (1998). iContract - The Java Design by Contract Tool. In Proceedings of
the Technology of Object-Oriented Languages and Systems (TOOLS) Conference, pages 295–307.
[Layaida et al. 2004] Layaida, O., Atallah, S. B., and Hagimont, D. (2004). A framework for
dynamically configurable and reconfigurable network-based multimedia adaptations. Journal of
Internet Technology, 5(4):57–66. Special Issue on Real Time Media Delivery over the Internet.
[Lea 1999] Lea, D. (1999). Concurrent Programming in Java. The Java Series. Addison-Wesley,
2nd edition. 412 pp.
[Lea et al. 1993] Lea, R., Jacquemot, C., and Pillevesse, E. (1993). COOL: System Support for
Distributed Object-oriented Programming. Communications of the ACM (special issue, Con-
current Object-Oriented Programming, B. Meyer, editor), 36(9):37–47.
[Leclercq et al. 2005a] Leclercq, M., Quéma, V., and Stefani, J.-B. (2005a). DREAM : un canevas
logiciel à composants pour la construction d’intergiciels orientés messages dynamiquement con-
figurables. In 4ème Conférence Francophone autour des Composants Logiciels (avec CFSE-
RENPAR 2005), Le Croisic, France.
[Leclercq et al. 2005b] Leclercq, M., Quema, V., and Stefani, J.-B. (2005b). DREAM: a component
framework for the construction of resource-aware, configurable middleware. IEEE Distributed
Systems Online, 6(9).
[Lendenmann 1996] Lendenmann, R. (1996). Understanding OSF DCE 1.1 for AIX and OS/2.
Prentice Hall. 312 pp.
[Levin et al. 1975] Levin, R., Cohen, E. S., Corwin, W. M., Pollack, F. J., and Wulf, W. A.
(1975). Policy/mechanism separation in hydra. In Proceedings of the Fifth ACM Symposium on
Operating Systems Principles, pages 132–140.
[Liang and Bracha 1998] Liang, S. and Bracha, G. (1998). Dynamic class loading in the java virtual
machine. In Conference on Object Oriented Programming, Systems, Languages, and Applications
(OOPSLA’98).
[Limthanmaphon and Zhang 2004] Limthanmaphon, B. and Zhang, Y. (2004). Web service com-
position transaction management. In Schewe, K.-D. and Williams, H., editors, ADC, volume 27
of CRPIT. Database Technologies 2004, Proceedings of the Fifteenth Australian Database Con-
ference, ADC 2004, Dunedin, New Zealand, Australian Computer Society.
BIBLIOGRAPHIE 381
[Lindholm and Yellin 1996] Lindholm, T. and Yellin, F. (1996). The Java Virtual Machine Speci-
fication. Addison-Wesley. 475 pp.
[Liskov 1988] Liskov, B. (1988). Distributed programming in Argus. Communications of the ACM,
31(3):300–312.
[Maes 1987] Maes, P. (1987). Concepts and Experiments in Computational Reflection. In Proceed-
ings of the Conference on Object-Oriented Programming Systems, Languages, and Applications
(OOPSLA’87), pages 147–155, Orlando, Florida, USA.
[Martens 2005] Martens, A. (2005). Analyzing Web Service Based Business Processes. In Ceri-
oli, M., editor, Proceedings of the 8th International Conference on Fundamental Approaches to
Software Engineering (FASE 2005), pages 19–33. Springer.
[McIlroy 1968] McIlroy, M. (1968). Mass produced software components. In Naur, P. and Randell,
B., editors, Software Engineering: A Report On a Conference Sponsored by the NATO Science
Committee, pages 138–155, Garmisch, Germany.
[Merle et al. 2004] Merle, P., Moroy, J., Rouvoy, R., and Contreras, C. (2004). Fractal Explorer.
ObjectWeb.
fractal.objectweb.org/tutorials/explorer/index.html.
[Meyer 1992] Meyer, B. (1992). Applying Design by Contract. IEEE Computer, 25(10):40–52.
[Middleware 1998] Middleware (1998). IFIP International Conference on Distributed Systems Plat-
forms and Open Distributed Processing. September 15-18 1998, The Lake District, England.
[Mitchell et al. 1994] Mitchell, J. G., Gibbons, J., Hamilton, G., Kessler, P. B., Khalidi, Y. Y. A.,
Kougiouris, P., Madany, P., Nelson, M. N., Powell, M. L., and Radia, S. R. (1994). An overview
of the Spring system. In Proceedings of COMPCON, pages 122–131.
[Monson-Haefel 2002] Monson-Haefel, R. (2002). Enterprise JavaBeans. O’Reilly & Associates,
Inc., 3rd edition. 550 pp.
[msmq 2002] msmq (2002). Microsoft Message Queuing (MSMQ). Microsoft,
http://www.microsoft.com/msmq/.
[Mullender et al. 1990] Mullender, S. J., van Rossum, G., Tanenbaum, A. S., van Renesse, R.,
and van Staveren, H. (1990). Amoeba: A distributed operating system for the 1990s. IEEE
Computer, 23(5):44–53.
[Naur and Randell 1969] Naur, P. and Randell, B., editors (1969). Software Engineering: A Report
On a Conference Sponsored by the NATO Science Committee, 7-11 Oct. 1968. Scientific Affairs
Division, NATO. 231 pp.
[.NET ] .NET. Microsoft Corp. http://www.microsoft.com/net.
[OASIS UDDI 2005] OASIS UDDI (2005). Universal Description, Discovery and Integration ver-
sion 3.0. http://www.uddi.org.
[ObjectWeb 1999] ObjectWeb (1999). Open Source Middleware. http://www.objectweb.org.
[ObjectWeb 2004] ObjectWeb (2004). The Kilim Project. kilim.objectweb.org.
[Objectweb Enhydra 2005] Objectweb Enhydra (2005). Enhydra XMLC.
http://xmlc.objectweb.org/.
[ODMG ] ODMG. The Object Data Management Group. http://www.odmg.org.
[ODP 1995a] ODP (1995a). ITU-T & ISO/IEC, Recommendation X.902 & International Standard
10746-2: “ODP Reference Model: Foundations”.
http://archive.dstc.edu.au/AU/research news/odp/ref model/standards.html.
382 BIBLIOGRAPHIE
[ODP 1995b] ODP (1995b). ITU-T & ISO/IEC, Recommendation X.903 & International Standard
10746-3: “ODP Reference Model: Architecture”.
http://archive.dstc.edu.au/AU/research news/odp/ref model/standards.html.
[OMG ] OMG. The Object Management Group. http://www.omg.org.
[Open Group ] Open Group. http://www.opengroup.org/.
[Oracle 1994] Oracle (1994). Oracle toplink.
http://www.oracle.com/technology/products/ias/toplink/index.html.
[O’Sullivan et al. 2002] O’Sullivan, J., Edmond, D., and ter Hofstede, A. (2002). What’s in a
Service? Distributed and Parallel Databases, 12(2–3):117–133.
[Papazoglou 2003] Papazoglou, M. (2003). Web services and business transactions. Technical
Report 6, Infolab, Tilburg University, Netherlands.
[Parrington et al. 1995] Parrington, G. D., Shrivastava, S. K., Wheater, S. M., and Little, M. C.
(1995). The design and implementation of Arjuna. Computing Systems, 8(2):255–308.
[Pawlak et al. 2001] Pawlak, R., Duchien, L., Florin, G., and Seinturier, L. (2001). JAC : a flexible
solution for aspect oriented programming in Java. In Yonezawa, A. and Matsuoka, S., editors,
Proceedings of Reflection 2001, the Third International Conference on Metalevel Architectures
and Separation of Crosscutting Concerns, volume 2192 of LNCS, pages 1–24, Kyoto, Japan.
Springer-Verlag.
[Peltz 2003] Peltz, C. (2003). Web services orchestration and choreography. IEEE Computer,
36(10):46–52.
[PicoContainer 2004] PicoContainer (2004). The PicoContainer Project.
www.picocontainer.org.
[Platt 1999] Platt, D. S. (1999). Understanding COM+. Microsoft Press. 256 pp.
[Plexus 2004] Plexus (2004). The Plexus Project. plexus.codehaus.org.
[PLoP ] PLoP. The Pattern Languages of Programs (PLoP) Conference Series.
http://www.hillside.net/conferences/plop.htm.
[Quéma et al. 2004] Quéma, V., Balter, R., Bellissard, L., Féliot, D., Freyssinet, A., and Lacourte,
S. (2004). Scalagent : une plate-forme à composants pour applications asynchrones. Technique
et Science Informatiques, 23(2).
[Quéma 2005] Quéma, V. (2005). Vers l’exogiciel - une approche de la construction
d’infrastructures logicielles radicalement configurables. Thèse de Doctorat de l’Institut National
Polytechnique de Grenoble, décembre 2005.
[RM 2000] RM (2000). Workshop on Reflective Middleware. Held in conjunction with Middleware
2000, 7-8 April 2000. http://www.comp.lancs.ac.uk/computing/RM2000/.
[Rodriguez 2005] Rodriguez, E. (2005). The Apache Directory Server and the OSGi Service Plat-
form. OSGi World Congress, 12-15 October 2005, Paris, France.
[Rogerson 1997] Rogerson, D. (1997). Inside COM. Microsoft Press, Redmond, USA.
[Rouvoy 2004] Rouvoy, R. (2004). The GoTM Project. ObjectWeb. gotm.objectweb.org.
[Rozinat and Aalst 2005] Rozinat, A. and Aalst, W. (2005). Conformance Testing: Measuring the
Alignment Between Event Logs and Process Models. BETA Working Paper Series, WP 144,
Eindhoven University of Technology, Eindhoven.
BIBLIOGRAPHIE 383
[SCA 2005] SCA (2005). Service Component Architecture Assembly Model Specification.
www-128.ibm.com/developerworks/library/specification/ws-sca/.
[Schantz et al. 1986] Schantz, R., Thomas, R., and Bono, G. (1986). The architecture of the
Cronus distributed operating system. In Proceedings of the 6th International Conference on
Distributed Computing Systems, pages 250–259. IEEE.
[Schmidt et al. 2000] Schmidt, D. C., Stal, M., Rohnert, H., and Buschmann, F. (2000). Pattern-
Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects.
John Wiley & Sons. 666 pp.
[Seinturier et al. 2006] Seinturier, L., Pessemier, N., Duchien, L., and Coupaye, T. (2006). A com-
ponent model engineered with components and aspects. In Proceedings of the 9th International
SIGSOFT Symposium on Component-Based Software Engineering (CBSE’06), volume 4063 of
Lecture Notes in Computer Science, pages 139–153. Springer.
[Shapiro 1986] Shapiro, M. (1986). Structure and encapsulation in distributed systems: The proxy
principle. In Proc. of the 6th International Conference on Distributed Computing Systems, pages
198–204, Cambridge, Mass. (USA). IEEE.
[Shapiro et al. 1989] Shapiro, M., Gourhant, Y., Habert, S., Mosseri, L., Ruffin, M., and Valot, C.
(1989). SOS: An object-oriented operating system - assessment and perspectives. Computing
Systems, 2(4):287–337.
[Smith 1982] Smith, B. C. (1982). Reflection And Semantics In A Procedural Language. PhD
thesis, Massachusetts Institute of Technology. MIT/LCS/TR-272.
[Spring 2004] Spring (2004). The Spring Framework. www.springframework.org.
[Strom et al. 1998] Strom, R., Banavar, G., Chandra, T., Kaplan, M., Miller, K., Mukherjee, B.,
Sturman, D., and Ward, M. (1998). Gryphon: An Information Flow Based Approach to Message
Brokering. In Proceedings of ISSRE’98.
[Stutz et al. 2003] Stutz, D., Ted Neward, and Geoff Shilling (2003). Shared Source CLI Essentials.
O’Reilly. ISBN 0-596-00351-x, 378 pages.
[Sun 2005] Sun (2005). J2EE Application Validation Kit.
http://java.sun.com/j2ee/avk/.
[Sun JSR-000116 2003] Sun JSR-000116 (2003). SIP Servlet API.
http://jcp.org/aboutJava/communityprocess/final/jsr116/index.html.
[Sun JSR-000127 2004] Sun JSR-000127 (2004). JavaServer Faces Specification.
http://jcp.org/aboutJava/communityprocess/final/jsr127/index2.html.
[Sun JSR-000152 2003] Sun JSR-000152 (2003). JavaServer Pages 2.0 Specification.
http://jcp.org/aboutJava/communityprocess/final/jsr152/index.html.
[Sun JSR-000153 2003] Sun JSR-000153 (2003). Java Entreprise Bean 2.1 Specification.
http://www.jcp.org/aboutJava/communityprocess/final/jsr153/.
[Sun JSR-000154 2003] Sun JSR-000154 (2003). Java Servlet 2.4 Specification.
http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html.
[Sun JSR-000904 2000] Sun JSR-000904 (2000). JavaMail Specification.
http://jcp.org/aboutJava/communityprocess/maintenance/jsr904/index.html.
[Sun Microsystems 2003] Sun Microsystems (2003). Java Remote Method Invocation (RMI).
http://java.sun.com/products/jdk/rmi/.
384 BIBLIOGRAPHIE
[Suvée et al. 2005] Suvée, D., Vanderperren, W., and Jonckers, V. (2005). FuseJ: An architectural
description language for unifying aspects and components. In Workshop Software-engineering
Properties of Languages and Aspect Technologies (SPLAT) at AOSD’05.
ssel.vub.ac.be/Members/dsuvee/papers/splatsuvee2.pdf.
[Szyperski 2002] Szyperski, C. (2002). Component Software - Beyond Object-Oriented Program-
ming. Addison-Wesley. 2nd ed., 589 pp.
[Szyperski and Pfister 1997] Szyperski, C. and Pfister, C. (1997). Component-oriented program-
ming: WCOP’96 workshop report. In Mühlhäuser, M., editor, Workshop Reader of 10th Eur.
Conf. on Object Oriented Programming ECOOP’96, Linz, July 8–12, Special Issues in Object-
Oriented Programming, pages 127–130. Dpunkt, Heidelberg.
[Tatsubori et al. 2001] Tatsubori, M., Sasaki, T., Chiba, S., and Itano, K. (2001). A Bytecode
Translator for Distributed Execution of “Legacy” Java Software. In ECOOP 2001 – Object-
Oriented Programming, volume 2072 of LNCS, pages 236–255. Springer Verlag.
[Team 2003] Team, F. (2003). Fractal: a modular and extensible component model.
http://fractal.objectweb.org/.
[Team 1998] Team, J. (1998). Jorm: a framework for mapping typed objects to various storage
systems.
http://jorm.objectweb.org/.
[Team 2000] Team, M. (2000). Medor: a middleware framework for enabling distributed object
requests.
http://medor.objectweb.org/.
[The Apache Software Foundation 2005] The Apache Software Foundation (2005). The Struts
Framework.
http://struts.apache.org/.
[The Apache Software Foundation 2006] The Apache Software Foundation (2006). Apache Geron-
imo.
http://geronimo.apache.org/.
[The Objectweb Consortium 2000] The Objectweb Consortium (2000). JOnAS: Java (TM) Open
Application Server.
http://jonas.objectweb.org/.
[Tuscany 2006] Tuscany (2006). The Tuscany Project.
incubator.apache.org/projects/tuscany.html.
[UN/CEFACT and OASIS 1999] UN/CEFACT and OASIS (1999). Electronic Business XML.
http://www.ebxml.org.
[UPnP Forum 2005] UPnP Forum (2005). Universal Plug and Play Forum. www.upnp.org.
[van Renesse et al. 2003] van Renesse, R., Birman, K., and Vogels, W. (2003). Astrolabe: A robust
and scalable technology for distributed system monitoring, management, and data mining. ACM
Transactions on Computer Systems, 21(2).
[Vidyasankar and Vossen 2003] Vidyasankar, K. and Vossen, G. (2003). A multi-level model for
web service composition. Technical report, Dept. of Information Systems, University of Muen-
ster. Tech. Report No. 100.
[Völter et al. 2002] Völter, M., Schmid, A., and Wolff, E. (2002). Server Component Patterns.
John Wiley & Sons. 462 pp.
BIBLIOGRAPHIE 385
[W3C-DOM 2005] W3C-DOM (2005). The Document Object Model (DOM), W3C Consortium.
http://www.w3.org/DOM.
[W3C-WSA-Group 2004] W3C-WSA-Group (2004). W3C Web Service Architecture Group. Web
Services Architecture. http://www.w3.org/TR/ws-arch.
[W3C-WSD-Group ] W3C-WSD-Group. Web Services Description Working Group.
http://www.w3.org/2002/ws/desc/.
[W3C-XML ] W3C-XML. XML coordination group.
http://www.w3.org/XML/.
[W3C-XMLP-Group ] W3C-XMLP-Group. XML Protocol working group.
http://www.w3.org/2000/xp/Group/.
[W3C-XMLSchema ] W3C-XMLSchema. XML coordination group.
http://www.w3.org/XML/Schema.
[Waldo 1999] Waldo, J. (1999). The Jini architecture for network-centric computing. Communi-
cations of the ACM, 42(7):76–82.
[Waldo et al. 1997] Waldo, J., Wyant, G., Wollrath, A., and Kendall, S. (1997). A Note on Dis-
tributed Computing. In Vitek, J. and Tschudin, C., editors, Mobile Object Systems: Towards
the Programmable Internet, volume 1222 of LNCS, pages 49–64. Springer-Verlag.
[Weatherley and Gopal 2003] Weatherley, R. and Gopal, V. (2003). Design of the Portable.Net
Interpreter DotGNU. In Linux.conf.au, Perth (Australia), 22-25 January.
[Weerawarana et al. 2005] Weerawarana, S., Curbera, F., Leymann, F., Story, T., and Ferguson,
D. (2005). Web Services Platform Architecture: SOAP, WSDL, WS-Policy, WS-Addressing,
WS-BPEL, WS-Reliable-Messaging, and More. Prentice Hall.
[Weinand et al. 1988] Weinand, A., Gamma, E., and Marty, R. (1988). ET++ - An Object-
Oriented Application Framework in C++. In Proceedings of OOPSLA 1988, pages 46–57.
[Weiser 1993] Weiser, M. (1993). Some computer science issues in ubiquitous computing. Com-
munications of the ACM, 36(7):74–84.
[White 1976] White, J. E. (1976). A high-level framework for network-based resource sharing. In
National Computer Conference, pages 561–570.
[Winer 1999] Winer, D. (1999). XML-RPC specification. http://www.xmlrpc.com/spec.
[Wohed et al. 2003] Wohed, P., Aalst, W., Dumas, M., and Hofstede, A. (2003). Analysis of Web
Services Composition Languages: The Case of BPEL4WS. In 22nd International Conference on
Conceptual Modeling (ER 2003), pages 200–215. Springer.
[Wollrath et al. 1996] Wollrath, A., Riggs, R., and Waldo, J. (1996). A Distributed Object Model
for the Java System. Computing Systems, 9(4):265–290.
[Yellin and Strom 1997] Yellin, D. and Strom, R. (1997). Protocol specifications and component
adaptors. 19(2):292–333.