DÉVELOPPEUR RÉFÉRENCE Hors-série Java

publicité
Méthodes
Architectures
Technologies
Algorithmie
Langages
Outils
Opinions
LA LETTRE
BIMENSUELLE
DU DÉVELOPPEMENT
www.devreference.net
Hors-série Java | 19 septembre 2002
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
lité de rencontrer les acteurs du
monde Java, Scope 2002 s'annonce comme un rendez-vous incontournable de toute la communauté Java. Pour Développeur
Référence, co-producteur de cet
événement, c'est l'occasion de
faire le point sur cette technologie. A ce titre, nous avons inter-
[Suite page 3]
○
Actualité
Te c h n i q u e
○
○
○
○
○
○
○
○
S ommaire
Sc
ope 2002 ................................ 1
Scope
L'événement Java de la rentrée.
○
○
La vie et l'oeuvr
l'oeuvree des
ex
excceptions ................................. 15
.NET ou J2EE ? ........................... 2
Par Hervé Crespel
○
○
○
○
○
○
○
L' é d i t o
Applica
tions Ja
olutiv
es ... 11
Applications
Javva év
évolutiv
olutives
Te n d a n c e
Le fichier mappé ....................... 18
Managemen
Managementt à chaud
avec JMX .................................... 21
○
○
○
○
○
Pierre Tran
Rédacteur en chef
rogé les principaux acteurs de cet
événement : comment se situent-ils dans le paysage informatique ? Quelle stratégie comptent-ils développer dans les prochains mois autour de Java ?
Quels produits ou quelles
○
○
S
cope 2002, le pre
mier événement
français autour de
la technologie
Java, se tiendra le 3
et 4 octobre prochains au Palais
des Congrès de Versailles. Avec
deux conférences plénières, 40
ateliers techniques et la possibi-
○
○
D
epuis
un
an,
Développeur Réfé
rence, dans sa nouvelle formule électronique, a
couvert bon nombre de sujets :
architectures distribuées, méthodologies, conception objet,
techniques de programmation,
outillage... qu'ils soient dédiés
aux technologies Microsoft, Java
ou Open Source. Avec toujours
comme principal objectif la
qualité du contenu au service
des développeurs et comme
principal soucis l'objectivité.
Aujourd'hui, l'événement Scope
2002 auquel nous sommes associés, est l'occasion de vous offrir une compilation des
meilleurs articles consacrés à
Java, avec en prime quelques
contributions inédites grâce à la
participation de membres du
Club des Utilisateurs Java.
Retrouvez-nous le 3 et 4 octobre lors de ce rendez-vous incontournable de la communauté Java. Bonne lecture !
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
○
EDITO
J2EE 1.4 ...................................... 6
Java orienté services Web
Génér
er des Ja
eans
Générer
JavvaB
aBeans
avec XSL
XSLTT ................................... 24
LES PROJET
PROJETSS OPEN SOURCE
(1) EEta
ta
tatt de l'Ar
l'Artt .......................... 30
(2) SSer
er
vlet/JSP/MV
C
ervlet/JSP/MV
vlet/JSP/MVC
avec Tomca
omcatt ............................... 33
(3) MV
C2 aavvec Struts ................ 37
MVC2
(4) CCompr
ompr
endr
omprendr
endree
et ét
endr
étendr
endree Struts ....................... 42
(5) Mapping Objet Rela
tionnel
Relationnel
avec CCast
ast
or ................................ 46
astor
et
our d'expérienc
(6) EEclipse
clipse
etour
d'expériencee 51
clipse,, rret
Outil
Borland JBuilder 7 .................... 58
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
2
L' é d i t o
.NET ou J2EE ?
Par Hervé Crespel
D
epuis quelques mois, une nouvelle
litanie prend possession de la presse.
Les rédacteurs en chef les plus talentueux ne cessent reposer le problème du choix entre
.NET et J2EE. Les arguments ne manquent pas de
surprendre. L’un nous dit que J2EE est une spécifica-
En ces temps où la presse informatique subit une
tion tandis que .NET c’est du vrai logiciel. L’autre voit
en J2EE une gamme de logiciels qui bénéficient de
forte érosion (-20% en 2001, -15% en 2002), il serait
bon d’ouvrir les yeux. Les lecteurs cherchent l’effica-
plus de quatre ans de mise au point tandis que .NET
demandera au moins quatre ans pour être complet
cité et n’ont que faire de comparatifs inutiles et de
potins. Il est temps de prendre les utilisateurs pour
et éprouvé. Face à la multiplication d’opinions aussi
contradictoires, que faut-il donc penser ?
des gens responsables. Plus que jamais, ils ont besoin
d’être guidés pour naviguer dans la complexité. Plus
D’abord, une chose est sûre : de grands intérêts
sont en jeu ! Lesquels ? Ceux des fournisseurs
que jamais, ils veulent partager l’expérience des
autres. Et ils sont capables de faire eux-mêmes les
informatiques, bien sûr. Dont fait partie la presse
informatique, rappelons-le. Tandis que les utilisa-
comparaisons.
La complémentarité du Yin et du Yang sied mieux
teurs demandent plus que jamais FIABILITE et
SIMPLICITE, le marché ne cesse de leur expliquer
à l’informatique que la guerre ouverte entre deux
technologies. Les utilisateurs veulent une réponse
qu’il faut changer pour avoir mieux. Changer de
version, remplacer les logiciels, réformer les ordina-
adéquate à leurs besoins. Ils n’aiment pas les
cadavres, surtout pas dans leur SI. «Relatez l’expé-
teurs, reformer les hommes. Alors que l’expérience
montre que l’informatique qui marche le mieux,
rience de leurs pairs. Expliquez-leur comment
construire du solide avec .NET et J2EE. Dites-leur sur
c’est celle à laquelle on ne touche pas.
combien de modèles de téléphones fonctionnent
J2ME et Stinger. Dites-leur pourquoi Sony ou Nokia
La complémentarité du Yin et du Yang
sied mieux à l’informatique que la
guerre ouverte entre deux technologies.
Mais pourquoi donc opposer Java contre C# ?
JVM contre CLR ? J2EE contre .NET côtés client et
serveur des plates-formes e-business ? J2ME contre
Stinger sur les téléphones ? J2ME contre Pocket PC
sur les PDA ? J2SE contre Windows dans les IHM ?
Le marché de l’informatique n’est ni une compétition sportive ni une guerre mondiale. Entre
commerce et science, il a radicalement bouleversé
nos modes de travail et notre société. La masse de
ses acteurs se trouvent chez les utilisateurs, non
chez les fournisseurs.
utilisent Java. Centrez votre discours sur la nouvelle
clé du marché informatique : l’interopérabilité. Ditesleur que c’est la vraie raison d’être des Web services
et que le choix technologique n’est qu’une façon de
réussir une indispensable mutation. »
Cesser de nourrir les antagonismes. Informer
justement sur le monde Java pour construire
durable. C’est dans cet esprit que SCOPE 2002 a été
pensé. S’il reflète vos aspirations, ne manquez pas d’y
participer les 3 et 4 octobre prochains au palais des
congrès de Versailles. Faites-en un succès, manifestez
votre demande d’une information juste et efficace.
Merci et bonne lecture de ce numéro spécial.
z
Hervé Crespel
Président du Club des
Utilisateurs Java
(www.club-java.com)
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
3
Actualité
LETTRE BIMENSUELLE ÉDITÉE PAR
5, rue Chantecoq
92808 Puteaux Cedex
Tél. : 01 41 97 61 61
Adresse électronique :
[email protected]
Directeur de la publication : Ted Bloom
Commission paritaire : en cours
Dépôt légal : 4e trimestre 2001
Rédaction
Borland Enterprise Studio
Editeur
Michel Crestin
Rédacteur en chef
Pierre Tran • [email protected] • 6257
Assistantes de rédaction
Kateline Renaudin
Ont collaboré à ce numéro
Paul-Bernard Amade, Hervé Crespel,
Nicolas Dasriaux, Gil Francopoulo,
Didier Girard, David Le Bras,
Guillaume Louel, Jean-Noël Ribette,
François Maurit, Viktor Okunev,
Philippe Prados, Alvaro Schwarzberg
Fabrication
Directeur artistique groupe
Patrice Servage
Mise en page
Pierre Tran
Photographies
Marc Guillaumot
Iconographie
Nouara Aftis
Infographies
Pierre Tran
Publicité
Pasquale Meyer • [email protected] • 6216
Service Abonnements
Développeur Référence
BP 90006 – 59718 Lille Cedex 9
Tél. 03 20 12 11 17
Fax 03 20 12 86 09
Tarif :
1 an 22 numéros : 200 euros
Prix de lancement : 150 euros
Renseignements :
Lucienne Bosser • [email protected] • 6128
Cop
yrigh
ommunica
tions FFrr anc
opyrigh
yrightt IDG CCommunica
ommunications
ancee .
Toute reproduction ou représentation, intégrale
ou partielle, par quelque procédé que ce soit, des
pages publiées dans la présente publication faite
sans l’autorisation écrite de l’éditeur est illicite et
constitue une contrefaçon. Seules sont autorisées,
d’une part, les reproductions strictement réservées à l’usage privé du copiste et non destinées à
une utilisation collective, d’autre part, les analyses et courtes citations justifiées par le caractère
scientifique ou d’information de l’œuvre dans laquelle elles sont incorporées (loi du 11 mars 1957
- art. 40 et 41 et Code pénal art. 425). Toutefois,
des photocopies peuvent être réalisées avec
l’autorisation de l’éditeur. Celle-ci pourra être obtenue auprès du Centre français du copyright, 6
bis, rue Gabriel-Laumain, 75010 Paris, auquel IDG
Communications France a donné mandat pour le
représenter auprès des utilisateurs.
[suite de la page 1]
technologies prévoient-ils de
présenter ou d'annoncer lors de
Scope 2002 ?
Pour Bruno de Combiens, responsable produits de Borland
France, Borland Software Corporation se situe comme l'un des
principaux fournisseurs de technologies dédiées au développement, au déploiement et à l'intégration d'applications logicielles.
L'éditeur dispose d'une base installée et de partenariats stratégiques qui le positionnant comme
un acteur leader dans de nombreux domaines dont celui des
solutions de développement
Java avec JBuilder et Borland
Enterprise Studio for Java… La
stratégie de Borland consiste en
la couverture complète du cycle
de vie des applications (de la conception au déploiement en passant par le test de performance),
car elle contribue à abaisser le
coût total de possession des solutions Java, quel qu'en soit
l'usage : de J2ME à J2EE. A l'occasion de Scope 2002, Borland présentera principalement sa solution couvrant les phases fondamentales du cycle de développement d'applications mobiles et
J2EE : Borland Enterprise Studio
for Java.
Pour Rémy Baranger, IBM
WebSphere Market Manager, la
priorité est aux standards
ouverts. Pour les entreprises, faire
face aux nouveaux défis économiques signifie automatiser toujours davantage leurs processus
métier, et donc unifier leur patri-
moine applicatif. Cette unification n'est possible que grâce aux
standards ouverts. C'est pourquoi IBM apporte sa contribution
à Linux, Apache, J2EE, XML, etc.
et est devenu un des leaders dans
le domaine des standards. Un effort qu'il poursuit aujourd'hui en
contribuant à l'évolution des Web
services, ou encore en proposant
un standard pour les outils de
développement au travers du
projet Eclipse. Scope2002 est l'occasion pour IBM de souligner son
engagement auprès des communautés formées autour des technologies ouvertes, et de faire la
démonstration de son infrastructure logicielle WebSphere, fondée sur Java et les standards de
l'industrie.
Ivstudio de ILOG
ILOG propose des composants Java qui permettent aux
entreprises de réduire considérablement le temps dédié au développement tout en leur offrant
portabilité et personnalisation
avancées. Selon Sandrine
Lebouc, EMEA Corporate
Marcom Manager, l'éditeur français a annoncé sa volonté de renforcer son support des services
Web sur l'ensemble de son offre
produits au cours de l'année
2002. JRules, JConfigurator et
JViews, peuvent déjà être intégrés aux services Web sur les
principales plates-formes de serveurs d'application J2EE. Scope
2002 sera l'occasion de présenter
les suites de composants de visualisation, de règles métier et
d'optimisation 100% Java et de
lancer la dernière version du moteur de configuration interactive
JConfigurator 2.0. De plus, ILOG
animera également deux conférences.
Pour Oracle, l'offre comprend
les progiciels de gestion Oracle EBusiness Suite, le serveur
applicatif Oracle9i Application
Server, le SGBDR Oracle9i
Database, les outils de développement Oracle9i Developer Suite
et les outils bureautiques Oracle
Collaboration Suite. Pour Laurent
de Lavarène, Responsable Marketing Oracle9i, " Avec plus de 1000
développeurs Java sur Oracle EBusiness Suite, Oracle est le plus
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
4
Ac tualité
D'autre part, les produits de l'offre Sun ONE suivront les mêmes
évolutions dans les prochains
mois. Lors de Scope 2002, Sun
montrera les nouveaux services
sur les téléphones Java (Siemens,
Nokia ou Sony/Ericsson) et l'intégration à la plate-forme Java
d'applications Peer-to-Peer ou
Grid Engine. Les nouvelles versions des environnements de
développement Sun ONE Studio
seront également de la partie.
Rational XDE pour Java
important éditeur de progiciels
Java du marché ". Utilisateur convaincu, l'éditeur souhaite aussi
contribuer au développement de
la plate-forme J2EE dans les entreprises. A l'occasion de Scope
2002, Oracle y donnera quatre
conférences sur les thèmes suivants : Mapping objet-relationnel, Développement rapide d'applications J2EE, Développement
rapide de services Web, Mise en
cache de pages Web.
Pour Anne Chaurand, responsable communication Rational,
Java est maintenant largement
acceptée comme plate-forme qui
dépasse le cadre du langage de
programmation. La stratégie de
l'éditeur consiste à accompagner
ses clients qui ont fait le choix Java
pour transformer ce choix en succès grâce à une plate-forme de
développement intégrée qui couvre tout le cycle de vie de développement et de déploiement
Java ainsi que des pratiques de
développement. Rational présentera lors de Scope 2002 cette
plate-forme avec notamment
Rational XDE, qui offre aux développeurs la conception, la modélisation et le codage à l'intérieur de
l'IDE, Rational Rose (modélisation
UML), Rational Suite TestStudio
(test), Rational ClearCase (configuration logicielle), Rational
Unified Process (RUP, processus
standards)…
Présent dans plus de soixante
pays, Software AG, éditeur européen de logiciels d'infrastructure,
propose un ensemble d'offres
technologiques couvrant l'intégration (EAI) et les Web Services,
les systèmes de gestion de bases
de données (XML et non XML),
ainsi que des solutions dédiées à
la gestion de contenu qui s'appuient toutes sur Tamino. Selon
Xavier Sauvan, responsable produit Software AG France, ce serveur de documents XML natif
confère, outre robustesse et flexibilité, une grande capacité de
stockage d'informations de toute
nature (XML, vidéos, sons…) et
des possibilités évoluées d'indexation et de recherches plein
texte. Par ailleurs, le serveur
Tamino possède une API Java/EJB
et s'interface à de nombreux
outils de développement Java
afin de faciliter l'utilisation de
XML par les développeurs Java.
Sun, qui vient de fêter ses 20
ans, est l'inventeur de la plateforme Java qui est sortie de ses
laboratoires il y a sept ans. C'est
une communauté Java de plus de
trois millions de développeurs
qui utilisent Java et plus de 500
sociétés au sein du Java
Community Process qui collabo-
rent aux futures évolutions. Pour
Eric Mahé, responsable marketing Java, la stratégie de Sun couvre d'une part les évolutions globales de Java : la maturité de
J2ME dans la téléphonie mobile
(specs MID-P pour les téléphones
Java), l'intégration des standards
des Web Services et les évolutions de l'architecture J2EE (montée en charge, administration).
Enfin Sybase est éditeur d'infrastructure, son offre résolument
non propriétaire permet d'intégrer tout type de technologie
tout en préservant les investissements existants. Selon Isabelle
Genestoux, directrice marketing,
Java est au coeur de l'offre et se
retrouve au niveau de sa couche
d'infrastructure (serveur d'applications certifié J2EE, base de données, modélisation), d'intégration (support des Web services)
et d'accès (portail et mobilité).
L'engagement de Sybase envers
Java sera renforcé notamment à
travers le support d'Eclipse. La
stratégie est de proposer des briques d'infrastructure totalement
portables et ouvertes. L'éditeur
présentera lors de Scope 2002
Enterprise Portal 5.0, portail d'entreprise indépendant du serveur
d'applications, pour agréger et
syndiquer des applications et du
contenu en quelques clics.
z
Sybase Enterprise Portal
Parasoft Jtest® automatise
entièrement le test JavaTM
Parasoft est le fournisseur principal de solutions
automatiques de prévention et de détection d'erreurs
pour les développeurs Java.
Fini le temps des longues heures de débuggage minutieux
qui vous prenait une bonne partie de votre énergie. Lancez
Jtest de Parasoft, et pour avoir la garantie d ’un code robuste
et sans erreurs, vous n ’avez plus qu ’à regarder le travail se
faire.
Jtest de Parasoft automatise entièrement le test Java.
Vous n’aurez plus jamais à écrire un seul cas de test. Pour la
première fois , des outils de prévention d'erreurs puissants –
comme le test unitaire, l'analyse statique et le Design by
Contract – peuvent être facilement déployés au cours de
votre processus de développement.
L'Exécution Symbolique Dynamique unique de Jtest produit
automatiquement des cas de test boîte blanche et boîte
noire réutilisables ,vous permettant d ’évaluer facilement vos
classes et vos composants Java, ainsi que vos JSP,
au cours du processus de développement . Le test de
régression de Jtest maintient l'intégrité du code et son
analyse statique prévient les erreurs, et pointe le code
prédisposé aux erreurs en appliquant un grand éventail de
standards de programmation. Rulewizard, l ’éditeur de règles
intégré,, permet d ’implémenter facilement vos propres règles
dans la revue de code automatique.
Jtest est disponible pour une large variété de plates-formes,
y compris Windows NT /2000 /XP, RedHat Linux 6.1,7.1,ou 7.2,
SuSE Linux 7.2, ou Mandrake Linux 8.1 et Solaris 7 et 8.
Téléchargez une copie d'évaluation de Jtest aujourd'hui sur
http://www.parasoft.com
Solutions automatiques de
prévention d’erreurs Parasoft.
Faites le bien dès le début
Parasoft écrit régulièrement des documents techniques
afin de vous aider dans le développement de vos applications.
Une copie de notre dernier document technique,
“Automating and Improving Java Unit Testing: Using Jtest with Junit” ,
vous sera remis sur présentation de cette page au stand Parasoft
lors de Scope 2002 , les 3 et 4 octobre 2002.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
6
Te n d a n c e
LAA VERSION
VERSION 1.3 DE J2EE A SOUVENT ÉTÉ CRITIQUÉE POUR SON ABSENCE DE SUPPORT
SUPPORT DES SERVICES WEB ET POUR LES LIMITATIONS
DES CONNECTEURS JCA. ACTUELLEMENT ÀÀ L’ÉTAT DE
DE PROPOSED FINAL DRAFT, LA VERSION 1.4 EST PRÉVUE
PRÉVUE POUR DÉBUT 2003.
CET ARTICLE PRÉSENTE LES NOUVEAUTÉS
NOUVEAUTÉS DE LA PLATE-FORME
FORME AINSI QUE LES AMÉLIORATIONS APPORTÉES
APPORTÉES.
T
David Le Bras
est consultant et
expert de la plateforme Java et des
serveurs d’application
J2EE chez Neoxia.
Lors de missions de
conseil ou de
coaching, il
accompagne les
équipes projet pour
les aider à tirer le
meilleur parti des
technologies Java.
[email protected]
Figure 1 : Architecture
de la plate-forme J2EE
rès attendue, la
nouveauté principale de la plateforme J2EE 1.4 est
le support des services Web. Dorénavant, la plateforme comprend une boîte à
outils complète permettant de
développer et de déployer des
services Web. Grâce à cet ajout,
J2EE 1.4 devient enfin une plateforme crédible pour la mise en
œuvre de services Web.
J2EE 1.4 comprend également de nouvelles API permettant la supervision du serveur
d'applications, et le déploiement
d'applications sur le serveur d'applications. Ces API permettent
aux outils de s'interfacer avec les
serveurs d'application de façon
standard. Autre nouveauté, des
améliorations ont été apportées
aux API d'intégration avec les systèmes d'information externes
(J2EE Connector Architecture
1.5), et de présentation (Java
Server Pages 2.0). L'installation et
la configuration de systèmes externes d'authentification et
d'autorisation sont également
rendues possibles dans cette
nouvelle version de J2EE.
Intégration des
services Web
La grande nouveauté de J2EE
1.4 est un ensemble d'interfaces
de programmation d'applications (API) permettant de développer des services Web :
• JAXP permet d'interpréter et
de transformer la structure
d'un document XML,
• JAXR permet d'accéder à des
annuaires ebXML et UDDI,
• JAX-RPC permet de mettre en
œuvre et d'utiliser des services
Web.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
7
Te n d a n c e
La spécification Java API for
XML Processing 1.2 (JAXP 1.2) définit les API permettant de lire, de
manipuler et de générer des documents XML. Cette spécification
est conforme à la spécification
XML 1.0, et aux espaces de
nommage
XML
(XML
Namespace). JAXP propose une
manière standard d'intégrer un
parseur XML dans une application Java, que ce soit un parseur
SAX 2 (Simple API for XML), un
parseur DOM 2 (Document
Object Model) ou bien encore un
transformateur XSLT (eXtensible
Style Language Transformation).
Autre nouveauté importante,
cette nouvelle version de JAXP
supporte enfin les schémas XML
(XML Schema).
Quant à elle, la spécification
Java API for XML-based RPC (JAXRPC) définit les API permettant
de développer et d'utiliser des
services Web. JAX-RPC est conforme à la spécification 1.1 du
protocole SOAP, et permet donc
l'utilisation de XML pour l'invocation de méthodes à travers le réseau. La spécification SOAP with
Attachments API for Java 1.1
(SAAJ 1.1) complète JAX-RPC, en
lui ajoutant le support des pièces
jointes dans les messages SOAP.
La spécification Web Services for
J2EE formalise le déploiement de
services et clients reposant sur
JAX-RPC.
Java API for XML Registries
(JAXR) propose une interface
standardisée d'accès aux annuaires employés dans le contexte
des services Web : ebXML
(Electronic Business using
eXtensible Markup Language) et
UDDI (Universal Description,
Discovery and Integration).
On peut regretter l'absence
de Java API for XML Binding
(JAXB), disponible séparément
de la plate-forme. Cette API permet de mettre en correspondance des classes Java et avec un
document XML (mapping objet
/ XML). Un autre absent est Java
API for XML Messaging 1.1 (JAXM
1.1), uniquement disponible
dans le pack XML. Cette API s'appuie sur SOAP 1.1 et permet aux
applications d'envoyer et de recevoir des messages XML orientés document.
Intégration avec les
outils de supervision
et de déploiement
Dans un environnement de
production, il est nécessaire de
pouvoir surveiller le comportement des divers constituants du
système d'information, mais également, de pouvoir les
paramétrer à chaud. Ceci est
d'autant plus nécessaire, lorsqu'il
s'agit de serveurs dont la disponibilité et le bon fonctionnement
sont cruciaux pour l'entreprise.
Généralement, cette fonction est
assurée par un outil de supervision du type IBM Tivoli, HP Open
VM, CA Unicenter, ou encore BMC
Patrol. Dans la plupart des serveurs d'application, la supervision et la configuration sont spécifiques : rares sont les serveurs
d'application proposant une interface acceptée par tous les
outils du marché.
En réaction, la plate-forme
J2EE 1.4 propose désormais une
interface permettant à des outils
de superviser et de configurer un
serveur d'applications, ainsi que
les applications déployées sur ce
dernier. Pour cela, les composants s'appuient sur JMX (Java
Management Extensions 1.1)
pour être administrables via de
multiples protocoles (HTTP, RMI,
SNMP).
L'administration et la supervision des applications J2EE s'intègre désormais aux principaux
outils de supervision du marché
grâce à SNMP (Simple Network
Management Protocol). Une version HTTP est fournie par Sun. Il
est donc possible d'intégrer la
surveillance des applications Java
dans une infrastructure globale
de supervision.
JMX définit un standard pour
écrire des objets JMX appelés
Mbeans. Ces objets Mbeans existent à l'intérieur d'un conteneur
défini par le standard. Un client
JMX est capable d'invoquer les
méthodes, et de lire ou de modifier les attributs de ces composants. Un client JMX peut également s'enregistrer auprès d'un
Mbean pour recevoir des événements.
Dans J2EE 1.3, le déploiement
des applications est spécifique
du serveur d'applications cible.
Pour déployer une application, il
est nécessaire d'utiliser l'outil
fourni par le serveur, ou par l'environnement de développement,
ou bien encore d'écrire un script
de déploiement spécifique. Dans
J2EE 1.4, le déploiement des applications est désormais standardisé grâce à la spécification J2EE
Deployment API 1.1. Non seulement, les fournisseurs de serveurs doivent fournir un outil de
déploiement, mais ils doivent
aussi respecter le standard de
déploiement. Ainsi, tout outil respectant le standard peut déployer des applications sur tout
serveur d'application conforme
au standard. Cette nouvelle API
s'adresse principalement aux éditeurs d'outils de développement,
mais aussi aux équipes responsables de la mise en production des
applications. À titre d'illustration,
Sun propose déjà des implémentations de référence de ces outils
qui s'intègrent à l'environnement
de développement Forte for Java.
Intégration avec le
système d'information
et de sécurité
Avant la spécification JCA
(J2EE Connector Architecture), il
n'existait pas de moyen standard
pour intégrer la plate-forme Java
avec les autres applications et
middlewares existant dans l'entreprise : applications spécifiques, ERP, CRM, middlewares divers. Par exemple, l'intégration
avec CICS, Tuxedo, SAP, Siebel ou
Peoplesoft ne pouvait se faire
que grâce à des solutions propriétaires.
Présent depuis J2EE 1.3, JCA
propose une interface de communication du serveur d'applications vers le système externe.
Toutefois, la communication du
système externe vers le serveur
d'applications a rapidement fait
défaut. De réelles améliorations
ont donc été apportées aux API
d'intégration dans la nouvelle
mouture 1.5 de JCA. Principal
apport, la communication asynchrone bidirectionnelle est désormais possible.
Les communications entrantes sont redirigées vers un Message Driven Bean (MDB). Il est
possible de définir des types de
message différents. Par exemple,
dans le cas d'une application
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
8
Te n d a n c e
d'envoi de message d'alertes
sous forme de mails, il est envisageable de définir un connecteur
qui envoie ce type de message.
En matière d'intégration,
cette possibilité de communication peut apparaître comme un
système concurrent des services Web. En fait, il s'agit plutôt
de deux technologies complémentaires. On peut ainsi développer un service Web permettant d'obtenir le solde d'un
compte bancaire. À son tour, le
service peut déléguer l'implémentation à un mainframe via
un connecteur JCA.
Depuis J2EE 1.3, le modèle de
sécurité s'appuie sur Java
Authentication
and
Authorization Service (JAAS), défini dans J2SE. La nouveauté de
J2EE 1.4 est la spécification Java
Authorization Service Provider
Contract for Containers. Elle permet l'intégration d'une infrastructure d'autorisation existante
dans le serveur d'application.
Ainsi, il n'est plus nécessaire de
créer une couche supplémen-
taire au niveau du serveur d'application, lorsqu'un outil fédérateur des autorisations existe dans
l'entreprise.
Conteneur Web
La couche présentation des
applications J2EE bénéficie d'une
nouvelle version de la spécification Java Server Pages 2.0 (JSP).
JSP 2.0 introduit la notion de fragment de pages réutilisable. JSP
2.0 comprend également un
nouveau langage d'expression.
Ce langage remplace avantageusement les expressions Java dans
les pages JSP. Le listing 1 et le listing 2 donnent un aperçu de ces
évolutions.
Malheureusement, la spécification Java Server Pages Standard Tag Library (JSTL) ne fait pas
partie de la plate-forme. JSTL propose un ensemble de balises
standard pour les pages JSP (voir
listing 3). Les fonctionnalités
communes sont fournies par des
balises standard : conditions et
itérations, internationalisation,
requêtes SQL et manipulations
Listing 1 : exemple JSP 1.2
<jsp:useBean id="produit" type="com.masociete.Product" scope="request"/>
[…]
Nom du produit: <%= produit.getNom() %>
[…]
<% if (produit.getFabricant().equals("SuperDiscount")) { %>
[…]
<% } %>
Listing 2 : exemple JSP 2.0
Nom du produit: ${produit.nom}
[…]
<c:if test="${produit.fabricant == param.fabricant}">
[…]
</c:if>
Listing 3 : exemple JSTL
[…]
<table>
<jstl:foreach
xmlns:c="http://java.sun.com/jstl/core"
var="compteur" begin="1" end="3">
<row>${compteur}</row>
</jstl:foreach>
</table>
Listing 4 : interface du service
[…]
public interface StockPriceProvider extends javax.rmi.Remote {
[…]
public float getStockPrice(String code) throws javax.rmi.RemoteException;
[…]
}
Listing 5 : implémentation du service sous forme d'un Stateless Session Bean
[…]
public class StockPriceProviderWS implements StockPriceProvider , javax.ejb.SessionBean {
[…]
public float getStockPrice(String code) {
// récupération des données de la base
[…]
}
}
XML. Il n'est plus nécessaire d'utiliser des librairies du marché ou
d'insérer du code Java. La librairie JSTL est néanmoins disponible séparément.
On peut également regretter
l'absence de la spécification Java
Server Faces, qui définit un ensemble de composants Web gérés côté serveur et facilement
manipulables par des environnements de développement. J2EE
1.4 manque ainsi une occasion de
donner le change à ASP.NET, l'un
des points forts de .NET.
Enterprise Java beans
Dans la plate-forme J2EE 1.3,
la spécification Enterprise Java
Beans 2.0 (EJB) améliore grandement la persistance des entités
gérées par le conteneur (Container Managed Persistence ou
CMP). À l'occasion, le langage
EJB-QL a été introduit pour définir des requêtes indépendamment de la base de données. La
nouvelle mouture 2.1 des EJB
apporte des améliorations au langage EJB-QL : clause ORDER BY,
opérateurs d'agrégation (AVG,
COUNT, MAX, MIN, SUM). Ces lacunes avaient été comblées par
les principaux serveurs du marché, qui proposaient une version
propriétaire de EJB-QL pour les
EJB 2.0.
Autre nouveauté, les composants ont maintenant la possibilité de s'enregistrer auprès d'un
service de planification de tâches
(Timer). Par exemple, un composant de facturation peut s'enregistrer auprès du service pour
déclencher l'envoi d'un message
au client à une date donnée. De
la même manière, il est également possible de développer un
composant qui nettoie la base de
données quotidiennement ou
déclenche un mécanisme de sauvegarde chaque semaine.
Au cœur de la plate-forme,
les services Web sont maintenant
intégrés aux composants EJB.
Ainsi, les Stateless Session Beans
peuvent être utilisés pour implémenter un service Web. La première étape est de définir l'interface du service (voir listing 4).
Ensuite, l'implémentation du
service doit être définie, sous
forme d'un Stateless Session
Bean (voir listing 5)
Grâce à un utilitaire de JAX-
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Analyse
9
Te n d a n c e
Listing 6 : exemple d'appel d'un service Web à partir d'un composant EJB
[…]
public class StockBuyer implements javax.ejb.SessionBean {
[…]
public String buyStock(String code, int number) {
[…]
// récupération de l'instance du service via JNDI
InitialContext jndiContext = new InitialContext( );
StockPriceProviderService service =
jndiContext.lookup("java:comp/env/StockPriceProviderService");
// récupération stub
StockPriceProvider stockPriceProvider = service.getStockPriceProvider();
// appel de méthode distante
float price = stockPriceProvider.getStockPrice( code );
[…]
}
}
RPC, il est ensuite possible de
générer le stub client et un document WSDL, le tout, à partir de
l'interface du service. Le stub permet d'accéder au Stateless Session Bean via le protocole SOAP.
Quant au document WSDL, il
peut servir à d'autres types de
clients, comme par exemple un
client .NET.
Les composants EJB peuvent
être clients de services Web grâce
à JAX-RPC. Pour cela, ils font appel à l'environnement JNDI pour
récupérer une référence sur le
service Web. Ensuite, l'appel au
service peut être réalisé, au choix,
via un stub généré statiquement,
via à un proxy dynamique ou
bien encore, grâce à un mécanisme d'invocation dynamique
(Dynamic Invocation Interface).
Le listing 6 montre un composant
EJB accédant à un service Web
grâce au stub généré.
La plate-forme J2EE 1.4 comporte de nombreuses nouveautés. La plus attendue d'entre elles est, sans doute, la prise en
compte des services Web dans la
plate-forme. De ce point de vue,
les choix du JCP (Java
Community Process) semblent
raisonnables. Les services Web
s'appuient principalement sur les
Stateless Session Beans, reconnus pour leur performance et leur
capacité à monter en charge. En
outre, l'intégration avec la plateforme est simple et cohérente.
Avec la version 1.4, J2EE prend
véritablement l'existant en considération. Ainsi, J2EE 1.4 spécifie, et donc standardise, un certain nombre de services essentiels en production, comme : la
supervision, l'administration et le
déploiement des applications.
Avec la nouvelle version de JCA,
et les services Web, J2EE 1.4 propose de véritables moyens pour
interopérer avec le patrimoine
applicatif de l'entreprise. En bien
des aspects, J2EE 1.4 propose des
fonctionnalités majeures, et l'on
est impatient de pouvoir juger
des implémentations de ces spécifications. C'est maintenant au
tour des principaux éditeurs du
marché de sortir une nouvelle
mouture de leur produit…
z
Crystal Reports® V9 est arrivé !
Vous aimeriez avoir plus de loisirs ? Avec le nouveau
Crystal Reports V9, vous allez gagner du temps. La toute
dernière version de ce grand classique reconnu par toute
l'industrie possède plus de 50 nouvelles fonctionnalités et
mises à jour. Elle n'a jamais été aussi rapide ni aussi
puissante et elle vous permet de créer et d'intégrer le
contenu de rapports personnalisés. Dans la vie, il n'y a pas
que le travail qui compte ...
Les nouvelles fonctionnalités et mises
à jour de Crystal Reports V9
· Gain de temps lors de la création de rapports grâce à des
outils de productivité avancés
· Amélioration du reporting et de l'intégration des
applications Internet
· Contrôle SQL illimité pour un accès aux données vraiment
flexible
· Echange efficace des éléments de rapport via portails,
appareils sans fil et Microsoft® Office®
Achetez avant le 30 septembre et essayez
GRATUITEMENT Crystal Analysis™ Professional. Pour
de plus amples informations, tapez
www.crystalreports9.com/frregister.php?campaign=117
© 2002 Crystal Decisions, Inc. Crystal Decisions et Crystal Reports sont soit des marques déposées, soit des
marques commerciales de Crystal Decisions, Inc. aux Etats-Unis et/ou dans d'autres pays. Toutes les
marques
commerciales ou marques déposées citées appartiennent à leurs propriétaires respectifs.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
11
Langage
TOUT PROJET INFORMATIQUE DOIT ACCEPTER FACILEMENT DES MODIFICATIONS.
PARMI LES TECHNIQUES QUI FACILITENT LES MODIFICATIONS DANS LES DÉVELOPPEMENTS
ORIENTÉS OBJET, NOUS PRÉSENTONS LE MÉCANISME DE DÉLÉGATION EXTERNE.
MAIS LA DÉLÉGATION PEUT ÊTRE DIFFICILE À METTRE EN PLACE. L’ARTICLE PROPOSE UNE
TECHNIQUE APPELÉE DYNAMIC JAVA BINDER (DJBINDER), QUI REND LA DÉLÉGATION
EXTERNE PRATIQUEMENT TRANSPARENTE EN ENVIRONNEMENT JAVA.
J
Alvaro Schwarzberg,
a participé à de multiples
projets dans des entreprises
internationales (Colombie,
Brésil, Etats-Unis), principalement chez Dassault
Systèmes. Son expérience
concerne principalement le
développement objet,
internet, les bases de données
et le PLM (Product Life
Management).
’ai eu l’opportunité de participer
à plusieurs projets pour faire évoluer des applications Java
existantes. A ces occasions j’ai
souvent souhaité pouvoir ajouter
de nouvelles fonctionnalités à une classe sans
la recompiler. Voici quelques une des raisons
qui ont justifié ce souhait :
• Je n’avais pas le droit d’accéder ou de modifier le fichier source de la classe. Par exemple,
quand la classe avait été développée par un
éditeur de logiciels externe.
• La classe était installée dans beaucoup de sites distribués et la nouvelle fonctionnalité n’était
utile que pour un nombre limité de ces sites.
• Il y avait des objets persistants et je ne voulais pas gérer plusieurs versions de la même
classe, ni mettre à jour tous les objets persistants en utilisant un outil batch.
• Je ne voulais pas toucher une classe bien
testée qui avait travaillé correctement pendant plusieurs mois ou plusieurs années.
• D’autres développeurs travaillaient sur la
même classe et nous devions coordonner nos
modifications pour éviter les incompatibilités
et ne pas écraser mutuellement nos changements. Cela aurait été bien plus facile si chaque développeur avait pu travailler dans son
propre fichier source associée à la fonctionnalité qui l’intéressait.
Après un temps de réflexion, j’ai décidé que
je ne voulais pas une solution complètement
dynamique comme, par exemple, dans
JavaScript où il est possible de rajouter de nouvelles fonctions à une classe pendant l’exécution. Dans ce type de langages, la performance
est très affectée et il est difficile de maîtriser
l’architecture de l’application.
Finalement, j’ai créé un outil, appelé
Dynamic Java Binder (DJBinder), qui permet
d’attacher dynamiquement à une classe de
nouvelles implémentations d’interfaces sans
modifier le fichier source de la classe. Les interfaces dynamiquement attachées sont
complètement équivalentes aux interfaces listées normalement après le mot clé
"implements".
DJBinder utilise le mécanisme de chargement de classes de la plateforme Java 2 pour
créer le lien entre les interfaces et les classes.
Un exemple valant mieux qu’un long discours,
je me servirai d’un exemple comme fil conducteur de mon explication de DJBinder. Voici
les définitions utilisés dans cet exemple :
class Person
{
public Person (String n, int a) {name=n; age=a;
allPersons.add (this);};
public static Enumeration getAll() {return
allPersons.elements();} ;
public String getName() {return name;} ;
protected static Vector allPersons = new
Vector();
protected String name ;
protected int age ;
}
class Man extends Person
{
public Man (String n, int a) {super(n, a);}
}
class Woman extends Person
{
public Woman (String n, int a) {super(n, a);}
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
12
Langage
}
interface Print
{
void toStandardOutput() ;
}
Il est facile de réaliser que ces classes n’ont
pas été conçues avec l’intention d’utiliser un
quelconque mécanisme dynamique (comme
la grande majorité de classes Java existantes).
Mon cas d’école est le suivant : imaginez
une application qui pendant plusieurs années
a créé des objets de type Man et Woman.
Maintenant, il est nécessaire d’ajouter une
fonctionnalité de listing pour imprimer le
nom et l’âge de chaque personne sur la sortie standard. Afin de réduire le risque de régression la solution développée ne doit pas
modifier le code existant de l’application.
Voyons d’abord le code Java que vous
devez coder pour résoudre ce problème en
utilisant DJBinder. Nous verrons après comment DJBinder fait marcher le tout.
Les instructions suivantes utilisent
l’interface Print pour imprimer les informations d’une personne :
Person p = ... ;
((Print) p).toStandardOutput ();
Le cast est nécessaire car l’interface Print
n’est pas implémentée par la classe Person. Le
compilateur Java croit que l’interface Print est
implémentée par une sous-classe de Person,
mais nous savons que ceci n’est pas vrai. Ni la
classe Man, ni la classe Woman n’implémentent l’interface Print.
Dans DJBinder, la façon d’implémenter
l’interface Print sans toucher à la définition de
la classe Person est une nouvelle classe appelée DI_Person__Print :
public abstract class DI_Person__Print implements
Print
{
public void toStandardOutput()
{
Person p = (Person) (Object) this ;
System.out.println (p.getName()) ;
}
}
Le nom de la classe est très important
parce qu’il fait le lien entre la classe Person et
l’interface Print. La classe est abstraite parce
qu’elle ne peut pas être instanciée directement, ses instances sont créées par BJBinder
en fonction des opérations de cast. La ligne
principale est :
Person p = (Person) (Object) this ;
L’objet référencé par la pseudo-variable
this est castée dans Person afin d’accéder à la
méthode getName(). Le cast n’est pas fait directement, car le compilateur Java interdit le
cast entre classes qui n’appartiennent pas à
la même hiérarchie. Le fait de passer par la
classe Object permet de lever cette restriction.
Pour imprimer l’âge des personnes il faut
accéder au membre age de la classe Person.
Le problème est que le membre age a un accès protected qui interdit l’accès depuis la
classe DI_Person__Print.
La solution offerte par DJBinder est de
créer une nouvelle classe DA_Person qui liste
les membres de la classe Person qui sont accessibles depuis toutes les classes
DI_Person__*. Par exemple, nous pouvons
utiliser la classe suivante pour autoriser l’accès au membre age.
abstract class DA_Person
{
public int age;
}
Ce qui nous permet d’imprimer l’âge des
personnes
dans
la
méthode
toStandardOutput() :
public abstract class DI_Person__Print implements
Print
{
public void toStandardOutput()
{
Person p = (Person) (Object) this ;
System.out.println (p.getName()) ;
DA_Person pp = (DA_Person) (Object) this ;
System.out.println (pp.age) ;
}
}
La principale nouveauté est que la pseudovariable this est castée dans DA_Person au lieu
de Person afin accéder au membre protected
age. Une alternative équivalente serait de
caster la variable p :
DA_Person pp = (DA_Person) (Object) p;
Maintenant nous pouvons rassembler
tous les morceaux pour écrire le code qui imprime toutes les personnes :
public class PrintAllPersons
{
public static void main (String [] arg)
{
Enumeration e = Person.getAll();
while (e.hasMoreElements())
{
Print p = (Print) e.nextElement() ;
p.toStandardOutput () ;
}
}
}
abstract class DA_Person
{
public int age;
}
abstract class DI_Person__Print implements Print
{
public void toStandardOutput()
{
Person p = (Person) (Object) this ;
System.out.println (p.getName()) ;
}
}
DA_Person pp = (DA_Person) (Object) this ;
System.out.println (pp.age) ;
Le mécanisme de DJBinder est similaire à
celui des classes internes (inner classes) de Java.
Les classes DI_* sont pratiquement des classes
internes. La plus grande différence étant que
chaque classe DI_* est définie dans un fichier
source différent et peut être placée dans un
fichier jar différent.
De plus le fait que chaque classe DI_* peut
être dérivée d’une autre classe permet de
combler le manque d’héritage multiple dans
Java d’une façon élégante sans les problèmes
d’autres langages comme C++.
Le code Java de l’exemple compile normalement, mais à l’exécution il y aurait plusieurs
erreurs si DJBinder ne profitait pas du chargement dynamique des classes Java pour
changer le bytecode.
Le chargement des classes Java marche
de la façon suivante : chaque fois que la machine virtuelle de Java a besoin d’une nouvelle classe, elle demande au class loader courant de retourner le bytecode correspondant.
Par exemple, le class loader par défaut cherche un fichier appelé nomDeLaClasse.class
dans les répertoires listés dans la variable d’environnement CLASSPATH, et retourne le contenu du fichier.
Pour sa part, le class loader de DJBinder travaille de façon différente. Il se sert du class
loader normalement utilisé par l’application
pour récupérer le bytecode original, et après il
modifie le bytecode avant de le retourner à la
machine virtuelle.
DJBinder demande que le class loader
utilisé par la machine virtuelle soit celui de
DJBinder. Pour les programmes de type console il suffit de remplacer la commande :
pathDeLaMachineVirtuelle paramètres
nomDeLApplication paramètresDeLApplication
par
pathDeLaMachineVirtuelle paramètres
amslib
.djbinder
.Star
amslib.djbinder
.djbinder.Star
.Startt nomDeLApplication
paramètresDeLApplication
La classe amslib.djbinder.Start se charge
d’exécuter l’application en utilisant le class
loader de DJBinder.
Pour les autres types de programme Java
(applet, servlet, JSP, etc.) il existe une façon
similaire de procéder adaptée aux caractéristiques de chaque environnement d’exécution.
Par exemple il suffit d’exécuter un serveur
d’applications
Java
avec
amslib.djbinder.Start pour que les beans
appelés par les JSP et les servlets soient capables d’utiliser les interfaces dynamiquement
implementées.
Le «class loader» de DJBinder modifie le
bytecode en quatre endroits :
1. les opérations de cast vers des interfaces.
Les exceptions lancées par les opérations
de cast sont capturées et une recherche est
faite pour trouver une classe nommée
DI_classeDeLobjet__interface
ou
DI_superclasseDeLobjet__interface. Si la classe
est trouvée, un objet de la classe DI_* adé-
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
13
Langage
quate est créé et retourné, sinon l’exception
est relancée. Le terme superclasseDeLobjet
représente n’importe quelle classe dans la hiérarchie de la classe jusqu’à java.lang.Object .
Si le même cast est fait deux fois pour un
même objet, le précédent objet DI_* est retourné au lieu de créer un nouvel objet. Ceci
permet de garder des informations dans les
objets DI_*.
Dans mon exemple, cette modification du
bytecode évite l’exception qui serait normalement lancée par l’instruction :
Print d = (Print) e.nextElement() ;
2. les opérations de cast vers des classes.
Si l’objet casté est une implémentation dynamique d’interface (un object DI_*),
DJBinder remplace le cast par une référence
à l’objet principal, c’est-à-dire l’objet qui a déclenché la création de l’objet DI_*.
Dans mon exemple, cette modification du
bytecode évite l’exception qui serait lancée
par les instructions :
Person p = (Person) (Object) this;
DA_Person pp = (DA_Person) (Object) this ;
3. les références vers des classes DA_*.
Les classes DA_* ne sont qu’un artifice
pour le compilateur, et en conséquence toutes les références vers une classe DA_XXX sont
converties dans des références à la classe XXX.
De plus, dans la classe XXX, des méthodes
spéciales permettent l’accès aux membres
protected depuis les classes DI_XXX__*.
Dans mon exemple ces modifications du
bytecode permettent d’accéder au membre
protected age de la classe Person.
4. les opérations instanceof et ==.
Les opérations instanceof et == sont modifiées afin que leur comportement soit compatible avec les opérations de cast. Par exemple, si les classes X, DI_X__I et DI_X__J existent et la variable v référence un objet de
type X, alors les opérations de cast
(I) v
(J) v
ne lancent pas d’exception et les opérations :
v instaceof I
v == (I) v
(J) v == (I) v
retournent True.
Ces modifications du bytecode créent l’illusion que l’objet principal et les objets DI_*
ne forment qu’un seul objet. Ceci rend la programmation beaucoup plus facile. La figure 1
montre l’architecture logicielle qui est rendue
possible par DJBinder.
Une dernière condition exigée par
DJBinder est que les classes DI_X__I et DA_X
appartiennent au même package que la
Figure 1. Architecture de DJBinder. Les classes et interfaces sont placées dans trois fichiers jar
différents. DJBinder fait l'union des trois classes (X, DI_X__I et DI_X__K). Pour tous les effets
pratiques, les interfaces I, J et K sont implémentées par la classe X.
classe X, ou à un sous-package appelé
djbinder. Par exemple si la classe Person appartient au package acme.applis, alors les classes DI_Person__Print et DA_Person doivent
appartenir au package acme.applis ou au package acme.applis.djbinder .
Le sous-package djbinder permet d’utiliser le mécanisme dynamique de DJBinder
pour des classes appartenant à un "sealed
package", c’est-à-dire à un package qui ne
peut plus être modifié.
Avant de finir, regardons les bénéfices apportés par DJBinder de deux points de vue différents : celui du développeur et celui du
client de l’application.
Pour les développeurs, l’unité de modification est normalement le fichier source. C’est
lui qui a une date de création et de modification. Dans les projets importants, avec plusieurs développeurs, il est nécessaire d’établir
un mécanisme de réservation afin que deux
développeurs ne modifient pas simultanément le même fichier source. DJBinder permet de mieux distribuer les fichiers sources
parmi les développeurs, de façon à ce que
chaque développeur travaille sur un ensemble bien défini de services (groupés dans une
même interface). Ceci facilite le travail en parallèle de plusieurs développeurs (concurrent
engineering), et en conséquence la durée du
développement est réduite.
Pour les clients et/ou les administrateurs
d’une application, l’unité de modification est
le fichier exécutable. Dans les applications
Java, les fichiers exécutables sont normalement des fichiers jar. DJBinder permet de
construire des fichiers jars associés à chaque
modification. Il suffit de les ajouter à l’environnement d’exécution, sans modifier les fichiers jars déjà livrés avec les versions précédentes du produit, pour que les nouvelles
fonctionnalités soient disponibles. Normalement, ceci facilite l’administration de l’application et rassure les clients craintifs de régres-
sions.
Une fonctionnalité qui peut être facilement réalisée avec DJBinder est le typage virtuel. Il s'agit de la possibilité d’utiliser un type
différent du nom de la classe Java pour chercher une implémentation dynamique d’interface. Par exemple, vous pouvez associer le
type virtuel "Mexican" à une instance de la
classe Man (la nationalité étant utilisée
comme type virtuel). Dans ce cas l’interface
Print pourrait être implémentée par la classe
DI_Mexican__Print et inclure quelques
champs additionnels. Différentes instances
de la même classe Person peuvent avoir différents types virtuels. Cette flexibilité est très
utile, particulièrement pour communiquer
avec des applications ou des bases de données existantes.
En résumé, DJBinder permet une architecture logicielle très flexible "orientée vers les
fonctionnalités" et qui facilite l’évolution de
n’importe quelle application Java, y compris
celles qui existent déjà, sans avoir besoin de
les recompiler. Pour ce faire, DJBinder utilise
le mécanisme de chargement dynamique de
classes de Java et il n’a pas besoin de compilateurs ni de machines virtuelles spéciales.z
Droit de publication : Java Developer’s Journal
(http://www.sys-con.com/java/).
ALLER PLUS LOIN
Pour plus d’information sur le class loader de Java :
http://java.sun.com/products/jdk/1.2/docs/api/java/lang/
ClassLoader.html
ou contactez moi:
[email protected] .
Vous pouvez télécharger une version complète de DJBinder
depuis le site de DJBinder. Cette version ne peut être utilisée pour
un développement commercial sans autorisation de ma part.
http://www.amslib.com/djbinder
Palais des Congrès de Versailles
3 et 4 Octobre 2002
Le rendez-vous autour de la Technologie JAVA
événement
co-produit par
Un
IONS et
T
A
M
R
INFO IPTION :
INSCR
2.com
e200
p
o
c
s
a
v
www.ja
Partenaires
Majors
JAVA, J 2 EE, J 2 ME, J 2 SE, EAI, XML,
construisent notre avenir
En présence de Kim Polese - Co-fondatrice de Java
Partenaires
Privilèges
En partenariat avec l’
Ingénierie pédagogique
www.java-scope2002.com
© 3ème Millénaire & Cie/Cho You
WSAD, Eclipse, JBuilder, 9iAS, netBeans, SunONE…
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
15
Langage
POUR UN PROGRAMMEUR NON FAMILIER AVEC LE MÉCANISME D'EXCEPTION, SA DÉCOUVERTE EST
SOURCE DE PERPLEXITÉ : POURQUOI UN DISPOSITIF EN APPARENCE SI COMPLIQUÉ POUR ÉTABLIR UN
DIAGNOSTIC D'ERREUR ?
Paul-Bernard Amade,
s'occupe des cours "java fondamental" chez Sun Education France
après un parcours professionnel
orienté vers les langages de
programmation et la messagerie (il
a participé à la réalisation
d'interprètes APL, SQL de langages
"propriétaires" , et à la réalisation de
systèmes X400). Il considère la
programmation comme un art à
pratiquer entre la gastronomie et le
cornet à piston.
[email protected]
Objection
votre honneur !
Il faut arriver à expliquer :
• pourquoi il est important d'établir un
diagnostic précis quand une erreur est
détectée (éviter un message comme
"une erreur est survenue"!)
• pourquoi ce diagnostic est lui- même
un objet (un simple message sous
forme de chaîne de caractère n'est pas
terriblement exploitable si on admet
l'idée qu'un autre code peut prendre
des mesures pour, par exemple, corriger l'erreur)
• pourquoi l'objet qui établit le diagnostic ne sait pas, a priori, "à qui" faire
passer cet objet (un code "réutilisable"
est le moins possible dépendant du
contexte, l'objet Throwable remonte la
pile d'exécution et est repassé à un
code initiateur de la demande qui a
provoqué l'exception).
• pourquoi le compilateur Java nous
force-t-il , en général, à récupérer quelque part cet objet de diagnostic (pour
améliorer la robustesse du code : vous
n'avez jamais vu de code dans lequel
l'oubli de la prise en compte d'une erreur provoquait des catastrophes ?)
Nous allons revenir ici sur deux aspects : quelles informations de diagnostic et qui déclenche une Exception? (sans parler du cas où c'est l'exécuteur de la JVM qui décide qu'il y a
quelque chose d'anormal).
Quelles informations
de diagnostic?
Supposons que nous écrivions un
code applicatif qui découvre subitement qu'il manque un produit commandé par un client , écririons nous :
if (yenaplus) {
throw new ExceptionStockEpuise(
"stock épuisé" ); // MAUVAISE IDEE!
}
Cette rédaction appelle plusieurs
remarques :
• C'est une tautologie : si nous avons
pris la peine de définir un type de diagnostic précis (ExceptionStockEpuise)
la nature même de cet objet dit que le
stock est épuisé. Le contenu du message est tout à fait superflu.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
16
Langage
• Bien que la classe mère des Exceptions (java.lang.Throwable) définisse
un constructeur avec un argument
"motif" (de type String). Il faut prendre
cette possibilité avec des pincettes :
• Le motif spécifié doit servir à préciser les circonstances de l'incident et
à préciser les informations qui accompagnent le diagnostic.
• L'intention est, en général, de produire une chaîne "lisible par un être
humain"... Cet être humain pourrat'il lire le message? Si votre message
est en français et qu'il ne sait lire que
le batave le but n'est pas atteint. Moralité: mieux vaut prévoir une chaîne
qui soit facilement exploitable par le
mécanisme d'internationalisation.
Ceci est une autre histoire...
• Qui nous dit que ce qui récupère le
diagnostic est nécessairement un
être humain ? Peut-être un autre
code sera-t'il intéressé par ce diagnostic pour exploiter des informations plus tangibles qu'une simple
chaîne de caractère. Peut-ètre y
aura-t'il un code quelque part pour
s'occuper de ce problème de stock
et ce code aurait besoin de données Java.
Faisons un essai simplifié de définition de l'Exception :
package com.pousse.gestion.stock ;
public class ExceptionStockEpuise extends
Exception {
public final Produit produitConcerné ;
public final Stock étatStock ;
public final Demande requête ;
public ExceptionStockEpuise (String
complémentInfo, Produit produit,
Stock état,
Demande demande) {
super(complémentInfo) ;
this.produitConcerné = produit ;
this.étatStock = état ;
this.requête = demande ;
}
// + méthodes spécifiques
}
Comme pour tout exemple celuici ne prétend pas être un modèle mais
une incitation à la reflexion. Quelques
points:
D'abord une figure de style : les
champs du diagnostic sont "public final" au lieu d'être "private" avec des
accesseurs. En observant les sources
mêmes de Java, on s'aperçoit qu'il n'y
a pas de politique constante à ce sujet. Pourtant, dans bien des cas, on
peut estimer que c'est une bonne stra-
tégie pour un objet diagnostic immuable. Bien sûr cela ne dispense pas , le
cas échéant, de doter cet objet d'un
comportement spécifique.
L'objet Exception essaye de véhiculer toutes les informations importantes sur l'incident. On remarquera
qu'il sera toujours temps, dans le code
qui récupérera le diagnostic, de générer des informations "lisibles par un
être humain". En attendant
u
n
code applicatif pourra aussi prendre
des mesures basées sur les informations exploitables par programme.
Ceci dit, il faut être prudent sur le
transfert d'objets par une exception.
• Entre le moment où le diagnostic s'est
déclenché et le moment où il est effectivement exploité il ne faut pas que
l'état d'un objet dont on passe la référence ait évolué d'une manière à fausser le diagnostic. Dans l'exemple on
peut imaginer la situation suivante :
une demande de produit déclenche
une erreur liée à la gestion de stock,
avant que le diagnostic soit exploité
une autre demande concurrente intervient. Dans ce cas toute information
susceptible d'évoluer doit être "clonée" pour figer le diagnostic à un instant T (on peut imaginer que c'est le
cas du champ "étatStock" dans notre
exemple).
• Si une instance d'exception "voyage"
sur le réseau (entre un client et un serveur, entre applications collaborantes,
etc.) il faut bien sûr que les objets référencés puissent eux-mêmes voyager
et ça ce n'est pas toujours évident. Bon,
on peut toujours faire prendre le relai
à une autre Exception qui voyage
mieux : nous laisserons le lecteur réfléchir à ce que cela implique.
Quelques autres remarques sur ce
que contient un diagnostic :
• Un diagnostic peut être établi par
le programmeur quand il détecte une
situation anormale et cette détection
peut elle-même résulter du déclenchement d'une autre exception. Dans
ce cas c'est un code à l'interieur d'un
bloc "catch" qui gère la création d'une
nouvelle Exception :
try {
..... //sauvegarde d'une donnée
} catch (IOException exc) {
throw new
ExceptionSauvegarde(exc,
donnéecourante) ;
}
On a ici deux diagnostics :
- on n'a pas pu sauvegarder cette donnée
- du fait d'une IOexception que voici.
Ce "chainage d'exception" a été
systématisé à partir de la version 1.4
de Java. java.lang.Throwable est dotée
d'un constructeur qui prend en paramètre un autre Throwable et la méthode "getCause()" permet d'obtenir
cet autre diagnostic.
• Les objets Throwable contiennent aussi implicitement des informations sur l'état de la pile. Les programmeurs connaissent généralement les
méthodes "printStackTrace()" qui exploitent ces informations. (On notera,
par ailleurs, qu'ils utilisent souvent
abusivement ces méthodes qui devraient n'être reservées qu'à des situations temporaires de debug.) Java 1.4
permet un accès plus sophistiqué à ces
informations au travers de la méthode
"getStackTrace()".
Le point important dont il faut se
souvenir pour avoir un diagnostic correct de ce point de vue est qu'il faut
faire le "new" d'une exception pratiquement à l'endroit précis où on fait
le "throw" : dans le cas contraire les informations de trace seraient erronées.
De manière générale le choix du point
de déclenchement mérite une petite
réflexion.
Qui déclenche
une exception ?
Rapellons ici la théorie. Une demande de service a :
• des paramètres,
• un résultat (quand il s'agit d'une méthode),
• un ou des diagnostics d'erreurs (les
exceptions déclarées par la clause
"throws").
Outre l'avantage d'une spécification précise du service, ce dispositif
nous permet d'éviter des situations
"à la langage C" dans lesquelles on a
des fonctions qui rendent un résultat
et.... certains résultats doivent être testés pour détecter un condition anormale. Tests parfois oubliés au détriment de la robustesse du code.
Dans la mesure où Java nous offre
un mécanisme pour renforcer cette
robustesse (les exceptions controlées)
il serait dommage d'oublier les leçons
du passé : on voit parfois des codes
Java qui n'utilisent les exceptions que
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
17
Langage
pour signaler l'aboutissement d'une
cascade de tests qui ont échoué. Pour
différentes raisons il convient de rapprocher au maximum le déclenchement d'une Exception de la source de
l'erreur.
Reprenons l'exemple de notre
Stock pour illustrer une de ces raisons
: la mauvaise idée serait d'écrire dans
notre application un code qui, avant de
déclencher une Exception, teste une
possibilité de demande d'achat pour
un Produit
qui à son tour demande une information à son Stock.
// dans code applicatif ....... A EVITER!
if( monProduit.estDisponible() ) {
monProduit.acheteSelon(maDemande);
}
else {
throw new ExceptionStock(/*parametre
*/) ;
}
C'est à l'objet le plus proche de la
connaissance de la situation anormale
de réagir (en l'occurence l'instance de
Stock). Cela change le point de vue du
service : au lieu de tester si une demande est possible on réalise directement la demande d'achat au risque de
la voir échouer. On peut légitimement
se demander si cette philosophie est
généralisable car un mécanisme d'exception n'est pas neutre au niveau performance.
Dans le cas ci-dessus il faut prendre en consideration le fait qu'on est
obligé de verrouiller toute la chaine
des appels ("synchronized") comprenant le test et la demande d'achat définitive (sinon le produit risque de ne
plus être disponible
au moment
où se réalise l'achat).
On aurait donc:
• dans Stock : quelque chose
comme
public synchronized void
fournirSelon(Demande dm) throws
ExceptionStockEpuise {...}
On notera ici un petit problème
avec l'exemple lui même : si
ExceptionStockEpuise utilise dans son
constructeur un Produit, alors le Stock
doit le connaître (nous laissons le lecteur imaginer des correctifs à cette petite incohérence).
• dans Produit : quelque chose
comme
phique avec un utilisateur pour lire le
rapport ? dans une application en tâche de fond avec un administrateur
pour lire le rapport ? etc.).
Un programmeur aguerri doit savoir répondre à ces questions :
public void acheterSelon(Demande dm)
throws ExceptionStockEpuise, ... {...}
• Quand utiliser des exceptions
controlées, quand utiliser des
RuntimeExceptions ?
Le code propage l'exception venu
de l'instance de Stock.
• Quand récupérer l'une et l'autre ?
• etc.
...jusqu`a ce que l'initiateur de la
demande reçoive un diagnostic. Notons que cela n'empêche pas un
code intermédiaire de récupérer l'exception avant de la repropager et ceci
pour informer d'autres parties intéressées par cet épuisement de stock.
Qui récupère
une exception ?
Voilà un vaste problème: quand
recupérer un exception dans un
bloc catch et que faire dans le cadre
de ce bloc ?
Si l'on considère une application
Java comme un assemblage (éventuellement dynamique) de sous-systèmes,
chaque sous-système doit signaler à
l'appelant l'échec d'une demande de
service le concernant. A un point de la
hiérarchie des appels un code doit être
capable de clore l'incident (ou bien
alors l'application s'arrête).
Un tel code n'est pas nécessairement situé "à la surface" de l'application (par ex. dans l'interface utilisateur). Dans ce dernier cas on rentre
dans une seconde phase de vie de l'exception : comment adresser un rapport signalant à "quelqu'un" que quelque chose s'est passé ? Le code qui
génère le rapport ne sait pas, a priori,
comment il est utilisé (est-il appelé
dans le contexte d'une interface gra-
• Comment écrire un code qui récupère quand même les Exceptions
qui ont échappé aux blocs catch ?
• Quand utiliser des assertions ? (à
partir de Java1.4)
• Pourquoi est-il immoral de laisser un
bloc "catch" vide ? (sauf dans de très
rares cas)
• Comment utiliser les blocs "finally"
à bon escient ?
• Comment filtrer des groupes d'exceptions par des "catch" en cascade ?
• Peut-on faire quelque chose d'une
Error ?
• Pourquoi n'est-il pas "politiquement
correct"
d'utiliser
printStackStrace() ?
• Comment utiliser des mécanismes
de traces, rapport, journalisation et
en particulier ceux du package
java.util.logging ?
• Comment et à quel moment internationaliser les messages "lisibles
par un être humain" ?
• Comment rendre le code plus facile
à maintenir en signalant au lecteur
les endroits où le déroulement normal des instructions est affecté par
la
propagation possible d'une
Exception.
Bon jeu de piste !
z
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
18
Langage
LA PERSISTANCE DES OBJETS EST UN PROBLÈME NON RÉSOLU EN JAVA. SI L'ON DOIT
GÉRER UN PETIT NOMBRE D'OBJETS, LA SÉRIALISATION EST UNE SOLUTION.
Gil Francopoulo
société Tagmatica
[email protected]
Les SGBD
La sérialisation
Tout d’abord, une mise en garde
évidente : le présent article reflète l’avis
personnel de l’auteur. Et cet avis n’est pas
nécessairement celui des autres membres du
Club-Java.
La persistance des objets n’est pas un
problème résolu en Java. Quand on a un
modèle simple avec un nombre moyen
d’objets, on peut se satisfaire d’un stockage
via un SGBD relationnel. Mais si l’on a des
millions d’objets et que l’on doit effectuer des
jointures (c’est bien souvent le cas quand on
a un modèle physique très éloigné du
modèle conceptuel et que l’on doit recalculer
le modèle conceptuel), alors le système
s’écroule. Si on a des objets plus complexes,
plus interconnectés ou de l’héritage, il vaut
mieux passer à un SGBD objets.
Dans un cas comme dans l’autre, cela
passe par l’achat (donc la dépendance) d’un
logiciel tiers. En tout cas, java ne répond pas
à cette problématique.
Si l’on doit gérer un petit nombre
d’objets, une autre possibilité est la
sérialisation. Le principe est assez simple : on
convertit les objets en un format adapté à la
sortie en flux. La désérialisation est le
processus inverse qui convertit un objet
sérialisé en une instance d’objet. Pour qu’un
objet soit sérialisé, il doit implémenter
l’interface « java.io.Serializable ».
On peut toujours chipoter sur le thème :
« est-ce qu’un langage de programmation
doit comporter ou non un mécanisme de
persistance ? », mais personnellement, je peux
répondre « oui » sans hésiter une seconde,
tout simplement parce que j’en ai souvent
besoin et que la pérennité du langage est bien
supérieure à celle des fournisseurs de logiciels
tiers. Notons qu’il existe un projet qui s’appelle
JDO, qui sera peut-être intégré au JDK, mais
ce ne sera pas avant plusieurs années, donc
je n’en parlerai pas.
La sérialisation pose trois problèmes :
1. Quand on désérialise des objets nonstructurés ou structurés sous forme
arborescente, le mécanisme est trivial. Mais,
quand on a affaire à un graphe (avec ou sans
cycle) c’est un peu plus délicat : il ne faut pas
désérialiser les objets qui l’ont déjà été. Il faut
donc marquer les objets déjà sérialisés afin
de ne pas les dupliquer artificiellement sur
disque. On utilise pour cela, soit un attribut
local de l’objet ou bien un bitSet général, ce
qui prend moins de place.
2. Lors de la désérialisation, le problème
se repose dans le sens inverse : a-t-on ou non
déjà créé l’objet ? Comment relier les objets
entre eux si celui-ci a été créé longtemps
auparavant ? En d’autres termes, il faut se
livrer à une édition des liens complète.
3. Mais surtout, cela prend beaucoup de
temps quand on a beaucoup d’objets. J’ai eu
récemment à gérer 5 millions d’objets. Il était
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
19
Langage
hors de question de les gérer par
sérialisation, car cela prenait plusieurs heures
pour sérialiser et désérialiser.
Qu’est-ce qu’un
fichier mappé ?
La technique du fichier mappé est un
mécanisme utilisé depuis longtemps par
certains développeurs C. En langage C, les
appels systèmes ne sont pas portables :
Sous Unix, il faudra faire :
« open » puis « mmap »
Sous Windows, il faudra faire :
« fOpen32 » puis « fMmap32 »
C’est un peu complexe à mettre en œuvre
(et à mettre au point), mais cela marche et
un certain nombre de gros logiciels
professionnels en tirent parti.
Pour ce qui concerne Java, la nouveauté
depuis le JDK-1.4 (sorti en version de
production en février 2002), c’est qu’on peut
faire la même chose en java. Il va sans dire
que le code est strictement identique qu’elle
que soit la plate-forme.
Après avoir importé les packages qui
vont bien (c’est toujours un peu pénible cette
affaire) :
import java.io.* ;
import java.nio.* ;
import java.nio.channels.* ;
import java.nio.channels.FileChannel.* ;
Il suffit des trois lignes magiques
suivantes :
mécanisme de mémoire virtuelle. En général,
ce sont des pages de 8K octets ou plus et
dont la montée et la descente sont
contrôlées par l’algorithme de gestion de la
mémoire virtuelle du SE. Cet algorithme tire
parti de la RAM disponible et est assisté par
un chip de gestion mémoire prévu à cet effet.
Bref … Ce que l’on sait, c’est que les
informations sont montées en mémoire à la
demande et que lorsque l’on ferme le fichier,
la totalité de la zone figure sur disque.
Notons qu’il est possible d’influencer la
stratégie du SE depuis java avec des ordres
spécifiques comme « load », mais cela n’a
d’intérêt (vis-à-vis du reste de la charge de la
machine) que lorsqu’on a plusieurs fichiers
mappés et que l’on désire en favoriser l’un
plutôt que l’autre. En fait, disons que ces
considérations débordent le périmètre du
présent article.
Pour plus de détails, vous pouvez
consulter : J. Hart, Early Adopter J2SE-1.4
[Wrox Press 2001].
L’usage
du fichier mappé
Maintenant que le fichier est mappé, on
dispose d’une zone d’octets : ce sont des
« bytes ». Notons que j’utilise volontairement
le terme de « zone » et non de tableau, car
ne n’est pas réellement un tableau Java. On
va pouvoir se déplacer en spécifiant le
déplacement en octets (j’insiste, ce sont des
octets). On va écrire l’objet avec la méthode
idoine.
Imaginons que l’on désire inscrire l’entier
7 à la position 36 du fichier, on fera :
RandomAccessFile raf= new
RandomAccessFile("f", "rw");
raf.seek(36L) ;
raf.writeInt(7);
FileChannel fc= raf.getChannel();
MappedByteBuffer mbb= fc.map(
MapMode.READ_WRITE, 0L, fc.size() );
On notera que le paramètre de «seek» est
un entier long. C’est heureux, car sinon, on
ne pourrait pas gérer des fichiers de taille
supérieure à 2 gigas (231 – 1). On n’a pas
forcément besoin de gérer des fichiers de
cette taille-là tous les jours, mais cela m’est
déjà arrivé et j’étais bien content de pouvoir
le faire.
Explication des trois lignes:
Ligne-1 : On crée un RandomAccessFile.
C’est un fichier qui va nous permettre de
nous déplacer dans toute sa longueur. Pour
en augmenter la taille, il suffit d’écrire en fin
de fichier. On va pouvoir lire et écrire sans
autre formalité.
Inversement, pour lire, il suffit de faire :
raf.seek(36L) ;
int e= raf.readInt();
Ligne-2 : On obtient le canal du fichier.
Ligne-3 : On « mappe » le canal sur la
mémoire. C’est cette instruction qui est
magique. A partir de ce moment, on a une
zone mémoire qui est associée à un fichier.
L’association en question n’est pas définie
strictement dans le langage. Le système
d’exploitation (SE) gère l’association via son
Un peu de pratique
sur la sérialisation
Voici un petit exemple tout simple de
sérialisation. Le programme principal prend
un paramètre. Si l’on donne le paramètre
« creation », l’objet est créé et sérialisé. Si l’on
donne le paramètre « consultation », l’objet
est désérialisé et interné en mémoire. C’est
la classe A qui suit :
import java.io.*;
class A implements Serializable {
int c1;
public static void main(String[] args){
String opt= args[0];
if (opt.equals(«creation»))
creation();
else if (opt.equals(«consultation»))
consultation();
}
static void creation(){
try {
A a= new A();
a.c1= 7;
ObjectOutputStream out= new
ObjectOutputStream(
new FileOutputStream(«A.dmp»));
out.writeObject(a);
out.close();
} catch (Exception e){
e.printStackTrace();
}
}
static void consultation(){
try {
ObjectInputStream in= new ObjectInputStream(
new FileInputStream(«A.dmp»));
A a= (A)in.readObject();
System.out.println(«Après rechargement:
«+a.c1);
in.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
Si l’on a besoin d’un traitement spécial
(ce qui est souvent le cas), il faut définir les
fonctions d’E/S notamment pour réaliser les
opérations d’édition des liens dont il était
question au chapitre 3. C’est la classe B qui
suit :
import java.io.*;
class B implements Serializable {
int c1;
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeInt(c1);
}
private void readObject(ObjectInputStream in)
throws IOException {
c1= in.readInt();
}
public static void main(String[] args){
String opt= args[0];
if (opt.equals(«creation»))
creation();
else if (opt.equals(«consultation»))
consultation();
}
static void creation(){
try {
B b= new B();
b.c1= 7;
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
20
Langage
ObjectOutputStream out= new
ObjectOutputStream(
new FileOutputStream(«B.dmp»));
out.writeObject(b);
out.close();
} catch (Exception e){
e.printStackTrace();
}
}
static void consultation(){
try {
ObjectInputStream in= new ObjectInputStream(
new FileInputStream(«B.dmp»));
B b= (B)in.readObject();
System.out.println(«Après rechargement:
«+b.c1);
in.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
Un peu de pratique
sur le fichier mappé.
C’est la classe C qui suit :
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.FileChannel.*;
class C {
int c1;
static void ecrire(RandomAccessFile raf,C c) throws
IOException {
raf.writeInt(c.c1);
}
static Object lire(RandomAccessFile raf) throws
IOException {
C c= new C();
c.c1= raf.readInt();
return c;
}
public static void main(String[] args){
String opt= args[0];
if (opt.equals(«creation»))
creation();
else if (opt.equals(«consultation»))
consultation();
}
static void creation(){
try {
C c= new C();
c.c1= 7;
RandomAccessFile raf= new
RandomAccessFile(«C.dmp»,»rw»);
FileChannel fc= raf.getChannel();
MappedByteBuffer mbb=
fc.map(MapMode.READ_WRITE,0L,fc.size());
ecrire(raf,c);
fc.close();
} catch (Exception e){
e.printStackTrace();
}
}
static void consultation(){
try {
RandomAccessFile raf= new
RandomAccessFile(«C.dmp»,»rw»);
FileChannel fc= raf.getChannel();
MappedByteBuffer mbb=
fc.map(MapMode.READ_WRITE,0L,fc.size());
C c= (C)lire(raf);
System.out.println(«Après rechargement: «+c.c1);
fc.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
Discussion
Les trois exemples de classe ne sont
destinés à servir de modèle à une gestion de
persistance d’un graphe. Ils sont juste
présentés pour établir une comparaison
entre la sérialisation et le fichier mappé. Ces
deux mécanismes sont très différents.
La question qui est souvent posée est :
« Est-ce que tous les objets sont chargés en
mémoire ? » Via la sérialisation, la réponse est
clairement « oui », puisque les objets sont
internés par création en début de session. Via
le fichier mappé, la réponse est « non ». Au
début de session, on mappe le fichier : on
prépare le terrain. Au cours de la session, le
fichier est chargé à la demande (page par
page) et les objets sont créés un par un à la
demande. Les difficultés d’édition de liens
(évoquées au chapitre 3) n’existent plus.
Ce qui peut paraître étrange, c’est que
l’on arrive à gérer des millions d’objets
comme cela. Et bien justement : pour gérer
de grandes bases d’objets, il faut prendre des
techniques simples. Si l’on utilise des moyens
tortueux, on n’y arrive pas.
Le lecteur perspicace aura peut-être
comprit qu’il est possible de se servir d’un
fichier mappé pour implémenter un véritable
système de gestion d’objets avec des index,
des relations, des listes, de l’héritage et des
transactions etc. C’est d’ailleurs comme cela
que fonctionnent certains SGBD objets du
commerce (présent ou passé). Mais ce n’est
pas une mince affaire à développer. Ce sera
peut-être le sujet d’un autre article ?
z
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
21
Langage
JAVA MANAGEMENT EXTENSION (JMX) PROPOSE UN FRAMEWORK
POUR ADMINISTRER SES APPLICATIONS À CHAUD.
L
Philippe PRADOS
est ethical hacker chez IBM
Global Services. Son équipe
aide les architectes et les
développeurs à intégrer la
sécurité très tôt dans le
développement. Elle audite
les applications existantes
afin de qualifier les risques et
de renforcer la sécurité. Elle
forme les développeurs pour
leurs permettre d’avoir un
regard critique sur chacune
des lignes qu’ils rédigent
es applications ont besoin d'être
administrées à chaud. Il est in
téressant de pouvoir intervenir
sur le paramétrage de l'applica
tion sans devoir l'interrompre.
Par exemple, il doit être possible de modifier
à chaud les critères de trace afin d'identifier
les problèmes. D'autre part, il est intéressant
de pouvoir consulter différents indicateurs
d'une application afin d'améliorer son comportement. Par exemple, les différents paramètres de cache peuvent être modifiés pour
tenir compte d'une pointe de trafic. Des indicateurs applicatifs peuvent avertir d'un risque
avant qu'il ne se produise.
Java Management Extension (JMX) propose une technologie normalisée de management. En respectant ces spécifications, une
application peut être managée à l'aide d'un
agent générique. Les spécifications sont séparées en deux parties : un pattern de développement pour les applications manageables et un agent s'appuyant sur ce pattern
pour manager une
application.
L'objectif de JMX
est de décharger le
développement de
l'agent. En quelques
lignes, il peut offrir
différents indicateurs
ou des API permettant
de manipuler l'application à chaud. L'agent
sera, à terme, intégré
aux moteurs J2EE. Cela
permettra de manager
le moteur lui-même,
Figure 1.
pour ajouter de nouvelles servlets, modifier les
paramètres d'accès aux bases de données, relancer les applications Web ou les moteurs EJB,
modifier les paramètres de sécurités, etc.
L'agent JMX
En respectant les spécifications JMX,
l'agent est capable de manager les autres couches logicielles que vous avez développées.
En téléchargeant les spécifications JMX et
les librairies associées, un agent est proposé
par défaut. Il permet le management d'une
application à partir d'un simple navigateur.
Cet agent n'offre pas tous les services que l'on
peut attendre d'un agent JMX complet. Il n'est
pas capable de traiter les événements du serveur afin de rafraîchir la page. Il utilise un
mécanisme de pooling pour contourner cela.
Quelques lignes permettent de lancer cet
agent :
MBeanServer server =
MBeanServerFactory.createMBeanServer();
com.sun.jdmk.comm.HtmlAdaptorServer html =
new com.sun.jdmk.comm.HtmlAdaptorServer();
server.registerMBean(html, new
ObjectName("Adaptor:name=html,port=8082"));
html.start();
La première ligne crée un serveur de
MBean. Un MBean est un bean manageable.
La deuxième ligne construit une instance de
l'agent HTML. La suivant l'enregistre dans le
serveur de MBean sous le nom
"Adaptor:name=html,port=8082". L'agent est
lui-même un MBean enregistré. Il est donc
administrable. Les noms des MBeans permettent de les retrouver à l'aide de requêtes complexes. La dernière ligne démarre l'agent.
En demandant la page http://
localhost:8082 vous pouvez administrer votre
application (figure 1).
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
22
Langage
Figure 3.
Figure 4.
Figure 2.
La page affiche le nom des différents
MBean enregistrés. Un filtre permet de retrouver un bean particulier à partir de son nom
de domaine ou de la valeur d'une de ces propriétés. Le bouton Admin permet d'enregistrer dynamiquement d'autres MBean. En cliquant sur un MBean, une page permet de modifier les différentes propriétés ou d'invoquer
des traitements (figure 2).
Si vous désirez protéger l'agent, vous devez
indiquer les différents utilisateurs acceptés.
MBeanServer server =
MBeanServerFactory.createMBeanServer();
AuthInfo[] auth=new AuthInfo[]{new
AuthInfo("admin","password")};
HtmlAdaptorServer html = new
HtmlAdaptorServer(8082,auth);
server.registerMBean(html, new
ObjectName("Adaptor:name=html,port=8082"));
html.start();
Le protocole HTTPS n'est pas supporté. Il
faut alors faire attention aux différents réseaux traversés lors de l'identification de l'utilisateur. Il est préférable d'administrer l'application directement sur le serveur.
Un autre agent en Open Source résout les
limitations de l'agent de Sun. L'agent MX4J
permet d'administrer des MBeans à partir
d'un navigateur HTML, via des filtres XSL. Cela
permet d'adapter la présentation à l'application aux MBeans manipulés. Il supporte l'utilisation du SSL et améliore la sécurité. Il permet également d'utiliser RMI pour manager
les MBeans à distance (figure 3).
Les MBeans
Pour pouvoir être administrable, une application doit enregistrer des MBeans auprès
d'un serveur JMX. Un MBean est un bean avec
des méthodes, des propriétés et des événements. Il existe différentes catégories de
MBean, plus ou moins complexes à rédiger.
Le MBean standard implémente une interface
suffixée par MBean, et proposant les différents
services et propriétés disponibles pour
l'agent. Par introspection, le serveur JMX déduit les méthodes et les propriétés du bean.
public interface MaClassMBean
{
public int getState();
public void setState(int s);
public void reset();
}
public class MaClass implements
MaClassMBean
{
private int state = 0;
private String hidden = null;
public int getState()
{ return(state);
}
public void setState(int s)
{ state = s;
}
public void reset()
{ state = 0;
}
}
Seul les propriétés et les services décrits
dans l'interface sont disponible pour l'agent
JMX. En enregistrant une instance MaClass,
l'agent
recherche
une
interface
MaClassMBean, l'analyse, et propose la consultation et la modification du bean. Si vous
demandez à l'agent, la création d'une instance
MaClass sous le nom :name=test, vous pouvez modifier la propriété state ou invoquer la
méthode reset() (figure 4). En cliquant sur
nom de la propriété ou sur la description de
la méthode reset, vous obtenez un message
générique (figure 5). Pour offrir des informations plus pertinentes, il faut rédiger un
MBean dynamique.
MBean dynamique
Un MBean dynamique doit décrire son interface à l'aide de métadonnée et d'une interface spécifique. Cela permet d'enrichir d'un
message explicatif la description des propriétés, des méthodes ou des constructeurs. Cela
Figure 5
permet également d'informer des différents
événements que peut générer le MBean.
L'interface DynamicMBean propose différentes méthodes permettant de décrire les
possibilités du MBean.
public interface DynamicMBean
{
public MBeanInfo getMBeanInfo();
public Object getAttribute(String attribute);
public void setAttribute(Attribute attribute);
public AttributeList getAttributes(String[]
attributes);
public AttributeList setAttributes(AttributeList
attributes);
public Object invoke(String actionName, Object
params[],
String signature[]);
}
La méthode getMBeanInfo() doit retourner toutes les méta-informations décrivant le
MBean. Les méthodes get/setAttribute(s) permettent de modifier un ou plusieurs attributs.
La méthode invoke permet d'invoquer une
méthode du MBean. Un MBean ne peut implémenter simultanément une interface
nameMBean et DynamicMBean. Modifions
notre MBean pour le rendre dynamique.
import javax.management.*;
import java.util.*;
public class MaClass implements DynamicMBean
{
private int state = 0;
public int getState()
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
23
Langage
{ return(state);
}
public void reset()
{ state = 0;
}
public void setState(int s)
{ state = s;
}
//---------public MBeanInfo getMBeanInfo()
{
MBeanParameterInfo[] noParamInfo=new
MBeanParameterInfo[0];
MBeanAttributeInfo attributes[] = new
MBeanAttributeInfo[1];
attributes[0] = new
MBeanAttributeInfo("state","int",
"Etat de MaClass", true, false,false);
MBeanConstructorInfo[] constructors = new
MBeanConstructorInfo[1];
constructors[0]=new
MBeanConstructorInfo("MaClass",
"Administre MaClass",noParamInfo);
MBeanOperationInfo[] operations = new
MBeanOperationInfo[1];
operations[0]=new MBeanOperationInfo("reset",
"Reset MaClass",
noParamInfo,
void.class.getName(),MBeanOperationInfo.ACTION);
return new MBeanInfo(getClass().getName(),
"Administre MaClass",
attributes,
constructors, // Contructeur
operations, // Operation
null); // Notification
}
public Object getAttribute(String attribute)
throws AttributeNotFoundException
{
if (attribute.equals("state"))
{ return new Integer(getState());
}
throw new
AttributeNotFoundException(attribute);
}
public AttributeList getAttributes(String[]
attributes)
throws AttributeNotFoundException
{
AttributeList list=new AttributeList();
for (int i=0;i<attributes.length;++i)
{
String name=attributes[i];
Object rc=getAttribute(name);
list.add(new Attribute(name,rc));
}
return list;
}
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException,
InvalidAttributeValueException
{
String name=attribute.getName();
try
{
if (name.equals("state"))
{
setState(((Integer)attribute.getValue()).intValue());
}
throw new AttributeNotFoundException(name);
}
catch (ClassCastException x)
{
throw new
InvalidAttributeValueException(name);
}
}
public AttributeList setAttributes(AttributeList
attributes)
throws AttributeNotFoundException,
InvalidAttributeValueException
{
for (Iterator i=attributes.iterator();i.hasNext();)
{
Attribute attr=(Attribute)i.next();
setAttribute(attr);
}
return attributes;
}
public Object invoke(String actionName, Object[]
params,
String[] signature)
throws MBeanException, ReflectionException
{
try
{
if (actionName.equals("reset"))
{ reset();
}
return null;
}
catch (Exception x)
{ throw new MBeanException(x);
}
}
}
Maintenant, les descriptions des propriétés et des méthodes sont plus riches. Un clic
permet d'avoir la description d'un service ou
d'une propriété. Ces services sont suffisants
pour l'agent HTML proposé par Sun.
Pour des agents plus complets, nous pouvons générer des notifications lors de la modification de la propriété state. Le source suivant indique les modifications.
import javax.management.*;
import java.util.*;
import java.beans.*;
public class MaClass extends
NotificationBroadcasterSupport
implements DynamicMBean
{
private int state = 0;
private long sequence_;
...
public MBeanInfo getMBeanInfo()
{
...
return new MBeanInfo(getClass().getName(),
"Administre MaClass.",
attributes,
constructors, // Contructeur
operations, // Operation
getNotificationInfo()); // Notification
}
public final MBeanNotificationInfo[]
getNotificationInfo()
{
MBeanNotificationInfo[] info;
info=new MBeanNotificationInfo[1];
info[0]=new MBeanNotificationInfo(
new
String[]{AttributeChangeNotification.ATTRIBUTE_CHANGE},
AttributeChangeNotification.class.getName(),
"Attribut modifié");
return info;
}
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException,
InvalidAttributeValueException
{
String name=attribute.getName();
try
{
if (name.equals("state"))
{
int old=getState();
setState(((Integer)attribute.getValue()).intValue());
Notification notification=new
AttributeChangeNotification(this,
sequence_++,
System.currentTimeMillis(),
"Etat modifié",
"state",
Integer.class.getName(),
new Integer(old),
attribute.getValue());
sendNotification(notification);
}
throw new AttributeNotFoundException(name);
}
catch (ClassCastException x)
{ throw new
InvalidAttributeValueException(name);
}
}
}
Un agent plus ergonomique que l'interface HTML peut afficher des courbes sur l'évolution dans le temps des valeurs des attributs
par exemple.
Open MBean
Un Open MBean est un MBean dynamique n'utilisant que certaines classes pour les
attributs et les paramètres des méthodes. Cela
permet aux agents de mieux comprendre les
informations manipulées par le MBean et de
proposer des ergonomies adaptées. Les types
acceptés sont les versions classes des types
primitifs plus trois nouvelles classes :
• javax.management.ObjectName
• javax.management.openmbean.CompositeData
(interface)
• javax.management.openmbean.TabularData
(interface)
Celles-ci permettent de décrire des structures complexes ou tabulaire. L'agent peut
alors proposer des tableaux ou des arbres
pour modifier ou consulter des propriétés.
Notre MBean est déjà un Open MBean car les
méthodes de l'interface DynamicMBean utilisent le type Integer. En quelques lignes, vous
pouvez offrir une administration à chaud de
votre application. L'agent par défaut est sympathique, mais n'offre pas toutes les fonctionnalités des MBeans. Les notifications ne sont
pas gérées.
JMX est un framework très simple. Des
évolutions permettront l'administration à distance de MBean lors de la publication de JMX
1.5. Cette API prendra tout son sens lorsque
les moteurs J2EE proposeront un agent JMX
complet. Votre application pourra être manager en même temps que vos servlets. Vous
pouvez dès maintenant anticiper ces évolutions en enregistrant vos MBean auprès d'un
MBean Serveur et en installant un agent
HTML.
z
ALLER PLUS LOIN
Spécification :
http://java.sun.com/jmx
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
24
Langage
CET ARTICLE POSE LES FONDATIONS D’UN FRAMEWORK SIMPLE PERMETTANT DE CONSTRUIRE UN
SYSTÈME RÉELLEMENT ADAPTATIF. APRÈS QUELQUES RAPPELS SUR JDBC ET JSP, NOUS VERRONS
COMMENT UTILISER LE PROCESSEUR XSLT COMME UN PUISSANT GÉNÉRATEUR DE CODE SOURCE JAVA.
Par Victor Okunev
D
ans certains projets logiciels,
vous ne pouvez pas vous
permettre de fixer de ma
nière définitive les structu
res des objets. Par exemple,
différentes entreprises peuvent avoir des besoins différents, voire qui s’excluent mutuellement pour un même bean Product. Si vous
n’avez pas défini proprement votre framework, vous serez amenés à passer de longues
heures à adapter vos structures de données,
ce qui inclut les classes, les tables des bases
de données , les DTD, et ce pour chaque nouveau client. Inutile de le dire, vous vous retrouverez rapidement avec un tas de versions
parallèles du même logiciel, complexifiant
toujours plus la maintenance. Comment être
sûr que le modèle choisi sera suffisamment
flexible pour satisfaire les différents besoins,
sans nécessiter de codage supplémentaire à
chaque fois que ces besoins évoluent ?
Cet article pose les fondations d’un framework simple et éprouvé, permettant de construire un système réellement adaptatif. Il est
basé sur une génération dynamique de code
source à partir de métadonnées d’une classe
maintenue dans un dictionnaire de données.
En cours de route, nous reverrons JDBC (Java
Database Conectivity) et JSP (JavaServer Pages), et nous verrons également comment
utiliser l’une des technologies les plus fascinantes liée à XML, XSLT (eXtensible Stylesheet
Language for Transformations).
Petite séance
de modélisation
Tout d’abord, mettons en place un projet
hypothétique où nous pourrons appliquer
toutes les magnifiques techniques que nous
allons voir ici. Ce projet sera une application
destinée à un marché vertical, utilisant la notion de produit. Vous pouvez l’adapter à votre secteur favori (services financiers, vente en
ligne, ou peut-être même formation technique), pourvu qu’il utilise un produit. L’architecture système n’est pas une contrainte non
plus ; cela peut aller d’une application isolée
à un système distribué multitiers. Dans cet
article, nous allons nous concentrer sur les
problèmes de modélisation d’objets, également applicables dans une grande variété de
projets.
Comme nous n’allons pas modéliser cette
application pour un client particulier, nous
devons nous assurer que nous connaissons
suffisamment le métier pour couvrir les cas
les plus complexes. Pour ce faire, nous engagerons les meilleurs experts dans le domaine,
lancerons quelques séances de CRC (Class
Responsability Collaboration "), dessinerons
cas d’utilisation, et finalement assemblerons
une vision précise du concept Produit. Avec
ces informations, nous pouvons construire un
modèle conceptuel de domaine métier qui
inclut notre bean Product parfait, décrit cidessous. Cela représente grosso modo les
étapes préalables au codage, particulièrement si l’on se contraint à un cycle de développement de qualité.
Product
code : int
name : String
testedOnAnimals : Boolean
availableSince : java.util.Date
Implémentation
traditionnelle
Maintenant, nous pouvons passer à la
phase de construction, où nous allons façonner le code Java du bean Product. En Java,
un bean est un composant réutilisable venant
du framework JavaBean ou Entreprise
JavaBean (EJB). En dépit des nombreuses différences entre ces deux types de beans, certaines caractéristiques les recoupent. Par
exemple, ils partagent la notion de propriété
de bean. Les propriétés de bean sont basées
sur les conventions de nommage standard de
Java pour les méthodes accesseurs. Dans le
cas où le bean à une propriété code non indexée, il est convenu qu’il implémente des
méthodes setCode() et getCode() respectivement pour l’écriture et la lecture. La méthode
de lecture pour un type booléen serait
isCode(). Pour une propriété indexée, on suppose que le bean possède deux jeux de méthodes setCode() et getCode(), l’un qui serait
indexé, et l’autre acceptant des tableaux. Le
java.beans.Introspector utilise les mêmes règles, appliquées à l’envers, pour analyser de
manière dynamique la classe bean et découvrir les jeux de méthodes exposées par le
bean, à la volée.
Le concept de propriété devient particulièrement pratique lorsque l’on a besoin de lier
les objets à une source de données, comme
une base de données ou un document XML.
De manière similaire, cela facilite également
la phase de présentation. Par exemple, l’API
JSP fourni un mécanisme pour lier les paramètres des requêtes HTTP aux propriétés du
bean. Les actions suivantes permettent d’économiser beaucoup de temps de codage in-
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
croyable en initialisant magiquement les propriétés du bean à partir des champs récupérés dans un formulaire HTML :
<jsp:setProperty name="myBean" property="*"/>
Pour schématiser, un bean n’est qu’une
classe Java qui se conforme à un ensemble
de règles bien définies. Nous allons utiliser le
type de bean le plus simple pour notre projet. Libre à vous d’appliquer par la suite ces
techniques aux composants EJB.
Nous pouvons maintenant dire que notre bean Product comporte une propriété
integer code, une propriété String nom, une
propriété boolean testedOnAnimals et une
propriété date availableSince. Il se traduit par
le code suivant :
Listing 1 : Product.java
public class Product {
private int code;
private String name;
private boolean testedOnAnimals;
private java.util.Date availableSince;
public Product() {}
public int getCode() {
return code;
}
public void setCode(int newCode) {
code = newCode;
}
public void setName(String newName) {
name = newName;
}
public String getName() {
return name;
}
public void setTestedOnAnimals(boolean
newTestedOnAnimals) {
testedOnAnimals = newTestedOnAnimals;
}
public boolean isTestedOnAnimals() {
return testedOnAnimals;
}
public void setAvailableSince(java.util.Date
newAvailableSince) {
availableSince = newAvailableSince;
}
}
25
Langage
public java.util.Date getAvailableSince() {
return availableSince;
}
Le pour
et le contre
Pour ce projet, nous avons choisi une approche simple, rapide, et peu coûteuse pour
créer le bean Product. On pourrait également l’étendre à l’EJB. Au final, nous avons
un système qui répondra avec un peu de
Pro duc t
code: int
name: String
testedOnAnimals: boolean
availableSince: java.util.Date
num01: double
num02: double
num03: double
bool01: boolean
bool02: boolean
bool03: boolean
str01: String
str02: String
str03: String
date01: Date
date02: Date
date03: Date
Figure 1
chance à tous les scénarios du domaine métier considéré.
Dans la plupart des cas, nous devrions
nous en sortir avec cette approche. Cependant, que va-t-il se passer si un client vient
malencontreusement avec une perspective
différente sur nos beans soigneusement modélisés ? Que se passe t’il s’il faut rajouter une
nouvelle propriété Boolean "y3kCompliant"
au bean Product ? Rappelez-vous, nous avons
besoin de modifier non seulement le code
source de ce bean particulier, mais également
toutes les structures de données pertinentes,
incluant les classes BeanInfo, les requêtes SQL
et peut-être même certaines classes qui appartiennent aux parties vue et contrôleur de
notre application. Pire encore pire, que se
passe t’il si le client demande la permission
de faire tout cela sans votre aide ? N’oubliez
pas que lorsque vous traitez une application
commerciale réelle, vous aurez au moins une
centaine d’objets différents. Alors que se
passe t’il s’il faut adapter chaque objet pour
chaque nouveau client ? En y regardant de
plus près, ce qui ressemblait à une solution
simple et rapide se révèle être un véritable
goulet d’étranglement.
Une solution classique consiste à ajouter
des propriétés supplémentaires à chaque
objet, réservées spécifiquement aux besoins
du client. Essayons cette tactique, et ajoutons
à notre classe Product trois propriétés supplémentaires pour quelques types primitifs, ainsi
que pour les types Date et String (figure 1).
Parfois, cela résout le problème, mais très
franchement cela n’est ni très propre, ni très
confortable. Mon expérience dans le développement logiciel montre que le manque de
confort est généralement signe d’une mauvaise conception. Et de toute manière, comment pouvons-nous être certains que trois
propriétés supplémentaires par type seront
suffisantes ? De plus, certains clients n’auront
jamais besoin de ces propriétés supplémentaires, et l’espace gâché dans les tables a un
coût. N’est il pas possible de créer un système
qui s’adapte facilement aux différents besoins
des clients ?
La génération de code
à la rescousse
Le problème auquel nous sommes confrontés est semblable au fait que le HTML statique ne peut plus satisfaire les besoins des
entreprises. Dans ce cas, les développeurs
d’applications Web utilisent diverses technologies pour fournir un contenu où les données
sont générées dynamiquement. Nous avons
besoin d’un mécanisme similaire, mais qui ,au
lieu de générer du HTML, va générer du code
source Java pour nos beans. Désormais, cela
devient notre objectif principal.
Un fois que nous aurons trouvé une solution adéquate, elle pourra être étendue à toutes les autres structures de données nécessaires à l’adaptation de l’application. En supposant que vous ayez déjà un framework
applicatif en place, vous devrez être capable
d’y placer du nouveau code, sans pour autant
nécessiter de codage supplémentaire. Si cela
est possible, vos clients pourront alors réaliser ces modifications par eux-mêmes, à condition de leur fournir, bien entendu, une belle
interface graphique pour le faire.
Pour bénéficier de ce processus de génération automatique du code, il nous faut lui
fournir une description détaillée des beans.
En d’autres termes, nous avons besoin d’un
repository que nous pourrons interroger pour
récupérer les métadonnées des classes. C’est
similaire à l’idée des métadonnées d’une base
de données que l’on peut obtenir en utilisant
la méthode getMetadata() de l’interface
java.sql.Connection de l’API JDBC. Dans le
monde des SGBDR, de telles informations
sont généralement stockées dans un dictionnaire de données, un jeu de tables réservées
au système qui décrit les structures des bases de données.
Dans le monde de la modélisation et de
l’analyse orientée objet, on utilise souvent
UML pour exprimer les métadonnées des classes. Un grand nombre d’outils de modélisation UML fournissent des moyens de maintenir synchronisés le code source et le modèle
UML. Bien que j’aime garder la synchronisation entre le modèle UML et le code source,
je ne suis pas satisfait par le niveau de contrôle qu’offrent ces outils au niveau de la génération du code.
L’autre langage utilisé pour les métadonnées est XML. Il comporte plusieurs fonctionnalités qui le rendent particulièrement attractif pour cette tâche :
• XML est indépendant de la plate-forme et
de l’application
• Vous pouvez facilement valider des documents XML avec une DTD ou un schéma
• Les processeurs de feuilles de styles XSLT
permettent de transformer des documents
XML dans n’importe quel autre format, y
compris du code source Java, Smalltalk, ou
même C#
En fait, le format XML Metadata
Interchange (XMI) développé par IBM, Unisys,
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
26
Langage
Application
Parser XSLT
Récupère le code source
Java du bean
Serveur métadonnées XML
Récupère les métadonnées
XML pour le bean
Dictionnaire de données
Récupère les résultats en
fonction des métadonnées
pour le bean
Données résultats
Document XML
Code source Java
Figure 2. Diagramme de séquence
Oracle et d’autres leaders de l’industrie, est un
langage pour l’échange des métadonnées
entre les outils de modélisation, ainsi qu’entre les outils et les entrepôts de métadonnées.
Nous pourrions l’utiliser ici, mais cela demanderait certainement un autre article pour introduire le langage. De plus, XMI est un peu
trop sophistiqué pour la tâche que l’on désire
réaliser. Cependant, gardez à l’esprit XMI
quand vous serez amenés à travailler sur des
projets plus évolués. Toutes les techniques
que nous allons détailler dans cet article seront également applicables si vous décidez
d’utiliser XMI.
Plan
de bataille
Notre solution va combiner le meilleur
des techniques existantes. Nous allons maintenir les métadonnées des classes dans un
simple dictionnaire de données que nous
allons créer. Une base de donnée relationnelle est probablement l’outil de choix pour
cette tâche :
• Elle offre un stockage sécurisé et fiable des
données
• Il est facile de créer une interface utilisateur
pour manipuler les données
• Il n’y a aucune contrainte technologique
pour les outils d’extraction de données
• Elle est de toute façon utilisée dans la plupart des projets
Une fois que nous aurons les métadonnées dans le dictionnaire de données, nous
pourrons utiliser le serveur de métadonnées
XML pour extraire dynamiquement les informations et les présenter sous forme d’un document XML sur une requête HTTP. En utilisant HTTP, nous enlevons toutes les contrain-
tes technologiques sur l’implémentation du
serveur de métadonnées XML, bien que je
préfère utiliser JSP ou l’API Servlet pour créer
un serveur de métadonnées.
Dès que nous recevons les métadonnées
de la classe du document XML, nous allons
utiliser un processeur XSLT générique pour
transformer le document en code source Java.
En plus des métadonnées, nous avons également besoin d’une feuille de style XSLT, qui
inclut les instructions nécessaires au processeur XSLT pour réaliser la magie de la transformation.
Une fois que nous avons tous ces morceaux en place, il ne nous reste plus qu’à adapter une classe bean en modifiant quelques
champs du dictionnaire de données, à envoyer une requête HTTP et à récupérer la nouvelle classe Java. N’importe quel client peut
le faire, étant donné qu’aucune programmation n’est nécessaire.
Le diagramme de séquence ci-dessus
vous aidera à visualiser le déroulement des
opérations (figure 2).
Créer
un dictionnaire de données
Il n’existe pas de schéma standard de dictionnaire de données que nous pourrions utiliser pour notre projet. Chaque équipe en crée
un qui répond au mieux à ses besoins. Mettons
en place un dictionnaire de données relativement simple. Nous reviendrons par la suite sur
la manière de le rendre plus sophistiqué, et plus
adapté à un projet d'envergure commerciale.
Nous n’avons besoin que de deux tables.
La table Beans contient les informations liées
à la classe bean, et la table Properties décrit
leurs propriétés. Vous trouverez ci-dessous les
données nécessaires à la création de ces tables, dans la base de données de votre choix :
DROP TABLE Beans;
CREATE TABLE Beans (id INT,name VARCHAR(30) NOT
NULL,comments
VARCHAR(100)); DROP TABLE Properties;
CREATE TABLE Properties (beanId INT,name
VARCHAR(30) NOT NULL,type
VARCHAR(30) NOT NULL,comments VARCHAR(100));
Bien que je vous conseille d’utiliser JDBC
pour créer ces tables, vous êtes libres d’utiliser n’importe quel outil pour entrer les données. C’est d’ailleurs peut être le bon moment
pour réfléchir au type d’outils que vous allez
fournir à vos clients pour ce même besoin.
Voyons les métadonnées à insérer dans notre dictionnaire de données pour notre bean
Product :
INSERT INTO Beans (id,name,comments) VALUES
(1,"Product","This bean represents a product that
the company offers to its customers");
INSERT INTO Properties
(beanId,name,type,comments) VALUES
(1,"code","int","the product inventory code");
INSERT INTO Properties
(beanId,name,type,comments) VALUES
(1,"name","String","the product name");
INSERT INTO Properties
(beanId,name,type,comments) VALUES
(1,"testedOnAnimals","boolean","the flag that
indicates if the product was tested on animals");
INSERT INTO Properties
(beanId,name,type,comments) VALUES
(1,"availableSince","java.util.Date","the date when
the company started
offering this product to its customers");
Comme mentionné précédemment, c’est
probablement l’information minimale qu’il est
nécessaire de maintenir si l’on veut que nos
métadonnées soient utiles. On pourrait certai-
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
27
Langage
Figure 3. Table 1 : Beans.
Figure 4. Table 2 : Properties.
nement survivre sans les commentaires, cependant le code non commenté est considéré
comme non professionnel dans le monde de
la programmation.
Les tables 1 et 2 montrent ce dont nous
avons besoin (figures 3 et 4).
Dans la vraie vie, vous chercherez probablement à réduire la taille de ces tables. Vous
pouvez également ajouter d’autres données
que vous jugerez plus intéressantes dans vos
métadonnées. Les API Java fournissent quelques bonnes suggestions du côté de
java.lang.Class et certaines classes du package java.lang.reflect. Il y a deux autres classes utiles dans le package java.beans. En fait,
la classe java.beans.BeanDescriptor
encapsule les métadonnées du bean, alors
que la classe java.beans.PropertyDescriptor
fait la même chose pour une propriété de
bean. Si vous n’êtes pas satisfait par le modèle de métadonnées offert, ces classes pour
permettent de l’étendre en associant des attributs nommés aux entités. Cela est particulièrement pratique pour donner des contraintes sur les valeurs acceptables pour les
propriétés des beans.
En gardant cela à l’esprit, il pourrait être
utile d’ajouter d’autres champs dans la structure métadonnées de votre application commerciale :
Table Beans :
·
·
·
·
·
·
·
·
·
package
imports
modifiers
isAbstract
isInterface
superClass
implements
displayName
description
Table Properties :
·
·
·
·
·
·
·
·
·
·
·
·
modifiers
isAbstract
isIndexed
isHidden
isLookup
lookupTableName
isLowercaseAllowed
isPassword
isRequired
isReadonly
defaultValue
maxValue
·
·
·
·
·
minValue
maxLength
displayName
description
displayFormat
Il peut être également utile de prévoir le
problème du contrôle de version des métadonnées en ajoutant des champs supplémentaires projet et version à chaque table.
Décrire les métadonnées
en XML
Avant d’extraire les métadonnées du document XML, il faut que nous nous mettions
d’accord sur sa structure. Pour cela, nous pouvons utiliser un langage existant parmi les
DTD et les XML Schemas.
Les DTD peuvent sembler démodées, particulièrement à cause de leurs possibilités de
typage très réduites. Cependant, les règles
ainsi définies sont simples, compactes, et lisibles directement par l’œil humain. C’est sûrement pour cela que les DTD restent si populaires. De l’autre côté, les Schemas XML offrent
des possibilités plus avancées, comme l’héritage et les typages forts, au détriment de la
simplicité. Pour nos besoins, un DTD est amplement suffisant. Voici comment nous allons
structurer notre document de métadonnées :
<!ELEMENT bean (name,property*,comments?)>
<!ELEMENT property (name,type,comments?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT comments EMPTY>
Cela veut dire qu’un élément <bean> doit
avoir exactement un fils <name>, et de manière optionnelle, n’importe quel nombre
d’éléments <property>. Un élément
<property> à son tour doit avoir exactement
un <name> et un élément <type>. Les éléments <bean> et <property> peuvent avoir
de manière optionnelle un élément
<comments>.
Nous devons maintenant créer un serveur de métadonnées XML qui effectue les
opérations suivantes :
• Extraire les métadonnées à partir du dictionnaire de données
• Générer un document XML conformément
à la DTD précédemment spécifiée
• Communiquer avec les clients au travers du
protocole HTTP
Nous utiliserons des JSP pour créer ce serveur de métadonnées XML. Ce serveur accède
à la base de données directement à partir de la
JSP en utilisant les API JDBC 2.0, fournies avec la
plate-forme Java 2 Standard Edition (J2SE). Notez cependant que ce n’est pas une approche
recommandable. Je l’ai choisie pour que nous
restions focalisés sur les fonctionnalités et pour
nous éviter d'être distraits par des problèmes
de modélisation d’application Web complexes.
Conformément aux Java BluePrints, on doit
créer des JSP avec le moins de code Java exposé
possible. Normalement, on implémenterait cela
au travers d’une servlet, ou au moins avec un
tag JSP spécifique comme par exemple ceux du
projet Jakarta Taglibs pour l’accès aux bases de
données. Nous allons nous concentrer sur l’algorithme de base, pour que vous puissiez tester cela sur votre machine avec un minimum
d’effort.
Pour ouvrir une connexion à une base de
données, la classe java.sql.DriverManager a
besoin des informations suivantes :
• Le nom de la classe d’un pilote JDBC
• L’URL de la base de données
• Le login (optionnel)
• Le password (optionnel)
Nous envoyons ces informations dans les
paramètres de la requête HTTP. Un autre paramètre essentiel est le nom du bean dont
nous extrayons les métadonnées. Le listing 2
présente le code de cette JSP résultante.
Pour que cette JSP fonctionne, il faut
qu’elle soit déployée sur un serveur Web disposant d’un moteur JSP/Java Servlet. Ces derniers temps, il est très dur trouver un serveur
Web ne supportant pas la technologie Java.
Si vous n’en avez pas encore un, c’est le moment. Jettez un œil sur ces logiciels libres qui
offrent un support complet des JSP et des
servlets :
• Le containeur de servlet Tomcat, développé
par l’Apache Software Foundation, est l’implémentation officielle des technologies Java
servlet et JavaServer Pages. Tomcat est un
sous projet du projet Jakarta.
• iPlanet Web Server, FastTrack Edition est
une joint venture entre Netscape et Sun.
Après que Sun ait annoncé la fin de Java Web
Server 2.0 le 7 février 2001, la société a redirigé
ses utilisateurs vers le serveur iPlanet. Celuici est particulièrement appréciable du fait de
son interface Web intuitive d’administration
à distance.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
28
Langage
Une fois que vous avez déployé le
GenXml.jsp sur le serveur Web, vous devriez
pouvoir effectuer une requête en utilisant le
format suivant :
http://<myserver>/<mypath>/
GenXml.jsp?driver=<jdbcdriver>&url=<dburl>
&username=<dbuser>&password=<dbpwd>
&bean=Product
Vous devez substituer les variables entre
chevrons par les valeurs réelles. N’oubliez pas
d’utiliser "&" à la place de "&", en spécifiant le paramètre url. Si vous faites tout cela
correctement, vous devriez recevoir une réponse, qui dans le cas de notre bean Product
donne le code XML suivant :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<bean>
<name>Product</name>
<comments>This bean represents a product that
the
company offers to its customers</comments>
<property>
<name>code</name>
<type>int</type>
<comments>the product inventory code
</comments>
</property>
<property>
<name>name</name>
<type>String</type>
<comments>the product name</comments>
</property>
<property>
<name>testedOnAnimals</name>
<type>boolean</type>
<comments>the flag that indicates if the
product was
tested on animals</comments>
</property>
<property>
<name>availableSince</name>
<type>java.util.Date</type>
<comments>the date when the company
started
offering this product to its customers</
comments>
</property>
</bean>
Si votre requête renvoie une exception,
c’est sûrement dû à des valeurs de paramètres invalides. Dans ce cas, le document XML
généré comporte un élément <exception>.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<exception>No metadata found for the bean
Product
</exception>
Transformer le XML
en code Java
Avant que XSLT deviennent une merveilleuse réalité, il faut que vous transformiez
le XML en Java de cette manière : réaliser à la
main une classe qui utilise un parser DOM ou
SAX pour extraire les données du document
XML source, lui appliquer des règles, et éventuellement ressortir le code en Java. Chaque
fois que vous avez à changer des règles, vous
aurez besoin de modifier et de recompiler la
classe. Si vous décidez de le transformer en
un autre langage, par exemple Smalltalk, il
vous faut créer une autre classe uniquement
pour cela. C’est faisable, bien entendu, mais
ce n’est pas particulièrement réjouissant, surtout quand on connaît XSLT.
XSLT est une recommandation officielle
du W3C. Elle permet de transformer un document XML en a peu près n’importe quoi
d’autre. Cependant, on l’utilise traditionnellement pour transformer en HTML, en texte
ou en un autre type de document XML. Le
processeur XSLT effectue la transformation
conformément aux règles définies dans la
feuille de style XSLT, qui elle-même est un
document XML. Si vous désirez changer la
logique de transformation, vous n’avez qu’à
modifier la feuille de style avec un éditeur
de texte.
On trouve différentes implémentations
de processeurs XSLT de nos jours, vous pouvez en avoir un aperçu en consultant le site
du W3C. Nous utiliserons le processeur XSLT
Xalan, développé pour l’Apache XML project.
Puisque c’est une implémentation Java, Xalan
offre des fonctionnalités particulièrement attractives pour les développeurs Java. On retrouve par exemple le support de TRaX (Transformation API for XML), qui fait partie de la
Java API for XML Processing (JAXP). TRaX vous
aide à construire des applications qui utilisent
les transformations XSLT au travers d’une interface standard.
Autre particularité, le compilateur XSLT, un
outil qui compile la logique de transformation
d’une feuille de style en une classe Java
appellée translet. Vous pourrez alors utiliser le
runtime XSLT de Sun pour appliquer le translet
au document XML, et augmenter les performances de manière significative.
Le listing 3 définit une feuille de style pour
transformer nos métadonnées en code source
Java. Si vous décidez par la suite d’étendre
votre structures de métadonnées, vous devrez effectuer les changements également
dans la feuille de style.
Listing 3 : JavaBean.xsl
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/
Transform"
version="1.0"
xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="java">
<xsl:output method = "text"/>
<xsl:template match="bean">
/**
* <xsl:value-of select="comments"/>
* This class has been generated by the XSLT
processor
* from the metadata
*/
public class <xsl:value-of select="name"/> {
/**
* Creates a new instance of the <xsl:value-of
* select="name"/> bean
*/
public <xsl:value-of select="name"/>() {}
<xsl:apply-templates select="property"/>
}
</xsl:template>
<xsl:template match="property">
private <xsl:value-of select="type"/>
<xsl:text> </xsl:text>
<xsl:value-of select="name"/>;
<xsl:variable name="name" select="name"/>
<xsl:variable name="cname"
select="java:Capitalizer.capitalize($name)"/>
/**
* Sets <xsl:value-of select="comments"/>
* @param <xsl:value-of select="name"/> is
<xsl:value-of select="comments"/>
*/
public void set<xsl:value-of select="$cname"/>(
<xsl:value-of select="type"/> <xsl:text>
</xsl:text><xsl:value-of select="name"/>) {
this.<xsl:value-of select="name"/> =
<xsl:value-of select="name"/>;
}
/**
* Returns <xsl:value-of select="comments"/>
* @return <xsl:value-of select="comments"/>
*/
public <xsl:value-of select="type"/>
<xsl:text></xsl:text>
<xsl:apply-templates select="type"/
><xsl:value-of
select="$cname"/>() {
return <xsl:value-of select="name"/>;
}
</xsl:template>
<xsl:template match="type">
<xsl:variable name="type" select="."/>
<xsl:choose>
<xsl:when test="$type=’boolean’">is</
xsl:when>
<xsl:otherwise>get</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XSL, le langage utilisé pour les feuilles de
styles XSLT, requiert pas mal de temps et d’effort pour être assimilé. La feuille de style que
nous venons de définir n'est peut-être pas très
parlante pour vous. Voici donc quelques explications sur son fonctionnement.
Il ya trois templates dans cette feuille de
style. Le premier template s’occupe de l’élément <bean>. C’est ici que nous fournissons
la définition de la classe bean, ainsi que le
constructeur par défaut.
Le deuxième template s’occupe de l’élément <property>. Notez que l’on utilise ici les
extensions Xalan-Java pour initialiser la variable XSL appelée cname. Cette variable contient le nom de la propriété, avec le premier
caractère en majuscule. Cela sert à générer le
nom des méthodes accesseur. Puisque XSLT
ne permet pas cela directement, nous avons
du créer une petite classe Java, Capitalizer, qui
met en majuscule le premier caractère d’une
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
chaîne. Cette classe doit être compilée et mise
à disposition du processeur XSLT via le
CLASSPATH, au moment de la transformation :
Listing 4. Capitalizer.java
/**
* This class provides an XSLT extension function
that
* may be utilized by Xalan-Java extension
mechanism.
*/
public class Capitalizer {
+
}
/**
* This method capitalizes the first character
* in the provided string.
* @return modified string
*/
public static String capitalize(String str) {
return Character.toUpperCase(str.charAt(0))
}
29
Langage
str.substring(1);
Notez que l’idée d’étendre XSLT n’est pas
uniquement lié à Xalan. En fait, cela fait partie de la recommandation XSLT.
Enfin, un troisième template décide du
nom de la méthode d’accès en lecture. Rappelez-vous, elle doit avoir pour préfixe is pour
les propriétés booléennes, et get pour toutes
les autres. Avant de continuer, il faut savoir
que XSLT est suffisamment riche pour vous
laisser accomplir cette transformation de différentes façons. Par exemple, au lieu d’utiliser un template séparé, on pourrait utiliser
l’élément <xsl:for-each select="property">
pour s’occuper des éléments <property>.
Nous avons maintenant tous les ingrédients pour créer notre bean Product. Une fois
que vous aurez installé et configuré Xalan,
vous pourrez effectuer la transformation avec
une ligne de commande de ce genre :
java org.apache.xalan.xslt.Process -in xmlSource -xsl
stylesheet -out outputfile
Le paramètre obligatoire –in doit être suivi
de la location du document XML source. Vous
pouvez le faire pointer vers un fichier local,
ou vers une URL.
Le paramètre optionnel –xsl pointe vers
la feuille de style XSLT. Notez que ce paramètre est optionnel, puisqu’il est possible de
spécifier directement l’URL de la feuille de
style XSLT dans le document XML source via
l’instruction PI. Je ne vous le recommande
pas. En découplant ces deux documents,
vous gagnerez en flexibilité. Vous pouvez par
exemple décider dans le futur de créer une
autre feuille de style XSLT pour générer un
IDL (Interface Definition Language) pour votre application Corba.
Le paramètre optionnel –out spécifie le
nom du fichier de sortie pour l'écriture du
code source de la classe Java. Si vous omettez ce paramètre, le résultat de la transformation apparaîtra alors à l’écran.
Si vous avez configuré correctement votre CLASSEPATH, le listing 5 Product.java cicontre vous montre ce que vous devriez obtenir à la sortie de la transformation.
C’est le résultat que nous attendions, le
code source se compile très bien, je suis certain que le compilateur ne se rend pas compte
des efforts nécessaires pour y arriver.
avec succès à plusieurs projets commerciaux.
Le domaine des services financiers, où je travaille actuellement, travaille sur de grandes
quantités d’objets volumineux. Chaque nouveau client nécessite une modification importante de ces objets. Sans un tel framework,
notre équipe n’aurait pas été capable de tenir les délais impartis, généralement très serrés. Je la partage avec vous parce qu’elle est
très efficace, qu’elle permet d’économiser
beaucoup de temps et d’argent et qu’elle utilise des technologies parmi les plus fascinantes disponibles de nos jours.
z
Victor Okunev est Sun Certified Java Programmer et
Microsoft Systems Engineer. Avec 11 ans d’expérience dans le développement, il est architecte dans
l’équipe R&D de Malborough Stirling Plexus,
Vancouver.
Traduction Guillaume Louel. "Generate JavaBean
classes dynamically with XSLT" par Victor Aokunev,
publié par JavaWorld, copyright IDG.net, 2002.
Traduit et republié avec permission.
http://www.javaworld.com/javaworld/jw-02-2002/
jw-0201-xslt.html?
Et maintenant ?
Une fois satisfait de la transformation,
vous pouvez créer une JSP ou une servlet qui
effectue cela via l’API TRaX ou en utilisant la
XSL Tag Library, qui fait partie de la collection
de tags Apache Jakarta Taglibs. Vous pourrez
alors envoyer la requête via HTTP, recevoir le
code Java, et le compiler dynamiquement en
utilisant la classe java.lang.Compiler. Vous
pouvez utiliser la même technique pour produire la classe BeanInfo, différentes classes de
support, des requêtes SQL, des DTD, tout ce
qui accompagne la classe bean que vous générez. Afin d’insérer ces classes fraîchement
compilées à votre application, vous pouvez
utiliser l’API Reflection, qui découvre de manière dynamique leur structure.
Un framework
qui marche
Tout ce qui à été décrit dans cet article a
servi de fondation à un framework appliqué
ALLER PLUS LOIN
La recommandation du W3C pour XSLT 1.0
http://www.w3.org/TR/xslt
Xalan, processeur XSLT Java de l’Apache XML Project
http://xml.apache.org/xalan-j/index.html
Le standard XMI
http://www-4.ibm.com/software/ad/library/standards/
xmi.html
Le container de servlet Tomcat
http://jakarta.apache.org/tomcat/index.html
iPlanet Web Server Fast Track Edition
http://www.iplanet.com/products/iplanet_web_fasttrack/
home_2_1_1l.html
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
30
Langage
DANS CETTE SUITE D’ARTICLES, NOUS PRÉSENTERONS L’ÉTAT DE L’ART DES SOLUTIONS JAVA OPEN SOURCE
DANS LE CADRE D’UNE APPLICATION WEB. NOTRE SÉLECTION DES PROJETS MONTRE QUE,
POUR LA CONSTRUCTION D’UN SYSTÈME D’INFORMATION DISTRIBUÉ POUR L’ENTREPRISE,
CES SOLUTIONS N’ONT RIEN À ENVIER À UN SYSTÈME COMMERCIAL.
P
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
rès de mille projets
Open
Source
ayant un rapport
avec Java font ac
tuellement partie
de sourceforge.org. Ce site n’hébergeant qu’une partie des projets Open Source, il est possible
d’estimer à plusieurs milliers les
projets Java Open Source. Dans
ces conditions, il est difficile de
faire une sélection. Notre objectif est de présenter les projets
Open Source qui semblent les
plus prometteurs.
Pourquoi utiliser des logiciels
Figure 1. L'architecture proposée.
issus de projets Open Source
alors qu’il existe une grande
quantité de produits commerciaux disponibles ? La première
raison est sans doute financière :
les logiciels Open Source sont
gratuits. Il est ainsi possible de
tester une architecture sans achat
de licence. La deuxième est technique, le code source des logiciels
Open Source étant disponible, un
développeur peut comprendre le
fonctionnement des logiciels,
corriger des bogues, ou encore
procéder à des évolutions. Le fait
qu’un logiciel soit Open Source
ne signifie pas qu’il soit de mauvaise qualité, au contraire ; beaucoup de meneurs des projets
Open Source sont des personnes
très compétentes qui s’investissent par défi technique et pour
la reconnaissance de leurs pairs.
D’autres arguments plus marketing laissent entendre que les logiciels Open Source sont plus stables, que le respect des standards
et la qualité du support sont
meilleurs. Ces avantages ont cependant un prix, la documentation associée aux projets Open
Source est souvent très succincte
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
31
Langage
et il est parfois difficile pour un
débutant d’installer ces logiciels
et de les utiliser. Ce sont aussi les
besoins de notre logiciel (niveau
d’API) et le niveau de maturité de
la solution Open Source qui vont
guider notre choix. Nos critères
de sélection sont le niveau d’API,
la maturité, les évolutions et le
leadership (voir tableau en fin
d’article).
Choix d’une architecture
Open Source
Nous présentons des solutions qui répondent à différents
besoins d’architecture :
• construction d’une application
Web constituée de plusieurs
centaines types d’écrans ;
• publication de l’information
dans plusieurs formats (HTML,
XML, WML, PDF…) ;
• accès à une base relationnelle
ou envoi de messages JMS.
Une histoire
de langage
• HTML (HyperText
Markup Language) est un
langage qui permet de
décrire de manière graphique un document. Il ne
permet pas d’isoler la
donnée qu’il représente.
• SGML (Standard
Generalized Markup
Language) est l’origine de
tous les langages à balise.
C’est un standard de l’ISO.
• XML (eXtended Markup
Language) est une version
légère de SGML. Ce n’est pas
un langage, mais une famille
de langages. La plupart des
langages basés sur XML
permettent de décrire des
données. La description d’un
langage XML est faite à l’aide
d’un DTD (Document Type
Definition).
• XSLT (eXtensible
Stylesheet Language
Transformation) est un
langage qui autorise la
transformation d’un
document XML en un autre
document XML ou non.
• XSL FO (eXtensible
Stylesheet Language
Formatting Objects) est un
langage de description de
texte 2D.
Le paradigme MVC
Dans le paradigme MVC (Modèle-Vue-Contrôleur), le modèle
représente les données manipulées par l’application (l’adresse
d’un client, son nom, une facture…). Les données sont représentées par des classes Java. La vue représente l’interface utilisateur
de l’application Web. Elle est construite avec des JSP. Le contrôleur est la colle entre les requêtes http qui arrivent sur l’application Web et les pages qui sont renvoyées à l’utilisateur.
L’architecture est composée :
• d’un moteur de servlet/JSP,
pour le traitement des requêtes en provenance d’un navigateur ;
• d’un ordonnanceur, pour la
gestion de la navigation dans
le site Web ;
• d’un parseur XML, d’un processeur XSLT pour la manipulation
des documents XML ;
• de formateurs, pour l’envoi des
documents vers les médias
(navigateurs Internet, téléphones portables, imprimantes…) ;
• d’un conteneur d’EJB ou d’un
mappeur objet relationnel,
pour l’accès aux informations
stockées en base ;
• d’une base de données, pour
le stockage des informations.
Moteur de servlet/JSP
et ordonnanceur
Un moteur de servlet/JSP et
un ordonnanceur formeront le
cœur de l’architecture du système Web d’entreprise proposé.
Le rôle d’un moteur de servlet/
JSP est de transformer les requêtes HTTP qu’il reçoit en documents HTML. Cette technologie
est choisie pour sa robustesse et
son élégance. Il est préférable
d’écarter une architecture basée
uniquement sur des langages de
scripts (JSP, ASP ou PHP) car cela
pose des problèmes de robustesse et de maintenabilité ; ces
technologies sont adaptées au
développement de “petits” sites
– avec un nombre restreint de
types de pages dynamiques –,
mais elles s’avèrent limitées pour
des applications Web ayant plusieurs dizaines de types de pages
dynamiques. Allier la force de
Java, pour la partie programmation d’un site (à travers les
servlets), à la force des langages
de scripts (à travers les JSP), pour
la partie présentation, est la solution la plus évolutive. Parmi les
différents moteurs de servlet/JSP
existants, nous avons retenu
Tomcat pour notre architecture.
Il s’agit de l’implémentation de
référence des spécifications
servlet 2.2 et des spécifications
JSP 1.1. Tomcat fait partie du projet Apache Jakarta.
Pour les applications Web
constituées de plusieurs centaines de types de pages, il est nécessaire de recourir à un
ordonnanceur, qui fera le lien entre les requêtes HTTP, les données du système d’information et
les pages renvoyées aux terminaux (navigateurs, PDA, téléphones…). L’objectif de cet outil est
d’apporter de la méthode dans le
développement de l’application.
Il simplifie le traitement des formulaires, automatise la gestion
des erreurs, apporte un support
pour la gestion des habilitations… Il amène aussi de la lisibilité sur une grosse application en
contraignant tous les développeurs à travailler de la même manière. Les architectures à base
d’ordonnanceur sont généralement rassemblées sous le terme
“Model 2”. Un bon ordonnanceur
peut entraîner des gains en productivité très importants (de l’ordre de X2 à X3). Il est, de plus,
réutilisable car complètement
détaché des aspects fonctionnels. Deux ordonnanceurs libres
ont vu le jour : Struts et Tapestry.
Struts est composé d’un ensemble de classes Java, de bibliothèques de tags JSP et de servlets
qui répondent à tous les besoins
du paradigme Modèle-Vue-Contrôleur (voir encadré Le paradigme MVC) de Smalltalk. Après
un an de développement, Struts
a atteint un bon niveau de maturité. Une version stable devrait
être disponible prochainement.
Tapestry est un autre
ordonnanceur respectant le paradigme MVC. Il est beaucoup
plus ambitieux dans son approche objet que Struts. Cependant,
deux points peuvent limiter
l’adoption de Tapestry. D’abord,
celui-ci est en concurrence avec
Struts, qui fait partie du projet
Apache. Or, les projets Apache
ont tendance à monopoliser
toute l’attention des utilisateurs.
Sachant que la qualité des projets
Open Source est apportée par le
nombre d’utilisateurs, on peut
penser que Struts sera de
meilleure qualité que Tapestry
(moins de bogues, plus de supports...). Ensuite, Tapestry n’est
pas compatible J2EE, il apporte sa
propre technologie de scripting
qui n’est pas compatible avec les
JSP. C’est pour ces raisons que
Struts a été préféré à Tapestry
dans cet article. Il est toutefois
recommandé aux architectes objet de consulter le projet
Tapestry : son architecture est
très riche d’enseignement.
Publication Web
La combinaison de Tomcat et
Struts permet de développer des
applications capables de servir
plusieurs centaines de types
d’écrans différents. En revanche,
ces logiciels ne sont pas capables
de diffuser de l’information pour
plusieurs types de médias. Pour
apporter cette fonctionnalité,
nous nous appuierons sur
Cocoon, un logiciel spécialisé
Figure 2. Intégration de XML,
XSLT, Cocoon et FOP
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
32
Langage
dans la publication. Ainsi, à partir du même contenu, on peut
diffuser de l’information dans
plusieurs formats : HTML, XHTML,
XML, WML, PDF…
Cocoon est né des idées de
Stefano Mazzocchi, qui avait en
charge la gestion de la documentation du projet Java.apache.org.
Celui-ci est en fait une agrégation
de sous-projets, dont chacun est
géré par un ou plusieurs développeurs se trouvant dans des lieux
distants. Rapidement, Stefano
Mazzocchi a été confronté à des
problèmes de cohérences globales lors des mises à jour. Ce qui l’a
poussé à développer un outil qui
lui permette d’administrer de
manière plus simple l’ensemble
de la documentation du projet
Java.apache.org. Son idée ? Séparer le contenu de son formatage
(dans le cas du projet Apache, le
contenu est la description du
code source des sous-projets et
le formatage est en HTML).
L’architecture de Cocoon repose entièrement sur les standards XML et XSLT du W3C. Elle
comporte trois étages : un producteur, un processeur et un formateur. La requête d’un utilisateur
traverse successivement tous les
étages. Le producteur (dans notre
cas, le noyau Tomcat/Struts) a en
charge la génération du document XML. Le processeur transforme le document généré. Et le
formateur a la responsabilité de
produire du code prêt à être renvoyé au client. Comme on le voit,
Cocoon n’est pas là pour simplifier
le développement de sites Web,
mais pour augmenter les possibilités de publication d’un site.
FOP (the Formatting Objects
to PDF) est sans doute le formateur
lié au projet Cocoon le plus intéressant. Comme son nom l’indique, il
transforme un document XSL FO
en PDF. Il permet donc d’ajouter,
via l’utilisation d’un plug-in
Acrobat Reader sur le client, des
possibilités d’impression de qualité
à une architecture Web.
Mapping
Dans l’état actuel, l’architecture Web ne résout pas la problématique de l’accès aux données.
Pour cela, il existe plusieurs approches. Soit l’accès aux données
se fait via des mappeurs, logiciels
permettant d’avoir une représentation objet d’une base relationnelle, soit l’accès s’effectue à l’aide
de la technologie EJB.
Le mappeur offre deux services de base, la persistance et la
réification, qui permettent respectivement de stocker et de lire
les informations stockées dans
une base relationnelle. Certains
mappeurs très évolués proposent des fonctionnalités de cache, de gestion des accès concurrents et distribuée…
Un mappeur objet-relationnel
assure la relation entre deux mondes : les bases relationnelles qui
sont des lieux de stockage traditionnels pour les données d’une
application, et les objets qui permettent à une application Java de
manipuler des données stockées
dans une base relationnelle.
Castor est sans aucun doute
le mappeur Open Source le plus
prometteur. Très simple à mettre
en œuvre, cet outil est capable via
des fichiers de description XML
de donner une représentation
objet, manipulable à travers Java,
non seulement d’une base de
données relationnelle mais aussi
d’un annuaire LDAP ou d’un document XML.
La deuxième solution pour
l’accès aux données est de passer par une couche de composants EJB (Enterprise JavaBeans).
Un EJB est un composant
réutilisable qui est conçu pour
être installé dans un serveur d’applications compatibles J2EE. Le
but de cette technologie est de
réduire les coûts de développement des applications. But atteint
grâce à l’utilisation de conteneurs
EJB, qui sont des logiciels comportant tout un ensemble de services réutilisables comme la gestion des accès concurrents, la
gestion de pool d’objets pour
augmenter les performances
d’allocation (instanciation), la
gestion des transactions distribuées sur plusieurs bases, la gestion de la distribution des EJB sur
plusieurs machines physiques
sans modification des règles de
développement.
Il existe plusieurs types d’EJB
: les EJB Session, stateless ou
statefull, qui sont utilisés pour
représenter l’aspect dynamique
des données, et les EJB Entity,
BMP ou CMP, qui sont mis en
œuvre pour représenter l’aspect
persistant des données. Un serveur d’applications J2EE est jugé
sur la manière dont il gère les EJB
Entity CMP.
Il existe trois projets Open
Source de serveurs EJB : JBoss, Jonas (projet de Bull) et openEJB. Il
est encore difficile de dire lequel
prendra le dessus. JBoss semble
cependant le plus avancé.
Middleware orienté
messages
Pour que notre architecture
Web soit complète, il est nécessaire d’y intégrer un middleware
orienté messages (MOM). Sun
propose l’API JMS (Java Message
Services), qui autorise le dialogue
des applications interentreprises
(pour le B to B) ou intraentreprises (pour l’EAI). La communication entre les applications se fait
par l’envoi de messages synchrones ou asynchrones. Il existe actuellement quatre projets Open
Source qui proposent une implémentation de l’API JMS :
ObjectEvents, openJMS, Joram et
spyderMQ.
Base de données
L’architecture Web proposée
n’aurait pas de sens sans bases de
données pour le stockage de l’information. Il existe deux projets
d’implémentation de bases de
données Open Source en Java :
InstantDB et HypersonicSQL.
Ces bases de données sont à utiliser uniquement dans le cas de
réalisations de maquettes ou de
prototypes, où elles se montrent
très utiles car leur légèreté leur
permet d’être directement intégrées aux environnements de développement. En dehors des maquettes et des prototypes, il est
préférable d’utiliser des bases
plus classiques comme MySQL et
PostgreSQL.
En plus du travail de sélection, nous devrons réaliser un travail d’intégration de notre solution. Bonne nouvelle : de la même
façon qu’il existe des distributions Linux, on voit naître des distributions de logiciels Open
Source Java. Celles-ci ont pour
ambition la construction de serveurs d’applications complets
basés sur des produits Open
Source. Il en existe trois actuellement : JBoss, Enhydra et Exolab.
Et si vous vous demandez comment développer du code Java
en utilisant un environnement de
développement, tournez-vous
vers netbeans.org, un projet qui
a pour but le développement
d’un IDE Open Source.
z
ALLER PLUS LOIN
Sur le Web
An MVC Framework from Kevin Duffy
http://www.brainopolis.com/jsp/mvc/
KDuffey_MVC.html
Application-servers : site Internet dédié à
l’actualité des serveurs d’applications
http://www.application-servers.com/
Technologie
Description
Dernière version
Logiciels alternatifs
URL
Tomcat
Moteur de servlet/JSP
4.1.10
Jetty, Resin
http://jakarta.apache.org/
Tapestry, WebWork
http://jakarta.apache.org/
Struts
Ordonnanceur (type Model 2) 1.0.2
Castor
Outil de mapping
0.9.3.21
JBoss
Conteneur EJB
3.0
Cocoon
Framework de présentation
2.0.3
FOP
Outil d’édition
HypersonicSQL Base de données
http://castor.exolab.org
Jonas, Enhydra, OpenEJB
0.20.4
1.4.3
http://www.jboss.org
http://xml.apache.org/
http://xml.apache.org/
MySQL, PostgreSQL, InstantDB
http://hsqldb.sourceforge.net/internet/hSql.html
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
33
Langage
LE MOIS
DERNIER, NOUS PRÉSENTIONS UNE CARTOGRAPHIE DES PROJETS
OPEN SOURCE EN JAVA
LES PLUS PROMETTEURS, PROJETS QUE NOUS ALLONS DÉTAILLER DANS CETTE SUITE D’ARTICLES.
VOICI TOMCAT, IMPLÉMENTATION DE RÉFÉRENCE DE LA TECHNOLOGIE SERVLET/JSP.
T
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
Figure 1. Topologie
d'une application Web.
out d’abord, il faut
situer Tomcat dans
l’environnement
des serveurs d’ap
plications.
Les
servlets et les JSP, technologies qui
le composent, seront ensuite présentés. Enfin, nous terminerons
par la mise en valeur des avantages jumelés des servlets et des JSP
au sein du paradigme MVC.
Les applications Web mettent
en relation des clients Web (tels
que des navigateurs) et des données à travers les protocoles standard d’Internet. Elles accèdent
fréquemment à des informations
qui existaient bien avant l’émergence du Web. La figure 1 présente les composantes principales d’une application Web.
Les premières applications
Web étaient très simples : il s’agissait principalement de mettre en
ligne l’annuaire d’une organisation, de proposer un service de
petites annonces… Autant d’applications, très utiles et non critiques, qui étaient peu sollicitées.
Pour les développer, une technologie facile à mettre en œuvre,
plus ou moins portable, était utilisée : CGI (Common Gateway
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
34
Langage
Interface. Dès que les applications se complexifièrent, les lacunes de cette technologie apparurent : les CGI se révélaient lents
et gourmands en mémoire. De
plus, ils n’étaient pas très agréables à coder : le code utilisé pour
la présentation (HTML) était toujours mélangé avec le code qui
représentait les logiques fonctionnelles (règles métier) et transactionnelles (accès aux données) de l’application. Il était
donc difficile de mettre en place
les séparations entre les différentes couches. En outre, les CGI n’apportaient aucune solution pour la
gestion des sessions utilisateurs,
pour la montée en charge (clustering et load balancing), pour la
gestion des transactions, pour la
gestion de la sécurité… Une application à base de CGI ne peut
tout simplement pas être utilisée
par des centaines voire des milliers
d’utilisateurs.
Afin de faire face à de telles
contraintes de charge, les éditeurs ont proposé des solutions
que l’on retrouve souvent derrière le terme de “serveur d’applications”. Ce terme prend des significations différentes en fonction de la personne ou de l’éditeur qui l’utilise. Il est fréquent de
diviser la famille des serveurs
d’applications en deux : serveurs
d’applications Web (Web application servers) et serveurs d’applications d’entreprise (enterprise
application servers).
Les serveurs d’applications
Web constituent l’alternative aux
CGI. Ils apportent beaucoup de
confort aux développeurs. Ils proposent en effet des assistants
pour accéder aux bases de données et permettent de gérer une
session utilisateur ainsi que l’état
de l’application. Les seconds sont
en général complètement intégrés aux serveurs Web (serveurs
HTTP). D’un point de vue technique, un serveur d’applications
Web s’exécute, sous forme de
thread, dans le même processus
que le serveur HTTP (contrairement aux CGI qui s’exécutent
dans des processus séparés) pour
limiter le temps de démarrage du
traitement d’une requête.
Comme technique de développement, les serveurs d’applications
Web
utilisent
l’encapsulation de code directement dans l’HTML. Cela permet
aux développeurs d’un site de
LAMP
Le serveur d’applications Web Open Source le plus utilisé est
constitué des quatre composantes suivantes : Linux (un Unix ou
BSD) comme système d’exploitation, Apache comme serveur Web,
MySQL comme base de données, et Perl, Python ou PHP comme
langage de programmation. L’éditeur américain Tim O’Reilly
nomme cette plate-forme LAMP (Linux-Apache-MySQLPerl|Python|PHP).
coder très simplement les aspects dynamiques du site. Les
technologies les plus populaires
sont PHP (Open Source), ASP
(Microsoft) et JSP (Java). Ce type
de serveurs d’applications comble la plus grande partie des besoins en matière de technologies
Web. C’est d’ailleurs pour cela
que cette technologie est très
prisée par les Web agencies.
Les serveurs d’applications
d’entreprise sont utilisés pour les
applications Web complexes qui
demandent un haut niveau de
disponibilité. Ils sont une extension des serveurs d’applications
Web. En clair, on peut dire qu’ils
sont plus robustes : meilleure sécurité des applications, gestion
des transactions améliorées
(avec, par exemple, des mécanismes de transactions réparties sur
plusieurs bases de données), intégration de systèmes de messageries synchrones ou asynchrones, mécanisme de connexion
aux systèmes centraux, clustering, équilibrage de charge et distribution de la logique fonctionnelle et de la logique transactionnelle sur des machines physiques
différentes, sans impact sur les
développements. La technologie
reine pour ce qui concerne les
serveurs d’applications d’entreprise reste J2EE.
Trois projets Open Source
sont disponibles : Jboss et
Enhydra (reposant sur la technologie J2EE) et Zope (basé sur le
langage Python).
Tomcat, un serveur
d’applications Web
Tomcat est un projet de l’Apache Software Foundation. C’est
également l’implémentation de
référence des technologies
servlet Java 2.2 et JavaServer
Page 1.1. Ce serveur d’applications Web comporte deux avantages. Tout d’abord, il est compatible J2EE, ce qui signifie que l’ensemble des développements réalisés pour Tomcat pourront être
portés sur des serveurs d’applications d’entreprise. Ensuite, les développements s’effectuent en
Java, ce qui assure un niveau
élevé de portabilité : il sera possible de déployer le code qui
tourne sur Tomcat sur la plupart
des systèmes d’exploitation,
(Linux, AS/400, Solaris, HP-UX,
AIX, Windows, NetWare, OS/
390…). Ce haut niveau de
portabilité
favorise
la
réutilisation, atout des technologies objets.
En quelques mots, Tomcat est
un moteur d’exécution pour les
servlets et les JSP. Il peut être intégré au très populaire serveur
Web Apache mais aussi à IIS. Le
serveur Web est alors en charge
de toute la partie statique du site
Web, alors que Tomcat gère la
partie dynamique (requêtes sur
les servlets et les JSP).
Servlets
Un servlet est un programme
Java très similaire aux CGI. Il se
Applet et Servlet
Un applet est un programme Java qui s’exécute dans un navigateur. Il nécessite la présence d’une machine virtuelle Java (JVM, Java
Virtual Machine). Il s’agit d’une technologie cliente.
Un servlet est un programme Java qui tourne sur un serveur. Tout
comme le navigateur a besoin d’une JVM pour exécuter l’applet, le
serveur en réclame une pour exécuter le servlet. On parle de technologie serveur.
déclenche sur demande d’un navigateur. Il interagit avec des bases de données ou d’autres programmes afin de fournir une réponse HTTP au navigateur.
Toutes les fonctionnalités offertes par les CGI sont accessibles
dans les servlets. Cette technologie serveur peut récupérer des
données en provenance d’un formulaire HTML, accéder aux informations stockées dans l’en-tête
HTTP de la requête du navigateur, déposer et demander des
cookies…
Contrairement aux CGI, un
programme servlet est chargé
une seule fois en mémoire. Il est
capable de traiter des requêtes
provenant de plusieurs navigateurs en même temps. Attention,
le code produit par le développeur d’un servlet se doit d’être
réentrant.
Il s’agit d’un programme qui
procure une garantie en matière
de sécurité. Ecrit en Java, il ne
peut pas faire planter le serveur
d’applications Web par des erreurs de manipulation mémoire.
Le code qui s’exécute sur le serveur est compilé, il n’est donc pas
possible, en utilisant des failles de
sécurité du serveur d’applications Web, d’accéder à des informations sensibles, hébergées
dans le code (contrairement aux
CGI Perl, aux scripts ASP, JSP ou
PHP). Cette technologie ne souffre pas de l’interprétation de
métacaractères (comme les ''''|, >,
>>, ... '''' de Perl), qui permettent
aux bidouilleurs d’interagir avec
le gestionnaire de fichiers.
Un servlet est la spécialisation de la classe Java
javax.servlet.HttpServlet. Afin
d’en initialiser un, un serveur
d’applications Web charge la
classe du serveur, crée une instance de cette classe en appelant
le constructeur à zéro argument,
puis déclenche la méthode
init(ServletConfig). Les spécifications servlet garantissent aux développeurs que cette méthode
ne sera exécutée qu’une seule
fois par le serveur d’applications
Web. Lorsque le servlet est initialisé, il peut traiter les requêtes en
provenance des navigateurs.
Pour cela, la méthode service
(HttpServletRequest,
HttpServletResponse) du servlet
est déclenchée. Lorsqu’il doit être
déchargé, la méthode destroy()
du servlet est déclenchée.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
35
Langage
Figure 2. Processus de transformation d’un JSP en servlet exécutable par le serveur d’applications Web.
Plusieurs types de requêtes
HTTP peuvent être émis par un
navigateur (GET, POST…). La méthode service d’HttpServlet
achemine le traitement d’une requête HTTP vers la méthode
adaptée. Une requête XXXX
d’HTTP est acheminée vers la
méthode doXXXX( HttpServlet
Request, HttpServletResponse )
de Java (le traitement de la requête HTTP GET est assuré par la
méthode doGet). Les informations en provenance du navigateur sont accessibles via la classe
HttpServletRequest (paramètres
des formulaires, type de navigateur, etc.). Les informations renvoyées aux navigateurs doivent
l’être par l’intermédiaire de la
classe HttpServletResponse
(type MIME de la réponse, en-tête
de la réponse, corps de la réponse, cookies, etc.).
Le code du listing 1 présente
le programme Hello World avec
la technologie servlet. Les lignes
3 et 4 importent les packages
nécessaires à la manipulation des
servlets. Le servlet est défini en
ligne 6. Il hérite de la classe
HttpServletResponse. Les lignes
7, 8 et 9 définissent la méthode
doGet, qui permet de répondre
aux requêtes GET du protocole
HTTP. La ligne 10 récupère un flux
de sortie, utilisé dans les lignes 11
à 18 pour renvoyer l’HTML à l’utilisateur.
L’API servlet offre des méthodes qui permettent aux développeurs d’interagir avec les paramètres de la requête HTTP. Par
exemple, si le servlet est sollicité
par la requête suivante :
http://server/servlet/HelloWorldServlet?
nom=Bill
La valeur du paramètre nom
peut être retrouvée en utilisant
l’instruction Java suivante :
String
nom = request.getParameter(''nom'');
L’API servlet permet bien
d’autres fonctionnalités. Afin d’en
avoir une présentation plus approfondie, mieux vaut se reporter
à l’excellent livre de Jason Hunter
Java Servlet Programming.
JavaServer Page
Il est possible pour un servlet
de récupérer une requête HTTP
en provenance d’un navigateur,
de générer une requête dynamiquement, en interrogeant le système d’information si nécessaire,
et de renvoyer une réponse contenant de l’HTML. Le problème
avec cette approche est que la
construction de la page doit être
réalisée par le servlet, ce qui signifie qu’un infographiste qui voudrait modifier l’apparence de la
page renvoyée devra modifier le
code Java du servlet et le
recompiler. La génération de contenu dynamique nécessite alors
des connaissances en programmation. On comprend par conséquent la nécessité de séparer la
partie traitement de la requête
HTTP de l’utilisateur (logiques
applicative, transactionnelle, règles métier) de la partie renvoi de
la page à l’utilisateur. Pour cela,
la plate-forme J2EE recourt au
paradigme Modèle-Vue-Contrôle (Model-View-Control, MVC).
Dans ce paradigme, le modèle
Listing 1
Listing 2
1 :
2 :
3 :
4 :
5 :
6 :
7 :
8 :
9 :
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
<HTML>
<BODY>
The date is <%= new Date() %>
</BODY>
</HTML>
package fr.improve.article.tomcat.servlet;
import javax.servlet.http.*;
import java.io.*;
public class HelloWorldServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
PrintWriter out = response.getWriter();
out.println("<html>");
out.println(" <head>");
out.println("
<title>Hello World</title>");
out.println(" </head>");
out.println(" <body>");
out.println("
Hello World !!");
out.println(" </body>");
out.println("</html>");
}
}
représente le système d’information. Il est constitué de composants Java. Composée de
JavaServer Pages (JSP), la vue représente la page HTML renvoyée
à l’utilisateur. Le contrôle est la
glu entre les deux, il est composé
d’un servlet.
Au moment du développement, un JSP est très différent
d’un servlet puisque c’est un document texte composé d’HTML
pour les parties statiques du document, et de tags spéciaux ou
de code Java pour les parties dynamiques. Un JSP peut donc être
développé par un infographiste
à partir de ses outils classiques de
développement.
Au moment de l’exécution,
un JSP est transformé en servlet
par un préprocesseur et compilé
en une classe Java de manière à
pouvoir être exécuté par le serveur d’applications Web (figure
2). Un exemple de code JSP est
présenté dans le listing 2, et sa
transformation par le préprocesseur en servlet pour exécution
dans le serveur d’applications
Web dans le listing 3.
Listing 3
public void service(
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType(«text/html»);
PrintWriter out = response.getWriter();
out.print(«<HTML>\n<BODY>\n»);
out.print(«The date is «);
out.print(new Date());
out.print(«\n</BODY>\n</HTML>»);
}
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
36
Langage
Figure 3. Implémentation du MVC avec des servlets et des JSP.
Modèle-Vue-Contrôle
La figure 3 présente une implémentation du paradigme
MVC avec les technologies
servlet et JSP.
1. Un navigateur lance une
requête sur un serveur d’applications Web. La réception de cette
requête est assurée par un
servlet.
2. Le servlet sous-traite la partie traitement de cette requête au
modèle. Le modèle est assuré par
un ensemble de classes Java. Celles-ci accèdent au système d’information afin de récupérer les
données requises par le traitement de la requête. La plateforme J2EE prévoit que le modèle
respecte les spécifications EJB.
3. Le servlet récupère du modèle les données qui devront être
renvoyées à l’utilisateur et les
dépose dans un objet accessible.
4. Le servlet sous-traite l’affichage des données en provenance du modèle à un JSP.
5. Le JSP récupère les données en provenance du servlet
dans un objet.
6. L’HTML généré par le
servlet est renvoyé au navigateur.
Le paradigme MVC est une
des composantes essentielles de
la plate-forme J2EE. Il assure à
cette technologie un niveau de
productivité élevé, même pour
les applications complexes : le
développeur Java utilise ses
outils pour tout ce qui concerne
la logique applicative, la logique
transactionnelle et les règles métier. L’infographiste utilise ses
outils pour la partie présentation
des données. Ainsi, chacun travaille de manière efficace. Ce
modèle assure également une
maintenance plus aisée des applications. En effet, une remise en
cause de la charte graphique de
l’application n’aura que peu d’impact sur l’application, puisque
seuls les JSP devront être modifiés (les parties servlet et modèle
étant indépendantes de la charte
graphique). De la même manière,
une modification de la structure
du système d’information n’aura
pas d’impact sur la partie infographie.
Dans cet article, nous vous
avons présenté les technologies
servlet et JSP. En utilisant le paradigme MVC, ces technologies
permettent de développer de
manière efficace des applications
maintenables. Le mois prochain,
nous verrons qu’il est encore possible d’augmenter la productivité
grâce à l’utilisation d’un framework : Struts.
z
ALLER PLUS LOIN
Sur le Web
Tomcat :
http://jakarta.apache.org
Linux as an Application Server - The Tomcat
Way (Guide d’installation de Tomcat) :
http://www.samag.com/linux/articles/
v10/i01/a4.shtml
Livres
Jason Hunter, Java Servlet Programming,
O’Reilly, 1998.
Hans Bergsten, JavaServer Page, 0’Reilly, 2000
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
37
Langage
LE DÉVELOPPEMENT D'APPLICATIONS WEB RESTE DIFFICILE ET LES MÉTHODES ACTUELLES PRÉSENTENT
CERTAINS DÉFAUTS. UN NOUVEAU TYPE DE FRAMEWORK PROPOSE DE SUIVRE UN MODÈLE EFFICACE DE
PROGRAMMATION : MVC2. CE MOIS-CI, NOUS ÉTUDIERONS STRUTS, UN PROJET OPEN SOURCE APACHE.
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
Jean-Noël Ribette,
développeur actif sur
le projet Struts, il
rejoint Improve en
2001. Il développe
actuellement des
bibliothèques pour
faciliter l’utilisation de
Struts.
[email protected]
D
ifférentes technologies existent pour développer des applications Web :
CGI, servlets, scripts (ASP, PHP et
JSP). Les CGI (Common Gateway
Interface) ont permis de développer les premiers sites Web dynamiques. Cette technologie permet d’appeler un programme externe au serveur Web lors d’une
requête. Ce programme qui peut
accéder à toutes les ressources
nécessaires, comme une base de
données, et construire la page en
fonction de la requête. Néanmoins, les CGI ont certains incon-
Figure 1 :
Interactions des
composants dans
une architecture
à base de scripts.
Le code d’accès à
la base et le code
de HTML est
difficilement
réutilisable.
vénients : mixage de code HTML
et de code de programmation
dans le programme qui rend la
maintenance difficile, surcharge
mémoire avec le lancement d’un
nouveau processus pour chaque
requête, etc. Les CGI ne sont donc
pas adaptés à la création d’applications Web importantes. Avec le
langage Java est apparue une
nouvelle technologie : les
servlets. Ces petits serveurs ou
services écrits en langage Java et
utilisant une API spécifique corrigent certaines faiblesses des
CGI. Initialisé à la création, le
servlet n’existe qu’une seule instance. Les performances sont
donc améliorées. Cependant, le
développeur doit toujours mixer
le code Java et HTML. De plus, la
moindre modification oblige à
recompiler le servlet et à le recharger. Les JSP, ou Java Server
Pages, viennent résoudre ces problèmes de recompilation. Ici, c’est
le code Java que l’on incorpore
dans la page HTML avec des techniques de scripting. Le serveur
compile automatiquement la
page en un servlet et l’exécute
ensuite. Les ASP et PHP sont
d’autres variantes de scripting, le
PHP étant sans doute la solution
la plus en vogue actuellement.
(Figure 1)
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
38
Langage
Figure 2 : Cheminement d’une requête lorsqu’un utilisateur agit sur une interface
(1) L’utilisateur manipule l’interface homme/machine. Un événement est envoyé. Cet événement est récupéré
par le contrôleur.
(2) Le contrôleur effectue l’action demandée par l’utilisateur en appelant les méthodes nécessaires sur le
modèle.
(3) Le contrôleur informe la vue d’un changement d’état du modèle.
(4) La vue interroge le modèle afin de connaître son état.
(5) L’utilisateur voit le résultat de son action.
Les approches à base de
scripting nécessitent l’incorporation importante de code
applicatif dans le code HTML. Ces
techniques limitent aussi la
réutilisation de code. Pour ce qui
est du monde Java, il a donc été
proposé de faire collaborer les
servlets et les JSP dans les applications. Les servlets sont utilisés
par les développeurs pour gérer
les aspects programmation d’une
application Web et les JSP sont
utilisés par les infographistes
pour effectuer l’affichage.
On retrouve ainsi un servlet
et un JSP par requête possible sur
le site Web. Le servlet ne contient
plus d’HTML, et le JSP contient
juste le code nécessaire à l’affichage. Ce style de programmation respecte le paradigme MVC.
Le paradigme MVC
L’objectif de MVC (Modèle/
Vue/Contrôleur) est de faire collaborer deux équipes : une
équipe à consonance infographie
et une équipe à consonance informatique. Attention toutefois,
cela augmente les interactions et
nécessite une gestion de projet
plus élaborée.
Le paradigme MVC est un
schéma de programmation qui
propose de séparer une application en trois parties :
• Le modèle, qui contient la
logique et l’état de l’application.
• La vue, qui représente l’interface utilisateur.
• Le contrôleur, qui gère la
synchronisation entre la vue et le
modèle.
Le contrôleur réagit aux actions de l’utilisateur en effectuant
les actions nécessaires sur le modèle. Le contrôleur surveille les
modifications du modèle et informe la vue des mises à jour nécessaires (figure 2).
Au final, une telle séparation
favorise le développement et la
maintenance des applications.
• Le modèle étant séparé des
autres composants, il est développé indépendamment. Le développeur du modèle se concentre sur le fonctionnel et le transactionnel de son application.
• Le modèle n’est pas lié à une
interface, il peut donc être réutilisé (passage d’une application
avec interface en Java à une application avec interface Web).
Dans les applications J2EE, le
modèle est assuré par un EJB, le
contrôleur est assuré par des
servlets et la vue par des JSP. Pour
plus d’informations sur l’implémentation en Java du paradigme
MVC se reporter à l’article du
mois dernier (Développeur Référence v1.1, dossier “Servlet/ JSP/
MVC avec Tomcat”).
Le paradigme MVC est une
avancée importante en terme
d’architecture d’applications
Web. Elle n’est cependant pas
encore idéale : elle oblige à écrire
une multitude de servlets, qui
sont autant de points d’entrée
dans l’application. Pour pallier cet
inconvénient, des frameworks
ont été développés. Ces
frameworks qui sont composés
d’un seul servlet (donc d’un seul
contrôleur) sont regroupés sous
l’étiquette “Model 2” encore appelé “MVC2” (figure 3).
Les frameworks
MVC2
Il existe actuellement un certain nombre de projets plus ou
moins avancés visant à la création
de framework MVC2. Ces projets,
tous basés sur les servlets, progressent très vite. En voici une
rapide présentation (Tableau 1
page suivante).
Barracuda
Ce framework qui se veut très
complet est développé par la
communauté Open Source
Enhydra (www.enhydra.org). Il
est prévu qu’il comporte :
• Un modèle événementiel
complet;
• Un modèle et une bibliothèque de composants MVC pour
Figure 3 : Interaction des composants dans une architecture MVC. Le code d’accès aux données et le code
HTML sont intégrés dans des composants séparés. Il est donc possible de réutiliser le code.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
39
Langage
mettent de rendre accessibles
des objets dans les contextes
standards (page, session, application) via des variables de
scripting. Il est ensuite possible
de récupérer, de modifier ou d’afficher les valeurs des propriétés
de ces objets. Par exemple, si la
session contient un objet nommé
“item” disposant d’une méthode
getPrize() et que l’on veut afficher
son prix, il suffira d’écrire :
<bean:write name='item'
property='prize'/>
Figure 4 : Interaction des composants dans une architecture MVC2. Le navigateur agit sur un seul
composant, le contrôleur, l’architecture est simplifiée. Dans un framework évolué, le contrôleur intègre des
services : gestion des erreurs, habilitation, autorisation, diffusion multiterminal…
l’interface utilisateur.
• Un système de validation
des formulaires et de mappings
avec des objets Java.
• Un contrôleur MVC2.
• Une bibliothèque JavaScript.
Hammock
Ce framework permet de développer une application Web de
la même façon qu’une application Swing. Pour cela, Hammock
utilise le même modèle : système
d’événements identique, présence de composants IHM “Panel”, et de layout “FlowLayout” ou
“TableLayout”, etc. Ce système
fonctionne sans JSP. Signalons
qu’il n’est pas Open Source mais
développé par OOP.
Tapestry
Ce framework est axé sur les
composants : la création d’une
page se fait par le développement de composants qui vont
définir cette page. Il propose des
concepts intéressants comme la
séparation de l’état de la page et
de la page elle-même afin de disposer d’un pool de pages, ou encore des possibilités d’internationalisation via des templates.
Tapestry facilite la gestion des
erreurs et de la charge. Il a
d’abord été développé par Primix
et se trouve maintenant disponible en Open Source.
mais son API est plus réduite. Il
pourrait être utilisé avec d’autres
technologies que les servlets (figure 4).
de remplir automatiquement
des champs et de créer des applications supportant plusieurs
langages.
Le framework Struts
Le modèle
Struts est un projet Open
Source développé par la communauté Jakarta d’Apache. Il a débuté en mai 2000 sous la direction de Craig R. McClanahan, qui
participe également au développement de Tomcat. Aujourd’hui,
Struts est géré par plusieurs
committers. Sa mailing-list comporte un millier de personnes.
C’est un projet très actif.
Struts fournit un framework
MVC comprenant les composants suivants :
• Un contrôleur facilement
configurable permettant d’associer des actions (méthode d’un
objet Java) à des requêtes HTTP.
• Des bibliothèques de tags
spécifiques pour créer facilement
une vue.
• Un Digester, permettant de
parser un fichier XML et d’en récupérer seulement les informations voulues.
• Des utilitaires permettant
Le modèle d’une application
Struts est complètement standard
: suivant le paradigme MVC, Struts
et le modèle sont indépendants.
Le modèle peut très bien accéder
directement à une base de données relationnelle, XML ou utiliser
des EJB. Il faudra juste veiller à
permettre son initialisation.
La vue
La vue sous Struts est constituée de pages JSP. Afin de rendre
la création de ces pages aisée par
un designer et d’y éviter l’introduction de code Java, Struts propose l’utilisation de bibliothèques de tags ou taglib spécifiques : html, bean, logic et
template.
La taglib bean comprend des
tags qui peuvent être vus comme
des améliorations des tags JSP
<jsp
:useBean>
<jsp
:getProperty>
et
<jsp
:setProperty>. En effet, ils per-
Struts supporte également
les nested et indexed properties.
Par exemple, si la session contient
un objet nommé “items” et a une
méthode getItem(int item) qui
renvoit un item, il est possible
d’écrire :
<bean:write name='items'
property='item[2].prize'/>
La taglib bean propose également un tag permettant d’afficher un message en fonction de
la langue de l’utilisateur. Dans
l’optique de créer une application
supportant plusieurs langues, les
pages JSP ne doivent pas contenir de texte mais faire appel à ce
tag. Dans Struts, les messages
destinés à l’utilisateur sont associés à une clé et définis dans des
fichiers spéciaux. Chaque fichier
contient les messages dans une
langue donnée. Le tag :
<bean :message key='titre.index'/>
affichera le texte sous la clé
titre.index dans la langue principale définie par le navigateur si
disponible, ou sinon dans la langue par défaut.
La taglib html contient les
tags nécessaires à la création d’interface utilisateur dynamique, et
notamment des formulaires. Les
tags permettent de remplir auto-
Tableau 1. Les frameworks MVC2
Projet
Version
URL
Puissance
Complexité
Barracuda
1.1
http://barracuda.enhydra.org/
+++
+++
Webwork
Hammock
Ce framework utilise le concept du Pull Hierarchical Model
View Controller. Il est similaire
techniquement au framework
Struts dont la présentation suit,
1.0
http://www.oop.com/
+++
+++
Tapestry
2.2
http://sourceforge.net/projects/tapestry
++
+++
Webwork
1.2
http://sourceforge.net/projects/Webwork/
+
+
Struts
1.0.2
http://jakarta.apache.org/struts
++
++
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
40
Langage
matiquement les champs à afficher et de retourner les valeurs
en vue de leur gestion par le contrôleur. Cela se fait en spécifiant
dans le tag le nom de la propriété
qui doit être affichée ou modifiée.
Cette propriété correspond à une
propriété Javabeans d’un objet
Formulaire qui aura été spécialement écrit par le développeur
pour servir d’interface entre la
vue et le contrôleur.
Les autres tags de la bibliothèque permettent d’afficher les
messages d’erreur relatifs à l’application ou au remplissage d’un
champ donné, et de gérer plus
facilement les liens et les sessions. Voici par exemple comment créer un formulaire permettant d’ouvrir une session :
<html:form action= '/logon'
focus='username'>
Username : <html:text
property='username'/>
<br>
Password: <html:password
property='password'/>
<html:submit property=''submit''
value=''Submit''/>
</html:form>
L’utilisation de ce formulaire
est plus détaillée dans la section
exemple.
La taglib logic définit tout un
ensemble de tags permettant
d’inclure ou de ne pas inclure le
corps des tags en fonction de critères logiques portant sur la valeur des propriétés d’un objet. Il
est également possible d’effectuer des itérations. Par exemple,
pour afficher un message en
fonction de l’âge de l’utilisateur :
<logic:greaterThan name='utilisateur'
property='age' value='17'>
Vous êtes majeur !
</logic:greaterThan>
Ou pour afficher tous les prénoms des enfants d’une personne :
<logic:iterate name='user'
property='enfants'
id='enfant'>
<bean:write name='enfant'/><br>
</logic :iterate>
La taglib template est destinée à faciliter la création de pages suivant un modèle. Pour un
exemple courant, imaginons la
barre de navigation en haut de la
page, une zone centrale et un
copyright en bas. Les templates
permettent de définir la barre de
Figure 5 : Traitement d’une requête par le contrôleur de Struts.
navigation et le copyright, puis
ensuite de les inclure aisément
dans toutes les pages.
Les taglibs de Struts sont très
complètes et permettent de faire
beaucoup de chose sans écrire de
code Java. Des bibliothèques répondant à des besoins plus spécifiques – création de composants, accès à une base de données, mises en page… – sont en
projet pour les prochaines versions de Struts.
Le contrôleur
Le contrôleur est la partie du
framework qui fait le lien entre la
vue et le modèle. C’est elle qui
permet de gérer l’enchaînement
de la navigation. Dans Struts, le
point central du contrôleur est un
servlet de la classe ActionServlet.
Toutes les requêtes y aboutissent
(figure 5).
Du point de vue du développeur, la programmation du contrôleur passe par :
• L’écriture d’un fichier de configuration : struts-config.xml. Ce
fichier décrit en particulier quelles sont les actions possibles et à
quelles classes Actions il faut les
associer (mapping).
• L’écriture des objets formulaires
qui vont servir à la transmission
des données de la vue au modèle.
Ces objets étendent ActionForm.
Ils contiennent toutes les pro-
priétés des formulaires, ainsi
qu’une méthode permettant de
valider ou non les valeurs de ces
propriétés. Lorsqu’un formulaire
HTML est renvoyé, Struts
instancie automatiquement
l’ActionForm correspondant et
initialise ses propriétés avant de
les valider.
• L’écriture des Action à effectuer
pour chaque requête.
Une fois que Struts a validé les
données envoyées, il appelle la
méthode perform() de l’Action
correspondante. Dans cette méthode, le développeur peut récupérer les données rentrées dans le
formulaire via l’ActionForm, et effectuer les opérations nécessaires
sur le modèle. Il peut pour cela
s’aider des utilitaires de Struts permettant de recopier des propriétés d’un bean à un autre.
Struts fournissant le servlet
cœur du contrôleur et des classes
Action et ActionForm qu’il n’y a
plus qu’à étendre, la création
d’actions est très rapide.
Essayer Struts
Essayer Struts est très facile :
la distribution binaire de Struts
est fournie avec des fichiers .war
parmi lesquels une application
exemple. Il suffit avec Tomcat de
copier ces fichiers dans le répertoire Webapps après avoir installé
Pourquoi choisir Struts
Struts fait partie du projet Apache. Or les projets Apache ont tendance à monopoliser toute l’attention des développeurs. Sachant que
la qualité des projets Open Source est apportée par le nombre de
développeurs, on peut penser que Struts sera de meilleure qualité
que les autres projets MVC2.
un parseur XML JAXP (l’implémentation de référence de Sun
ou Xerces par exemple) et de redémarrer le serveur pour faire
fonctionner Struts.
Les personnes désirant
recompiler Struts ou utiliser
d’autres serveurs trouveront les
informations nécessaires sur le
site
de
Struts
(http://
jakarta.apache.org/struts).
Un exemple
d’application Struts
Struts est fourni avec une application exemple très simple.
Celle-ci montre comment effectuer des tâches courantes
comme afficher un formulaire,
récupérer les données, les
réafficher, afficher des listes dynamiques, etc. L’application exemple permet à un utilisateur de
s’enregistrer et de maintenir une
liste des différents comptes mail
qu’il utilise sur Internet.
Les actions possibles avec
cette application sont :
• Se loguer (associée par le contrôleur à la classe LogonAction).
• Se
déloguer
(classe
LogoffAction).
• Enregistrer ses coordonnées
(classe SaveRegistrationAction).
• Enregistrer les propriétés d’un
compte
mail
(classe
SaveRegistrationAction).
• Afficher ses coordonnées (classe
EditRegistrationAction)
• Afficher les propriétés d’un
compte
mail
(classe
EditSubscriptionAction).
L’application est composée
de cinq pages :
• La page d’accueil, index.jsp.
• La page de login, logon.jsp.
• Le
menu
principal,
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
41
Langage
Listing 1
…
<!— Logon form bean —>
<form-bean
name=»logonForm»
type=»org.apache.struts.example.LogonForm»/>
…
<!— Process a user logon —>
<action path=»/logon»
type=»org.apache.struts.example.LogonAction»
name=»logonForm»
scope=»request»
input=»/logon.jsp»>
</action>
…
Listing 2
public class LogonForm extends ActionForm {
…
public void setPassword(String password) { …}
public void setUsername(String username) {…}
public String getUsername() {…}
public String getPassword() {…}
…
}
Listing 3
public class Logon Form extends ActionForm {
…
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if ((username == null) || (username.length() < 1))
errors.add(«username», new
ActionError(«error.username.required»));
if ((password == null) || (password.length() < 1))
errors.add(«password», new
ActionError(«error.password.required»));
return errors;
}
}
Listing 4
public class LogonAction extends Action {
…
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request, HttpServletResponse
response) throws … {
…
// récupération des paramètres
String username = ((LogonForm) form).getUsername();
String password = ((LogonForm) form).getPassword();
// recuperation de la base de données
Hashtable database = (Hashtable)
servlet.getServletContext().getAttribute(..) ;
…
// création de l’utilisateur (utilisation du modèle)
User user = (User) database.get(username);
if (user!=null &&
(!user.getPassword().equals(password))) user=null;
if (user==null)
errors.add(ActionErrors.GLOBAL_ERROR,
new
ActionError(«error.password.mismatch»));
// si on a des erreurs, retour à la page de login
if (!errors.empty()) {
saveErrors(request, errors);
return (new ActionForward(mapping.getInput()));
}
// ok, sauvegarde de l’utilisateur
HttpSession session = request.getSession();
session.setAttribute(Constants.USER_KEY, user);
…
// renvoie à la page suivante
return (mapping.findForward(«success»));
}
mainMenu.jsp.
• La page permettant d’afficher
les propriétés de l’utilisateur,
registration.jsp.
• La page permettant d’afficher la
configuration d’un compte mail
de l’utilisateur, subscription.jsp.
Grâce à l’emploi de la taglib
logic, les pages servant à l’affichage des propriétés de l’utilisateur et de ses comptes servent
aussi pour la création, la modification et la suppression. Les pages contenant des formulaires
(logon.jsp, registration.jsp,
subscription.jsp) sont chacune
associées à un objet Form (on
trouve respectivement les classes
LogonForm, RegistrationForm et
SubscriptionForm).
Examinons en détail le processus permettant de se loguer.
La page logon.jsp contient les
tags suivants :
...
<html:form action= '/logon'
focus='username'>
...
<html:text property='username'/>
...
<html:password
property='password'/>
...
<html:submit property=''submit''
value=''Submit''/>
...
</html:form>
...
ce qui provoque l’affichage
d’un formulaire avec deux
champs login/password et un
bouton submit. Voici ce qui se
passe lorsqu’on clique sur
submit :
• La requête est envoyée à
l’adresse logon.do à laquelle va
répondre le contrôleur de Struts.
• Le contrôleur utilise le fichier
struts-config.xml (Listing 1) pour
déterminer quel formulaire va
permettre la validation des données (dans notre exemple il s’agit
de LogonForm) et quelle action
est associée à cette requête (dans
notre exemple il s’agit de
LogonAction). (Listing 1, Extrait
du fichier struts-config.xml)
• Le servlet va donc instancier un
LogonForm et l’initialiser avec les
valeurs des attributs fournis dans
la requête (password et
username). Pour se faire
LogonForm propose les méthodes setUsername et setPassword
(Listing 2) :
• Le servlet va ensuite appeler la
méthode
validate()
de
LogonForm. Celle-ci renvoie une
erreur si au moins un des deux
champs est vide. (Listing 3)
• S’il n’y a pas d’erreur, le contrôleur va appeler la méthode
perform() de LogonAction. Sinon
il va renvoyer à la page de login.
• La méthode perform() contient
le code métier associé à une
opération d’authentification
(listing 4).
Le code source complet de
l’application, écrit par Craig R.
McClanahan, est fourni avec
Struts. Struts a été conçu de façon
à pouvoir être aisément modifié
par les développeurs pour répondre à leurs besoins. Cela se voit
dans l’application exemple, qui
redéfinit la classe ActionMapping
pour introduire la notion de
“success” et de “failure” pour une
action. Des tags spécifiques sont
également introduits pour vérifier
si un utilisateur est logué ou non
et pour écrire plus facilement certains liens.
Nous avons vu que Struts est
un framework qui permet de développer des applications Web
en utilisant le modèle MVC2. Les
applications conçues ainsi sont
plus facilement maintenables et
évolutives, de part la séparation
des fonctionnalités du modèle,
du contrôle et de la vue. L’utilisation du framework permet au
développeur de se concentrer sur
le modèle, Struts fournissant le
cadre nécessaire au contrôle et à
l’interface de l’application, permettant un développement rapide de ces parties. Struts préfigure une nouvelle méthode de
développement, succession de la
programmation par servlets.
Néanmoins, il convient d’ajouter
que l’écriture d’une application
utilisant Struts ne doit pas se décider à la légère : un certain
temps est nécessaire pour prendre en main le concept et les API.
z
ALLER PLUS LOIN
Le projet Struts :
http://jakarta.apache.org/struts
Un autre site sur Struts :
http://www.husted.com/about/struts/
Le serveur Tomcat :
http://jakarta.apache.org/tomcat
Le parseur XML de Sun :
http://Java.sun.com/xml
La mailing-list de Struts :
[email protected]
Les archives de la mailing-list :
http://www.mail-archive.com/[email protected]/
D’autres articles sur Struts :
http://www-106.ibm.com/developerworks/
library/j-struts/?dwzone=Java
http://www.us-eh.com/craiger/articles/struts
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
42
Langage
APRÈS LA PRÉSENTATION
STRUTS PUBLIÉE LE MOIS DERNIER, VOICI DES EXPLICATIONS
SUR LE FONCTIONNEMENT DE L’INTERNATIONALISATION ET DE L’AFFICHAGE DE DONNÉES DYNAMIQUES
À L’AIDE DE TAGS SPÉCIFIQUES. UN EXEMPLE CONCRET MONTRE COMMENT METTRE EN ŒUVRE CES CONCEPTS
AFIN D’AMÉLIORER LA PRODUCTIVITÉ DU DÉVELOPPEMENT D’UNE APPLICATION.
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
DU FRAMEWORK
S
truts est un framework Open Source
permettant de développer des applications Web. Il utilise le modèle MVC2, qui sépare
une application en trois couches :
le modèle, cœur de l’application
contenant les règles métier ; le
contrôleur, dont le rôle est d’effectuer des actions sur le modèle
en fonction des requêtes de l’utilisateur ; la vue, qui sert à afficher
les données.
Ce modèle présente plusieurs avantages. Le cœur de l’apFigure 1. Modèle MVC de Struts.
Jean-Noël Ribette,
développeur actif sur
le projet Struts, il
rejoint Improve en
2001. Il développe
actuellement des
bibliothèques pour
faciliter l’utilisation de
Struts.
[email protected]
plication et son fonctionnement
interne, d’abord, sont complètement indépendants du système
d’interface utilisé (Swing, Web,
Wap, etc.). Ensuite, la vue est décrite par les différents écrans de
l’application, qui appellent uniquement des “getters” sur le modèle. Enfin, la navigation entre les
écrans est écrite séparément
dans le contrôleur. Chaque partie est ainsi plus simple.
Dans Struts, le contrôleur est
constitué par un servlet unique
(classe ActionServlet), point de
passage obligé pour toutes les
requêtes, permettant d’effectuer
des traitements systématiques
(vérification des données saisies,
d’authentification…). Celui-ci va
appeler des méthodes sur des
classes écrites par le programmeur en fonction de chaque requête (classes Action). La vue est
composée de pages JSP utilisant
des tags spéciaux reconnus par le
moteur de servlet. Ces tags permettent aux infographistes d’éditer des JSP plus simplement, en
leur évitant d’écrire du code Java
(figure 1). Les deux apports principaux des tags spéciaux de
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
43
Langage
Struts du point de vue des fonctionnalités
sont l’internationalisation d’une application et
l’affichage dynamique de données.
L’internationalisation
Struts permet de développer facilement
des applications internationalisées. Il se base
pour cela sur les concepts de la classe
ResourceBundle. Celle-ci, qui se trouve dans le
package java.util, permet de récupérer une
chaîne de caractères à partir d’un mot clé, en
fonction d’un langage (java.util.Locale). En
étendant cette notion et en utilisant un objet
Locale selon les langages acceptés par le navigateur Web du client, on obtient des possibilités d’internationalisation intéressantes : regroupement de tous les messages concernant
une langue dans un fichier, support d’une langue supplémentaire en traduisant les chaînes
de caractères associées aux mots clés.
sage de la taglib bean de Struts pour récupérer le texte. Le code JSP correspondant à
l’écran de login est alors :
<html>
<body>
<bean:message key=''login.title''/>
<html:form action=''XXX''>
<bean:message key=''login.username''/>
<html:text property=''username''>
<bean:message key=''login.password''/>
<html:password property=''password''>
<html:submit><bean:message
key=''login.accept''/></html:submit>
</form>
</body>
</html>
Struts permet également d’utiliser des
images différentes en fonction de la langue
de l’utilisateur. Pour cela, les tags <html:img>
et <html:image> acceptent un mot clé
comme source de l’image. L’attribut src du tag
html généré correspond alors à la chaîne associée à cette clé.
Configuration
Configurer Struts pour une utilisation internationale revient en fait à créer un fichier
pour chaque langue. On aura par exemple,
pour un panneau de login en français et en
anglais :
• la liste de mots clés des messages pouvant
être affichés :
login.title, login.username, login.password,
login.accept
• un fichier ApplicationResources.properties
pour le langage par défaut (par exemple l’anglais) contenant le texte suivant :
login.title=Welcome, please type in your name
and password
login.username=Username :
login.password=Password :
login.accept=Login
• un fichier ApplicationResources_fr.properties
pour le français :
login.title=Bienvenue, veuillez taper votre
nom d’utilisateur et votre mot de passe
login.username=Compte:
login.password=Mot de passe :
login.accept=Se loguer
Ces deux fichiers devront être placés dans
le répertoire WEB-INF\classes de l’application
Web. Puis il suffira d’ajouter le paramètre Application dans la section ActionServlet du fichier WEB-INF\web.xml pour configurer
Struts.
<init-param>
<param-name>application</param-name>
<param-value>ApplicationResources
</param-value>
</init-param>
Ecriture des JSP
Du côté de la programmation de l’interface (page JSP), il faudra utiliser le tag mes-
L’accès aux données
et au modèle
Pour transmettre des données du modèle
à la vue, la technologie servlet dispose de
quatre contextes dans lesquels on peut stocker des données :
• un contexte global à l’application Web ; les
objets s’y trouvant survivent pendant toute
la vie de l’application ;
• un contexte relatif à la session, où les objets
sont accessibles pendant la durée de vie de
la session ;
• un contexte relatif à la requête, dans lequel
les objets sont accessibles par les servlets et
JSP qui traitent la requête ;
• un contexte relatif à la page, où l’accès aux
objets n’est possible que par la page JSP en
cours de traitement.
Système de nommage
L’accès aux données dans la JSP se fait en
utilisant des tags spéciaux, auxquels on peut
fournir le nom de l’objet à récupérer, le contexte dans lequel il faut le chercher, et la propriété de l’objet qui nous intéresse.
Struts se base sur les propriétés JavaBeans
pour accéder aux valeurs des objets. Par
exemple, si un objet User a une méthode
getAdresse(), on peut afficher l’adresse en
utilisant la propriété Adresse, ce qui donne en
Struts :
<bean:write name='USER' scope='session'
property='adresse'/>
Struts supporte également les nested
properties (propriétés imbriquées). Autrement dit, si l’on veut récupérer la ville de notre utilisateur (méthode getVille() d’Adresse),
on pourra utiliser la propriété adresse.ville.
Enfin, Struts supporte les indexed
properties (propriétés indexées) : dans le cas
où l’utilisateur dispose de plusieurs adresses
accessibles par getAdresse(int i), on peut
écrire adresse[1].ville et adresse[2].ville. Voici
le résultat en Struts :
<bean:write name='user' scope='session'
property='adresse[1].nom'>
Création de tags pour une
bibliothèque Struts
Dans une application de gestion, les
écrans sont relativement simples et similaires.
Pourtant, les développeurs passent encore
beaucoup de temps dans le développement
de l’interface homme/machine (IHM).
En étendant Struts avec de nouvelles bibliothèques, il est possible de diminuer le
temps de développement de l’IHM. Nous allons voir comment cette bibliothèque doit
être développée.
Bibliothèque de tags JSP
Il est souvent reproché aux langages de
scripting (JSP, ASP, PHP3…) de mixer le code
HTML et le code de script. Pour limiter cela, il
est possible depuis la version 1.1 des JSP de
définir des bibliothèques de tags pour masquer le code de script.
Une bibliothèque de tags est tout simplement composée d’un ensemble de classes
Java définissant les tags et d’un fichier de déclaration des nouveaux tags.
Définition d’un nouveau tag
Un tag sera toujours composé de balises
ouvrantes (<...>) et fermantes (</...>). Pour le
définir, il faut déclarer une classe Java héritant
de la classe TagSupport. Celle-ci doit être dotée de deux méthodes doStartTag() et
doEndTag(), qui correspondent respectivement à la balise ouvrante et à la balise fermante.
Le tag peut aussi comporter des attributs.
Dans ce cas, la classe Java doit disposer des
propriétés en rapport avec ces attributs. Par
exemple, au tag :
<tag:personneTag nom='Dupont' prenom='Luc'>
telephone : 01.01.01.01.01
</tag:personneTag>
correspondra la classe suivante (sans gestion des exceptions) :
public class PersonneTag extends TagSupport {
protected String nom = ''NOM'';
protected String prenom = ''PRENOM'';
public void setNom(String nom) {
this.nom = nom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
44
Langage
public int doStartTag() {
jspWriter out = pageContext.getOut();
out.print(''<table>\n<tr><td>'') ;
out.print(nom) ;
out.print(''</td> <td>'') ;
out.print(prenom);
out.println(''</td></tr><tr>\n<td
colspan=2'');
return EVAL_BODY_INCLUDE;
}
public int doEndTag() {
jspWriter out = pageContext.getOut();
out.println(''</td></tr></table>'');
return EVAL_PAGE;
}
public void release() {
nom = ''NOM'';
prenom = ''PRENOM'';
}
}
Après interprétation de la JSP par le serveur, le navigateur recevra le code suivant :
<table>
<tr><td>Dupont</td> <td>Luc</td></tr><tr>
<td colspan=2>
telephone: 01.01.01.01.01
</td></tr></table>
Examinons maintenant un tag compatible avec Struts supportant l’internationalisation. Celui-ci affichera un champ de saisie
(<input>) avec une valeur initiale dynamique.
Le champ aura également un nom et une
mise en page spéciale.
Les paramètres du tag
Le tag va avoir besoin d’un paramètre
pour le nom du champ, et d’un autre pour sa
valeur. Il générera l’HTML dans la méthode
doEndTag(). Afin d’afficher le nom du champ
dans la langue de l’utilisateur, on fait appel à
la classe RequestUtils. Pour récupérer la valeur initiale du champ (stockée dans un objet
formulaire par le framework Struts), c’est la
classe BeanUtils qui est mise en œuvre. Voici
la classe correspondant au tag :
public class FormElementTag extends TagSupport {
protected String key;
protected String property;
public void setKey(String key) {
this.key = key;
}
public void setProperty(String property) {
this.property = property;
}
public int doStartTag() {
return EVAL_BODY_INCLUDE ;
}
public int doEndTag() {
...
StringBuffer out = new StringBuffer();
out.append(''<tr><th>'');
// display the field name
out.append(RequestUtils.message(
pageContext, null, null, key));
out.append(''</th><td>'');
out.append(''<input type=\''text\'' name=\'''');
out.append(property);
out.append(''\'' value=\''v);
//display the initial value
out.append(BeanUtils.getProperty(
RequestUtils.lookup(
pageContext,Constants.BEAN_KEY, null),
property));
out.append(''\''></td></tr>'');
pageContext.getOut().println(out);
...
}
}
Utilisation du tag
Le nouveau tag s’utilise ainsi :
<exemple:formElement key='XXX' property='YYY'/
>
Le code Struts équivalent se présente sous
la forme suivante :
<tr>
<th><bean:message key= 'XXX'/></th>
<td><html:text property='YYY'/></td>
</tr>
Et le code JSP équivalent pourrait être le
suivant :
<tr>
<th><%= session.getValue(''XXX'') %></th>
<td><input type=''text'' name=''YYY''
value=''<%= session.getValue(vYYY'')%>''></td>
</tr>
L’utilisation du nouveau tag est de loin la
plus simple.
Le fichier tld
Le fichier tld sert à la définition de l’ensemble des tags qui vont constituer une bibliothèque. Voici des extraits du fichier définissant le nouveau tag :
...
<taglib>
...
<tag>
<name>formElement</name>
<tagclass>FormElementTag</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>key</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>property</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
Ce fichier doit être placé dans le répertoire
web-inf de l’application Web. Il faut inclure le
code suivant dans la page JSP qui utilisera ce
tag :
<%@ taglib uri=''/WEB-INF/exemple.tld''
prefix=''exemple'' %>
Struts-layout
Le tag formElement présentait l’idée se
trouvant à l’origine de la bibliothèque strutslayout, actuellement en cours de développement. Celle-ci a été proposée pour être intégrée à Struts et dispose déjà des fonctionnalités suivantes :
• Mise en page automatique des formulaires
par les tags sans écriture de code HTML. Par
exemple, la page logon.jsp de l’application
d’exemple de Struts réécrite avec strutslayout :
<layout:html locale=''true'' key=''logon.title''
styleClass=''FORM''>
<layout:form action=''/logon.do''
focus=''username'' styleClass=''FORM''>
<layout:field key=''prompt.username''
property=''username'' size=''16''
maxlength=''16'' isRequired=''true''
styleClass=''LABEL''/>
<layout:field key=''prompt.password''
property=''password'' size=''16''
maxlength=''16'' type=''password''
styleClass=''LABEL'' isRequired=''true''/>
<layout:formActions>
<html:submit property=''submit''
value=''Submit''/>
<html:reset/>
</layout:formActions>
</layout:form>
</layout:html>
• Code Struts standard :
<html:html locale=''true''>
<head>
<title><bean:message
key=''logon.title''/></title>
<html:base/>
</head>
<body bgcolor=''white''>
<html:errors/>
<html:form action=''/logon'' focus=''username''>
<table border=''0'' width=''100%''>
<tr>
<th align=''right''>
<bean:message key=''prompt.username''/>
</th>
<td align=''left''>
<html:text property=''username''
size=''16'' maxlength=''16''/>
</td>
</tr>
<tr>
<th align=''right''>
<bean:message key=''prompt.password''/>
</th>
<td align=''left''>
<html:password property=''password''
size=''16'' maxlength=''16''
redisplay=''false''/>
</td>
</tr>
<tr>
<td align=''right''>
<html:submit property=''submit''
value=''Submit''/>
</td>
<td align=''left''>
<html:reset/>
</td>
</tr>
</table>
</html:form>
</body>
</html:html>
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
45
Langage
La figure 2 présente l’affichage de ce code
dans un navigateur.
Figure 4. Affichage des éléments d’une collection.
Figure 5. Affichage des éléments d’une collection avec une autre feuille de style.
Figure 2. Ecran de login struts-layout.
• Affichage automatique des erreurs associées
à un champ. Les champs contenant des valeurs obligatoires ou de mauvaises valeurs
sont indiqués par une étoile rouge. Si le formulaire est envoyé avec des erreurs, un message explicite est affiché pour chacune d’elles. La figure 3 montre un exemple d’écran affichant des erreurs.
Par exemple, l’affichage des comptes d’un
utilisateur dans l’application :
<layout:collection key=''heading.subscriptions''
property=''subscriptions''
name=''user'' styleClass=''SUBSCRIPTION''>
<layout:collectionLine header=''heading.host''
property=''host''/>
<layout:collectionLine header=''heading.user''
property=''username''/>
<layout:collectionLine header=''heading.type''
property=''type''/>
<layout:collectionLine
header=''heading.autoConnect''
property=''autoConnect''/>
<layout:collectionAction header=''heading.action''
property=''host''
key=''registration.deleteSubscription''
action=''editSubscription.do?action=
Delete&host=''/>
<layout:collectionAction header=''heading.action''
property=''host''
key=''registration.editSubscription''
action=''editSubscription.do?action=
Edit&host=''/>
</layout:collection>
La figure 5 montre l’affichage des comptes d’un utilisateur avec une autre feuille de
style. L’utilisation de la bibliothèque strutslayout permet de développer plus rapidement les pages JSP d’une application Struts.
Son code source illustre une façon de créer
des tags compatibles Struts.
Avec des frameworks tels que Struts, les
applications Web entrent dans une ère nouvelle, où le développement des applications
de gestion atteint des niveaux de productivité qui compensent rapidement l’investissement en apprentissage. L’utilisation de bibliothèques telles que struts-layout est indispensable pour y parvenir.
z
ALLER PLUS LOIN
Sur le Web
Struts :
http://jakarta.apache.org
Le résultat visuel se trouve en figure 4.
Figure 3. Présentation des erreurs avec strutslayout.
• Mise en page automatique des éléments
contenus dans une collection en spécifiant
uniquement les champs à afficher.
• Look modifiable par fichiers CSS. Les css permettent de spécifier dans un fichier externe
les éléments de style (polices, alignement,
couleurs…) à utiliser pour les différents éléments d’une page HTML. Avec la bibliothèque struts-layout, il est possible de changer
de fichier de façon dynamique. Les applications deviennent skinnables.
Documentation Struts :
http://jakarta.apache.org/struts/api/org/apache/struts/
util/package-summary.html#package_description
Tag extension, spécification JSP 1.1, chapitre 5 :
http://java.sun.com/products/jsp/download.html
Struts-layout :
http://struts.application-servers.com
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
46
Langage
PARMI LES PROJETS OPEN SOURCE LES PLUS PROMETTEURS, NOUS AVONS CHOISI DE VOUS PRÉSENTER
LE MAPPEUR OBJET RELATIONNEL CASTOR, UN PRODUIT RICHE ET SIMPLE À UTILISER, QUI INTÉRESSERA
LES DÉVELOPPEURS JAVA TOUTES EXPÉRIENCES CONFONDUES. L’UN DES OBJECTIFS DE CE TYPE D’OUTIL
EST DE FAIRE GAGNER DU TEMPS DANS LE DÉVELOPPEMENT D’APPLICATIONS.
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
L
’une des plus gran
des difficultés que
l’on peut rencon
trer dans le déve
loppement d’applications Web est de pouvoir accéder aux données en tenant
compte du contexte particulier
lié aux applications Internet et
intranet. Dans le monde Java, cet
accès aux données repose le plus
souvent sur une technologie permettant de représenter sous une
forme orientée objet les informations qui se trouvent stockées
dans une base de données relationnelle.
Le mapping
objet
relationnel
Pourquoi utiliser un
outil de mapping ?
Le but premier est de gagner
du temps dans le développement d’une application (figure 1).
Dans le cadre d’un modèle multicouche (figure 2), les services
proposés par un outil de
mapping vont se positionner entre la couche physique et la cou-
Figure 2.
Figure 1.
François Maurit,
ingénieur de
développement
spécialisé dans la
mise en œuvre
d’architectures J2EE
chez Improve.
[email protected].
che entreprise (pour plus d’informations sur le modèle multicouche, voir Développeur Référence
v1.0 et v1.1). Ces services vont
transformer les données de la
couche physique en objets utilisables pour la couche entreprise.
Citons quelques-uns des
principaux outils commerciaux :
Toplink de WebGain Inc., connu
également pour ses outils Java
tels que WebGain Studio et
VisualCafe ; CocoBase de Thought
Inc., adapté à de multiples SGBDR
et serveurs EJB ; WebObjects/EOF
d’Apple, bientôt disponible en
Java. En Open Source, les deux
Objet
Relationnel
Mapping
DÈpendances
Couche Client
Couche Application
Couche Entreprise
Couche Mapping
Couche Physique
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
47
Langage
solutions les plus référencées sont Castor et
ObjectBridge, hébergé sur Sourceforge.
Pourquoi utiliser un SGBDR avec un outil
de mapping plutôt qu’un SGBDOO ? Aujourd’hui, les systèmes de base de données objet
sont suffisamment matures pour être utilisés
dans un environnement de production. Toutefois, malgré leur inadéquation avec le
monde objet, les systèmes relationnels restent dominants. Les bases installées sont
majoritairement des SGBDR. Or, ces derniers
existent depuis vingt ans et ont une large
avance en termes d’optimisation et de diffusion. Pour des modèles objets simples (la
majorité des projets), un SGBDR reste plus
performant qu’un SGBDOO.
Gestion du cache
Tous les outils de mapping présentent un
cache (figure 3) que chacun d’eux utilise de façon spécifique, avec deux objectifs principaux.
Le premier, diminuer le nombre des accès en
base. Quand un objet déjà récupéré dans la
base est demandé, une version présente en
mémoire s’active automatiquement. La durée
de rafraîchissement du cache et sa désactivation sont en général paramétrables. Second
objectif, assurer la cohérence entre les objets.
Lorsque deux threads accèdent au même objet, les modifications sont répercutées dans le
cache pour éviter les conflits de versions.
Objet
Cache
Relationnel
Figure 3.
Construction du mapping
Les outils commerciaux proposent généralement une interface graphique pour la création
du mapping, tandis que les outils Open Source
favorisent l’utilisation d’un fichier de configuration. Les outils commerciaux apportent donc
souvent une meilleure productivité. C’est le rapport prix/productivité qui demande à être bien
évalué (figure 4).
Introduction à Castor
Le projet Castor
Castor est un outil de mapping, hébergé
par Exolab, assurant d’une façon générale le
passage entre le monde objet et les SGBDR.
Deux types de structures sont supportées :
Castor XML, un outil prometteur dans la gestion de fichiers XML, et Castor JDO, qui gère
la connexion avec la majorité des bases de
données relationnelles du marché. Dans cet
Figure 4.
article, nous traiterons uniquement de Castor JDO. Castor se distingue par une interprétation personnelle de deux des concepts les
plus innovants en matière de gestion des objets persistants : la spécification Java Data
Objects, qui fait partie des suggestions d’extensions du langage Java proposées dans le
cadre du Java Community Process (JSR 012) ;
l’Object Query Language (OQL) défini par
l’Object Data Management Group (ODMG),
un langage 100 % objet inspiré de SQL. Associé à Castor JDO, celui-ci permet de ne manipuler que des objets. On occulte ainsi tous les
aspects relationnels lors du développement
de la couche Entreprise.
Exolab est un groupe de travail centré surtout sur Java et XML. Il est financé par la société Intalio, dont les développeurs participent
aux projets regroupés au sein d’Exolab. Outre
Castor, Exolab héberge surtout des applications de type middle-tier.
Le code ci-dessous permet d’établir une
connexion et de créer un nouvel article dans
la base :
1. import mapping.Article;
2. import org.exolab.castor.jdo.*;
3. class Test {
4. public static void main(String[] args) {
5. Article article = new Article();
6. article.setTitre("accéder
aux données avec castor");
7. JDO jdo = new JDO("articles");
8. jdo.setConfiguration("database.xml");
9. try {
10. Database db = jdo.getDatabase();
11. db.begin();
12. db.create(article);
13. db.commit();
14. } catch(PersistenceException e) {
15. e.printStackTrace();
16. } finally {
17. db.close();
18. }
19. }
20. }
La ligne 5 construit un bean à l’aide d’un
constructeur sans argument, requis par Cas-
tor pour l’instanciation. Les lignes 7 et 8 permettent de configurer Castor, via un fichier
externe au code Java : “database.xml”. La ligne
10 effectue la connexion avec le SGBDR. Castor interprète alors le fichier de configuration
(ce traitement coûteux n’est mis en œuvre
qu’au premier appel de la méthode
getDatabase()). Les lignes 11 et 13 marquent
les limites de la transaction. Enfin, la ligne 17
relâche la connexion. Dans le cas d’un driver
JDBC 2.0 ou de l’utilisation d’un mécanisme
de pooling, celle-ci n’est pas refermée, mais
conservée pour plus tard.
Fichier de configuration
Castor comprend des adapteurs dédiés
aux bases de données suivantes : Oracle,
Sybase, SQL Server, DB2, Informix, Hypersonic
SQL, InstantDB, Interbase, MySQL,
PostgreSQL, SAP DB. Ces adapteurs ne remplacent pas les drivers JDBC, également nécessaires. Ils permettent un niveau d’abstraction supplémentaire en gérant les interprétations propriétaires de la norme SQL propres
à chaque SGBDR. Un adapteur générique est
également proposé, le seul prérequis étant
dans ce cas l’existence d’un driver répondant
à la norme JDBC2 (c’est ainsi que certains projets utilisent Castor pour l’accès à des systèmes AS/400). La configuration de l’adapteur
se fait en XML dans un fichier externe :
1. <!— DOCTYPE databases PUBLIC "-//EXOLAB/
Castor JDO Configuration DTD Version 1.0//EN"
"http://castor.exolab.org/jdo-conf.dtd"—>
2. <database name="articles" engine="db2" >
3. <driver class-name=
"COM.ibm.db2.jdbc.app.DB2Driver"
4. url="jdbc:db2:castor">
5.
<param name="user" value="castor" />
6.
<param name="password" value="jdo" />
7. </driver>
8. <mapping href="mapping.xml" />
9. </database>
La ligne 2 permet de déclarer l’adapteur.
Ici, la base de données à utiliser est identifiée
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
par le mot-clé “articles”, et la base que devra
adresser le driver est une base db2. Les lignes
3 et 4 déclarent le driver JDBC devant être mis
en œuvre pour accéder à la base. La ligne 7
fait référence au fichier qui décrit le mapping
objet relationnel. Pour la suite de notre démonstration, ce fichier de description sera
supposé être présent dans le répertoire de
travail sous le nom “database.xml”.
Fichier de mapping
Le mapping permet d’associer des classes
Java aux tables de la base de données (figure
5).
Article
id : Integer
titre : String
48
Langage
Table article_table
Mapping
Nom de
la colonne
id
titre
Type des
donnÈes
Integer
Varchar (200)
Construction du
mapping
Les relations
On doit également modifier la classe
Article :
Les relations sont un point sensible du
passage entre monde objet et monde relationnel. En effet, alors qu’un SGBDR ne présente qu’une série d’enregistrements, il va falloir construire un modèle dans lequel chaque
relation peut être parcourue à volonté.
Les relations 1 vers 1
Table exemple_table
Exemple
id : Integer
Figure 5.
Mapping
Nom de
la colonne
Type des
donnÈes
id
Integer
id article
Integer
1. <!— DOCTYPE databases PUBLIC "-//
EXOLAB/Castor Mapping DTD Version 1.0//
EN" "http://castor.exolab.org/
mapping.dtd" —>
2. <mapping>
3. <class name="mapping.Article"
identity="id">
4. <description>
5. Article pour Developpeur Reference.
6. </description>
7.
8. <map-to table="article_table" />
9.
10. <field name="id" type="integer">
11. <sql name="id" type="integer" />
12. </field>
14. <field name="titre" type="string">
15. <sql name="titre" type="varchar" />
16. </field>
18. </class>
19. </mapping>
La ligne 3 fait la relation entre le mapping
et la classe Java associée, qui est un simple
bean :
package mapping;
public class Article {
protected Integer id;
protected String titre;
public Article() {}
public Integer getId() { return id; }
public String getTitre()
{ return titre; }
public void setId(Integer id)
{ this.id = id; }
public void setTitre(String titre)
{ this.titre = titre; }
}
Une section délimitée par les balises “class”
sera ajoutée entre les balises “mapping” pour
chaque classe dont on déclarera le mapping.
La ligne 8 définit la table qui va être mappée
(“article_table”), les lignes 10 à 12 et 14 à 16
le mapping entre les attributs du bean et les
colonnes de la table “article_table”.
protected final Collection exemples;
public Article() { exemples =
new ArrayList(); }
public void addExemple(Exemple exemple) {
exemples.add(exemple);
//la ligne suivante évite toute
//incohérence en exécution
exemple.setArticle(this);
}
public Collection getExemples()
{ return exemples ; }
Exemple_table
Exemple
id
id : Integer
1
Pour notre exemple, le fichier de mapping
est le suivant :
type="mapping.Exemple"
collection="collection">
<sql many-key="article" />
</field>
</class>
0..N
Article
Mapping
id : Integer
titre : String
Une clé étrangère est déclarée de la façon
suivante :
Le type de la clé SQL est déterminé par
Castor à partir de celui de la clé primaire déclarée pour la classe Exemple (donc “integer”
dans notre cas). La classe Java associée s’écrit
ainsi :
package mapping;
public class Exemple {
//le constructeur et l’identifiant
//doivent aussi être ajoutés
protected Article article;
public Article getArticle()
{ return article; }
public void setArticle(Article article) {
this.article = article; }
}
Les relations 1 vers N
A présent que la relation Exemple vers Article a été définie, l’ensemble des exemples
de l’article se définit de la façon suivante :
<class name="mapping.Article" identity="id">
...
<field name="exemples"
73
Article_table
Article
<class name="mapping.Exemple" identity="id">
<description>
Un des exemples de l’article.
</description>
<map-to table="exemple_table" />
<field name="id" type="integer">
<sql name="id" type=vinteger" />
</field>
<!— declaration de la cle etrangere —>
<field name="article"
type="mapping.Article">
<sql name="id_article" />
</field>
</class>
73
3
1
Figure 6.
id_article
73
1
2
id
Titre
73
Castor
Figure 7.
Il est possible de déclarer des conteneurs
autres que l’interface java.util.Collection, par
exemple java.util.Vector (notamment pour la
compatibilité avec les JDK antérieurs au 1.2).
Les relations M vers N
Article_table
id
71
72
73
Article
id : Integer
titre : String
0..N
0..N
Auteur
id : Integer
titre : String
Mapping
Titre
Open Source
Struts
Castor
Article auteur_table
id_article
71
72
73
73
id_auteur
1
1
1
2
Auteur_table
id
1
2
Nom
Girard
Maurit
Figure 8.
Dans un SGBDR, ce type de relation nécessite obligatoirement une table spécifique.
La déclaration se fait ainsi :
<class name="mapping.Article" identity="id">
...
<!— auteurs ayant ecrit cet article —>
<field name="auteurs" type="mapping.
Auteur"collection="collection">
<sql many-key="id_article" name="id_
auteur" many-table="article_auteur_table" />
</field>
</class>
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
49
Langage
L’écriture des accesseurs nécessaires se
fait de la même façon que pour une relation
1 vers N. La relation réciproque (articles écrits
par un auteur) peut aussi être déclarée mais
n’est pas obligatoire.
L’héritage
Soit le diagramme UML de la figure 9. Il
existe trois façons principales de définir le
mapping objet relationnel. Chacune a ses
avantages et ses inconvénients. Toutes ne
sont pas supportées par Castor dans la version testée. Les classes Java restent inchangées dans la plupart des cas.
Auteur
ExtÈrieur
Journaliste
SociÈtÈ
Bureau
Figure 9.
Héritage vertical
Voir Figure 10. Nous en avons déjà vu la
syntaxe lors de la discussion sur l’optimisation
du stockage. On définit une table pour la
superclasse et une pour chaque sous-classe.
La deuxième ne contient que les informations
différentes : il faut donc faire une jointure chaque fois qu’une classe fille est demandée.
La déclaration de la classe Auteur n’est pas
modifiée. On définit le mapping de la classe
Exterieur de la façon suivante (la déclaration
de la classe Journaliste est similaire) :
<class name="mapping.Exterieur" identity="id"
extends="mapping.Auteur">
<description>
Auteur n’appartenant pas a la redaction.
</description>
<map-to table="exterieur_table" />
<field name="societe" type="string">
<sql name="societe" type="varchar" />
</field>
</class>
Héritage horizontal
Voir figure 11. On définit une table pour
chaque objet. C’est particulièrement intéressant si la classe Auteur est abstraite : les instances appartiennent donc soit à la classe
Exterieur, soit à la classe Journaliste, chacune
possèdant sa propre table. Ce qui évite notamment le surcoût lié aux jointures. La classe
Auteur est donc déclarée abstraite et retirée
du fichier de mapping. La classe Exterieur
devient :
<class name="mapping.Exterieur"identity="id">
<description>
Auteur n’appartenant pas a la redaction.
</description>
<map-to table="exterieur_table" />
<field name="id" type="integer">
<sql name="id" type="integer" />
</field>
<field name="nom" type="string">
<sql name="nom" type="varchar" />
</field>
<!—
<field name="articles" type="mapping.
Article" collection="collection">
<sql many-key="auteur" name="article"
many-table="article_auteur_table" />
</field>
—>
<field name="societe" type="string">
<sql name="societe" type="varchar" />
</field>
</class>
On remarque que ne pas déclarer l’héritage dans la partie relationnelle de l’application restreint les possibilités. Ainsi, on ne peut
plus se référer à une classe commune pour la
relation N vers M entre auteurs et articles. Mais
cette approche peut être intéressante pour
des modélisations plus simples.
Figure 10.
Auteur_table
ExtÈrieur_table
Journaliste_table
Nom de
la colonne
Type des
donnÈes
Nom de
la colonne
Type des
donnÈes
Nom de
la colonne
Type des
donnÈes
id
Integer
id
Integer
id
Integer
Nom
Varchar (100)
SociÈtÈ
Varchar (100)
Bureau
Integer
Figure 11.
ExtÈrieur _table
Journaliste _table
Nom de la colonne
Type des donnÈes
Nom de la colonne
Type des donnÈes
id
Integer
id
Integer
Nom
Varchar (100)
Nom
Varchar (100)
SociÈtÈ
Varchar (100)
Bureau
Integer
Filtered mapping
Auteur_table
Nom de la colonne
Type des donnÈes
id
Integer
Nom
Type
Bureau
Varchar (100)
Integer
Integer
SociÈtÈ
Varchar (100)
Figure 12.
On utilise une table commune à tous les
objets. Une colonne supplémentaire permet
généralement d’identifier pour chacune des
lignes de la table la classe concernée. Cette
approche conduit à la création de nombreux
champs vides mais peut être très efficace.
Le filtered mapping n’est pas supporté par
Castor 0.9.2. Dans le cas d’une base existante,
on peut contourner cette restriction par l’utilisation de vues partielles, mais cela peut
s’avérer coûteux. Pour cela, on déclare le
mapping complet des classes Auteur,
Exterieur et Journaliste sans préciser le lien
d’héritage, mais en les liant à la même table :
<map-to table="auteur_table" />
On peut alors récupérer les objets de chaque classe de la façon suivante :
Auteur auteur = (Auteur) db.getOQLQuery
("SELECT a from mapping.Auteur a")
.execute.next();
if (auteur.getType() == 1) {
Exterieur ext = (Exterieur)
db.load(Exterieur.class, auteur.getId());
} else if (auteur.getType() == 2) {
Journaliste jou = (Journaliste)
db.load(Journaliste.class, auteur.getId());
}
Génération
des clés primaires
Castor intègre plusieurs types de générateurs de clés primaires dont la déclaration se
fait dans le fichier de mapping. Par exemple,
l’algorithme classique MAX est déclaré de la
façon suivante :
<class name="mapping.Article" identity="id" keygenerator="MAX">
La clé doit être un entier. Castor propose
deux autres algorithmes génériques : UUID,
qui agrège la date en seconde depuis 1970,
l’adresse IP de la machine et un compteur statique (chaîne de caractères), et HIGH/LOW, qui
réserve des séquences de clé dans la base ellemême (paramétrable).
Enfin, deux mécanismes spécifiques sont
proposés : Identity pour Sybase ASE/ASA,
MSSQL Server, MySQL et Hypersonic SQL, et
Sequence (paramétrable) pour Oracle,
PostgreSQL, Interbase et SAP DB.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
50
Langage
Utilisation du
mapping
Castor génère de nombreuses exceptions
qu’il convient de traiter correctement. Afin de
faciliter la lecture du code, elles ne sont pas
reportées ici.
Création
Une fois mis en place le mapping et les
tables associées dans la base (Castor utilisé
seul ne gère pas la génération des tables), on
peut créer des objets de la façon suivante :
package entreprise;
import org.exolab.castor.jdo.*;
import mapping.*;
public class GestionAuteurs {
protected final JDO jdo;
//objet JDO instancié comme décrit
//dans la partie "Exemple d’utilisation"
public GestionAuteurs(JDO jdo) {
this.jdo = jdo;
}
public void créer(String nom) {
Database db = jdo.getDatabase();
db.begin();
Auteur auteur = new Auteur();
Auteur.setNom(nom);
db.create(auteur);
db.commit();
db.close();
}
}
Retrouver les objets
de la base
Deux méthodes assurent l’accès aux données :
La méthode “load” :
public Auteur parId(Integer id) {
Database db = jdo.getDatabase();
db.begin();
Auteur result = (Auteur) db.load
(Auteur.class, id);
db.commit();
db.close();
return result;
}
Exécution d’une requête au
format OQL :
public Auteur parNom(String nom) {
Database db = jdo.getDatabase();
db.begin();
Query oql = db.getOQLQuery("SELECT
a from mapping.Auteur a WHERE a.nom=$1");
oql.bind(nom);
QueryResults results =
oql.execute();
Auteur result = (Auteur)
results.next();
}
//l’appel de results.close() est
//inutile ici
//à cause de la ligne ci-dessous
oql.close() ;
db.commit();
db.close();
return result;
Citons trois autres exemples de requêtes
supportées par Castor.
Trier les auteurs par ordre alphabétique :
SELECT a FROM mapping.Auteur ORDER BY a.name
asc
Compter le nombre d’articles
d’un auteur (retourne un objet de
type Integer) :
SELECT COUNT(a.id) FROM mapping.Article WHERE
a.auteur=3
Exemples qui n’ont été affectés à
aucun article :
On remarque que les changements effectués sur les données de l’objet “auteur” ne
sont pas pris en compte s’ils ont lieu hors
transaction.
Enfin, on peut supprimer un objet à l’aide
de la méthode remove :
public void supprimer(Auteur auteur) {
Database db = jdo.getDatabase();
db.begin();
db.update(auteur);
db.remove(auteur);
db.commit();
db.close();
}
Une bonne implémentation séparerait
l’interface (accès en lecture uniquement) et
la classe de mapping (masquée aux classes
n’ayant pas à agir au niveau mapping) en utilisant un modèle multicouche.
Au final, Castor est un produit riche, puissant et simple d’utilisation. C’est un projet
Open Source dynamique. Il peut et est déjà
utilisé pour les projets demandant une montée en charge limitée. Castor est définitivement un logiciel à surveiller.
z
SELECT e FROM mapping.Exemple WHERE is_
undefined(p.article)"
Mise à jour
et suppression
On peut soit récupérer à nouveau un objet en base, soit utiliser un objet déjà présent
en mémoire, qu’il convient alors de mettre à
jour :
//auteur récupéré à partir de son nom
//supposé unique
public void changerNom(String ancienNom,
String nouveauNom) {
Database db = jdo.getDatabase();
db.begin();
Query oql = db.getOQLQuery("SELECT
a from "+Auteur.class+" a WHERE a.nom=
$1");
oql.bind(ancienNom);
QueryResults results = oql.execute();
Auteur auteur = (Auteur) results.
next();
oql.close() ;
auteur.setNom(nouveauNom);
db.commit();
db.close();
}
//auteur récupéré avec l’une
//des méthodes décrites
//dans le paragraphe précédent
public void changerNom(Auteur auteur,
String nouveauNom) {
Database db = jdo.getDatabase();
db.begin();
db.update(auteur);
auteur.setNom(nouveauNom);
db.commit();
db.close();
}
ALLER PLUS LOIN
Le modèle multicouche :
http://www.application-servers.com/articles/multicouches/
http://www.application-servers.com/articles/
multicouchesjava/
SGBDR contre SGBDOO :
http://www.kuro5hin.org/?op=displaystory;sid=2001/5/
3/32853/11281
A propos du mapping objet relationnel :
http://www.objectmatter.com/vbsf/docs/maptool/
ormapping.html
Le projet Castor :
http://castor.exolab.org
Intalio :
http://www.intalio.com
Castor Doclet :
http://pages.infinit.net/bansi/
L’Object Data Management Group :
http://www.odmg.org
Java Data Objects (JDO) Specification :
http://access1.sun.com/jdo/
http://java.sun.com/aboutJava/communityprocess/jsr/
jsr_012_dataobj.html
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
51
Outil
APRÈS AVOIR PRÉSENTÉ LES ENJEUX DU PROJET OPEN SOURCE ECLIPSE
ET L’ARCHITECTURE DE CETTE PLATE-FORME, NOUS MONTRERONS LA PERTINENCE
DE WSAD POUR LE DÉVELOPPEMENT D’APPLICATIONS CÔTÉ SERVEUR,
QU’ELLES SOIENT
J2EE, XML OU SERVICES WEB.
L
Didier Girard,
directeur technique
Technologies &
Solutions d’Improve.
Expert J2EE et XML,
créateur du site
portail applicationservers.com et
animateur
du site de littérature
francophone
abu.cnam.fr.
[email protected]
« Je remercie
sincèrement
Bruno Paul
pour l’aide qu’il m’a
apporté lors de la
rédaction
de cet article ».
e 5 novembre 2001,
IBM a annoncé qu’il
donnait sous licence Open Source
une plate-forme de
développement d’applications
nommée Eclipse. Ce cadeau
d’IBM à la communauté des développeurs est estimé à 40 millions de dollars.
Cette annonce sonne le glas
de VisualAge for Java au profit
d’une toute nouvelle famille
d’outils basés sur Eclipse, qui reprend le nom WebSphere Studio.
Le premier outil de cette génération est WSAD (WebSphere Studio Application Developer).
Les atouts
de l'Open Source
Dans cet article, nous reprendrons pour le terme “Open
Source“ la définition de l’OSI
(Open Source Initiative). On utilisera également le terme logiciel
libre comme synonyme, bien que
les deux termes ne soient pas forcément toujours équivalents.
Un raccourci rapide permet
de dire qu'un logiciel est Open
Source si le code source de ce logiciel est disponible, si ce code
source est modifiable et ce code
source peut être redistribué
Le fait qu'un logiciel soit
Open Source ne signifie pas pour
autant qu'il soit risqué de l'utiliser. La preuve : les plus grands
éditeurs tels que Sun, IBM,
Microsoft ou HP s'appuient sur
des logiciels Open Source pour
développer leur gamme de produits. Ils ont choisi de s'appuyer
sur des logiciels libres car ils sont
de grande qualité. En effet, les
meneurs des projets Open
Source phares sont toujours des
personnes très compétentes qui
s'investissent dans le logiciel libre
par défi technique et pour la reconnaissance de leurs pairs.
Le marketing autour des projets Open Source laisse entendre
que les logiciels Open Source
sont plus stables, que le respect
des standards et la qualité du
support sont meilleurs. Ces avantages ont cependant un prix, la
documentation associée aux projets Open Source est souvent très
succincte et il est parfois difficile
pour un débutant d'installer ces
logiciels et de les utiliser. C'est
pourquoi le marché de l'Open
Source n'existe qu'associé à un
marché de services. Bon nombre
de sociétés ont compris cet enjeu et proposent des services permettant de faciliter l'utilisation
des logiciels libres.
Le code source des logiciels
Open Source étant disponible, un
développeur peut comprendre le
fonctionnement des logiciels,
corriger des bugs, ou encore procéder à des évolutions. Si ces évolutions ne sont pas acceptées par
le mainteneur du projet, il est possible de créer un projet concurrent (fork). Chaque développeur
de la communauté décide alors
de participer à l'un ou l'autre, ou
aux deux projets à la fois. L'histoire de l'Open Source montre
que les forks sont très rares.
La taille de la communauté
permet une sélection darwinienne des projets : les plus valides techniquement survivent, les
autres sont mis en sommeil, disparaissent ou fusionnent. Même
dans des niches écologiques
étroites, où il peut n'y avoir
aucune concurrence, un projet
ne peut persister très longtemps
sans soutien de développeurs ou
d'utilisateurs qui motivent le
mainteneur à rajouter de nouvelles fonctionnalités.
IBM
et l'Open Source
Le soutien du mode de développement Open Source par IBM
ne date pas du 5 novembre dernier. Avant Eclipse, IBM maintenait un grand nombre de projets
Open Source et participait très
activement à des projets majeurs : Apache Jakarta, Mozilla,
Apache Web Server, Apache XML
ou encore Linux.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
52
Outil
fin novembre a rassuré : la licence est la
“Common Public License“, reconnue par l'OSI.
Le projet est géré par un consortium, constitué aujourd'hui de dix entreprises avec des
droits de vote équivalents : Borland, IBM,
Merant, QNX, Rational Software, RedHat, SuSE,
TogetherSoft et WebGain. L'accès de nouveaux
participants à ce comité obéit à des règles habituelles dans les projets Open Source : le projet Eclipse est une “méritocratie“.
L’architecture
d’Eclipse
Figure 1. Architecture de la plate-forme Eclipse.
Les composantes que l'on retrouve le plus
souvent dans les projets Open Source soutenus par IBM sont XML, Java, Linux et les services Web. Le point commun entre ces technologies est la portabilité :
• Java est un langage portable, ce qui permet de faire disparaître le coût
du portage d'une application d'un système
à un autre.
• Linux est un système “portable“ qui permet
de limiter les coûts de portage d'une application native d'une plate-forme à une autre.
• XML assure la portabilité des données.
• Les services Web assurent un accès portable aux applications.
Qu'IBM soutienne des projets qui vont lui
permettre de maintenir la qualité des services offerts par ses plates-formes matérielles
(iSeries, pSeries, xSeries et zSeries) tout en diminuant le coût de développement de ces
services n'a donc rien d'étonnant.
L'Open Source ne représente pas une
menace pour IBM : outre les indéniables qualités de l'Open Source que nous avons déjà
énoncées, l'avantage pour IBM est qu'un logiciel libre n'appartient à aucune entreprise.
C'est pourquoi le succès de l'Open Source ne
devrait pas dresser un futur concurrent en
face d'IBM.
Pourquoi Eclipse ?
Avoir une stratégie sur la portabilité des
applications, c'est bien, avoir une communauté de développeurs pour développer ces
applications portables, c'est mieux. Il était
donc important, voire urgent, pour IBM de
proposer à la communauté des développeurs
un outil de développement. C'est l'objectif
d'Eclipse.
Avec Eclipse, IBM propose une nouvelle
étape dans la standardisation. Pour contrer
Microsoft et son outil VisualStudio .NET, il
ouvre les spécifications d'une plate-forme de
développement d'outils qui a nécessité deux
ans d'effort. Plutôt que d'être le seul acteur à
utiliser cette ambitieuse plate-forme de développement, IBM crée un nouveau marché.
Grâce à la JVM, socle d'Eclipse, ce marché sera
multi plates-formes matérielles (PC, serveurs,
mainframes, assistants personnels…). Parions
qu'IBM ne sera pas long à retirer des bénéfices de ce marché, puisqu'il se positionne déjà
avec son produit WebSphere Studio Application Developer, construit sur Eclipse. La dynamique autour d'Eclipse est semblable à celle
qui consiste à baisser le taux d'imposition,
pour faire repartir la consommation et prélever au final plus d'impôts.
La création du projet Open Source Eclipse
montre qu'IBM a parfaitement assimilé le nouveau principe de base de l'édition d'outils :
celui qui gagne des parts de marché est celui
qui a la plus grosse communauté de développeurs. Ce principe est apparu et s'est imposé
avec le Web, médium de communication. Il
existe plusieurs façons de créer une communauté : le marketing, la diffusion des outils (par
exemple par le modèle Open Source),
le leadership technologique ou une meilleure
productivité apportée par l'ergonomie des
outils.
Eclipse n'est plus IBM
IBM avait annoncé depuis juin dernier son
souhait de donner Eclipse à la communauté
Open Source. On l’attendait sur
la licence et le mode de fonctionnement du
projet. Le lancement officiel du projet Eclipse
Eclipse a été conçu par Object Technology
International (OTI, filiale d’IBM), qui a une très
forte expérience du développement de produits : depuis 1988, cette équipe a produit
VisualAge Smalltalk, VisualAge for Java et
VisualAge Micro Edition. L'un des trois
mainteneurs du projet Eclipse est Erich
Gamma, co-auteur du célèbrissime “Design
Patterns“, bible de tous les développeurs soucieux d'architecture. Le projet Eclipse est déjà
très avancé, puisque la version 1.0 est disponible et que la version 2.0 en développement
est prévue pour avril 2002.
Le projet Eclipse est constitué de trois sous
projets : la plate-forme, le Java Development
Tools (JDT ) et le Plug-in Development
Environment (PDE).
La plate-forme
Elle définie un ensemble de frameworks et
de services communs pour assurer une
interopérabilité des outils qui se rajoutent par
dessus (plug-ins). C'est l'équivalent d'un kernel
pour un OS. Elle comprend un système de gestion concurrente de ressources distribuées
VCM (Versioning and Configuration Management), une gestion de modèle de projets, une
gestion automatique des deltas entre versions
pour les compilations incrémentales, une infrastructure de debug indépendante des langages de développement, un framework d'extension des fonctionnalités par plug-ins, une
bibliothèque graphique portable SWT (Standard Widget Toolkit), un framework pour une
interface graphique portable (JFace), un système générique d'aide, de recherche, de comparaison, de mise à jour des plug-ins, des
parseurs, l'intégration de Jakarta Ant, le support de scripts, etc.
Comme tout le reste d'Eclipse, la plateforme est intégralement codée en Java, à l'exception d'une unique DLL de 232Ko en C. La
JVM utilisée est interchangeable à souhait,
Eclipse supporte la JVM 1.3 aux dernières bêtas de la JVM 1.4. Des JVM antérieures peuvent être utilisées mais un certain nombre de
fonctionnalités seront inopérantes.
La DLL mentionnée est une partie du
Standard Widget Toolkit. Cette nouvelle bibliothèque graphique a surpris toute la com-
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
53
Outil
Le Java
Development Tools
munauté Java. SWT/JFace se positionne
comme un concurrent de Swing. Toute l'interface graphique d'Eclipse est bâtie sur SWT.
Swing n'est pas pour autant passé à la trappe,
on peut toujours développer et tester des
composants AWT ou Swing avec Eclipse, mais
il n'existe pas pour l'instant d'environnement
de programmation visuelle.
IBM est le concepteur de SWT, dont la première version de production date de mars
2000. IBM utilise déjà cette bibliothèque dans
des produits commerciaux, en témoigne l'interface utilisateur de VisualAge for Java Micro Edition 1.1. De manière générale, IBM va
tendre à utiliser la plate-forme Eclipse pour
migrer le maximum de ses produits logiciels.
Pour autant, IBM a affirmé ne pas vouloir imposer SWT comme standard. SWT est à la disposition de la communauté Java, c'est elle qui
devra en assurer la promotion si elle l'estime
nécessaire.
Quels sont donc les avantages de SWT par
rapport à Swing ?
• SWT est moins complexe : environ 200 classes pour SWT contre plus de 1000 pour Swing.
• SWT est plus flexible : il permet des appels
directs à l'API graphique de l'OS. On peut donc
coder des appels à des ActiveX sur Win32
(mais au détriment de la portabilité).
• SWT est aussi rapide qu'une bibliothèque
native, notamment pour le rafraichissement
des composants.
• Les applications SWT sont mieux intégrées :
elles ressemblent vraiment à des applications
natives, les primitives graphiques utilisées
sont directement celles de l'OS.
• SWT est plus facilement portable : 232 Ko de
code natif pour SWT dans le JDK 1.3, contre
960 Ko pour Swing.
Il existe aujourd'hui des implémentations
complètes de SWT sur Win32 et Motif (Linux,
AIX, Solaris). Un développement sur GTK/
Linux, Motif/HP-UX et Photon/QNX est en
cours. L'utilisation de GTK permettrait l'appel
à des composants Bonobo, là aussi au détriment de la portabilité. Pour assurer une reprise de l'existant, des ponts AWT-SWT et
Swing-SWT sont en développement, mais le
problème est ardu.
En cassant le paradigme de la portabilité
totale des Swing, SWT laisse une plus grande
liberté au programmeur. En choisissant ou pas
d'intégrer des composants spécifiques de l'OS
où il se trouve, il peut choisir entre portabilité
et rapidité du développement. Et surtout,
SWT résout de manière élégante les problèmes de performance des grosses applications
Swing. Par contre, l'application doit libérer explicitement les ressources de l'OS allouées par
SWT. Elle ne peut pas se reposer sur le ramasse-miettes pour cela.
Si SWT reçoit un fort soutien de la communauté, le renouveau des développements
Java côté client est assuré.
Figure 2. Architecture des plug-ins.
Les plug-ins :
ne réinventez pas la roue !
Un autre intérêt majeur de la plate-forme
réside dans son architecture de plug-ins. Pour
mieux comprendre sa maturité, il faut détailler
les différents niveaux d'intégration offerts
pour un plug-in :
1. Invocation : un double-clic appelle la
cible.
2. Intégration des données : permet à des
plug-ins d'accéder à des données qui viennent d'être créées par d'autres plug-ins.
3. API : l'ajout d'un plug-in sur le système
rajoute des nouveaux comportements à
des plug-ins déjà installés. On n'accède
plus à des données comme précédemment, on réutilise un comportement.
4. Framework d'interface graphique : permet de construire une interface utilisateur.
La découverte et la mise à jour des nouveaux plug-ins est dynamique. Mieux, les
plug-ins peuvent eux-mêmes comporter des
points d'extensions, et supporter leurs propres plug-ins, lesquels peuvent utiliser tous
les services de la plate-forme.
La prolifération de ces plug-ins n'alourdit
pas inutilement l'application. En effet, bien
que leurs présences et leurs fonctionnalités
soient reconnues dès l'installation, ils ne sont
pas chargés en mémoire avant leur utilisation
réelle. Il existe aussi un mécanisme de déchargement des plug-ins de la mémoire, quand
l'utilisateur ferme un outil par exemple. L'empreinte mémoire de l'application est ainsi fortement réduite. Je recommande d'utiliser
Eclipse sur une station avec 180 Mo de RAM
(avec Windows NT4). Il est possible d'alléger
très simplement l'application en retirant les
plugins inutilisés et qui ne font pas partie du
Core
(composé
uniquement
de
org.eclipse.core.boot, org.eclipse.core.
runtime, org.apache.xerces). La plate-forme
est ainsi customisable et facilement
déployable dans des contextes très divers.
Le JDT (Java Development Tools) est un environnement de développement intégré pour
Java, très complet, qui utilise les services de la
plate-forme : éditeur de code, complétion contextuelle, debugger, JDK adaptable pour chaque projet, refactoring, gestion des versions,
comparaison, recherche, fusion de versions,
compilation incrémentale, build automatique
ou manuel, assistants de création.
C'est la partie la plus visible du projet
Eclipse, car c'est l'environnement qui s'ouvre
quand on lance Eclipse. Le JDT distingue ce
projet Open Source des autres : il propose un
IDE déjà parfaitement utilisable pour du développement Java professionnel, avec une
documentation complète. Le JDT est utilisé
par OTI depuis plusieurs mois pour développer les nouvelles fonctionnalités d'Eclipse
(statut dog food). Le mainteneur du projet JDT
est Erich Gamma. Il participe aux débats sur
le newsgroup et sur les listes de diffusion du
projet Eclipse. Il se voit assigner dans Bugzilla
la résolution d'un nombre important de problèmes.
Le JDT reprend une vue par fichier d'un
projet Java. Cependant on a accès à l'arborescence des méthodes dans le panel navigateur,
comme dans VisualAge for Java. Par défaut ,
on édite l'ensemble du source du fichier, bien
qu'une option permette de n'éditer que la
méthode sélectionnée. Tous les développeurs
Java trouveront leur bonheur. Nouveauté : le
javadoc peut être consulté en survolant la déclaration avec la souris.
Malgré sa maturité, le projet JDT n'est pas
terminé pour autant. Il manque par exemple
une génération de javadoc, un support aisé
de l'internationalisation d'applications
comme dans VisualAge for Java, des raccourcis clavier personnalisables, et un environnement de composition visuelle pour SWT, AWT
ou Swing. L'interface utilisateur est seulement
disponible aujourd'hui en anglais, mais l'architecture supportant l'internationalisation
de l'interface est en place. Très bientôt l'allemand et le japonais seront supportés, suivis
par l’hébreu et l’arabe.
Le Plug-in Development
Environment
Le PDE (Plug-in Development Environment) propose un ensemble d'outils pour faciliter le développement de plug-ins pour
Eclipse. On pourra créer facilement le fichier
manifest XML du plug-in, indiquer les ressources nécessaires à l'exécution, définir les points
d'extension, associer des fichiers XML Schema
aux plug-ins pour permettre une validation
des ressources spécifiées (on pense notamment à la notion de version, ce qui règle de
façon élégante le DLL HELL classique du développement dans un environnement
Windows).
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
54
Outil
Figure 3. La perspective Java avec l'organisation par défaut.
Prise en main :
les perspectives
L'ergonomie générale d'Eclipse est organisée en perspectives. Il s'agit de fenêtres rassemblant des options autour d'un thème
commun. La navigation dans Eclipse est complètement customisable : emplacement, présence, taille, empilement des panels, options
présentes. Cette versatilité peut dérouter au
début, mais permet à tous les développeurs,
quelles que soient leurs habitudes, de construire une ergonomie qui leur convienne. Il
existe sept perspectives par défaut, nous allons aborder les principales.
Perspective
Java
Le JDT est bien sûr constitué d'un ensemble de plug-ins sur la plate-forme. Il ne faut pas
restreindre Eclipse à un IDE Java, l'ajout de futurs plug-ins permettra de développer dans
d'autres langages. Il existe déjà un plug-in
Open Source pour le développement C/C++.
Sans surprise, la perspective Java rassemble tous les outils nécessaires pour développer des classes. Par contre, les habitués de
VisualAge for Java seront surpris par la néces-
sité d'enregistrer systématiquement les changements dans tous les fichiers ouverts avant
de changer de perspective. En effet, un fichier
ouvert dans une perspective n'est qu'une vue,
et ouvrir le même fichier dans une autre perspective ouvre une autre vue, qui ne sont pas
forcément toujours synchronisées !
Refactoring et compilation
incrémentale
Le JDT intègre des outils de refactoring,
prisés par la méthodologie Extreme
Programming. Il s'agit de transformer la structure du code sans modifier sa logique. On
peut par exemple :
• renommer un objet : toutes les références à
cet objet sont automatiquement modifiées ;
• extraire une méthode : la sélection de portion de code peut être transformée en méthode appelée avec un clic ;
• déplacer du code.
Toutes ces actions supportent le retour à
l'état initial.
Les fonctionnalités que nous venons de
citer sont encore loin de celles offertes par un
outil comme IDEA, mais gageons que la communauté Eclipse va se charger de les enrichir
régulièrement.
Il est aussi important de signaler que la
compilation incrémentale est déjà intégrée
dans Eclipse. Cette fonctionnalité permet de
recompiler uniquement le code modifié ainsi
que ses dépendances.
Perspective
debugger
Elle propose des services de debug très
classiques. C'est l'un des points en retrait par
rapport à VisualAge for Java :
• pas de point d'arrêt conditionnel ;
• pas de remplacement de code à chaud
(HCR) ;
• pas de passage méthode par méthode
quand elles sont chaînées au sein d'une ligne ;
• pas de reprise du debuggage en amont dans
le code (drop to frame).
Ces options très avancées étaient rendues
possibles dans VisualAge for Java par une JVM
supportant une API propriétaire IBM, et un
compilateur qui permettait une instrumentation plus fine du code. Une partie de ces fonctionnalités (dont le magique HCR) ont été
standardisées sous l'impulsion d'IBM, et seront présentes dans la JVM1.4. Le HCR commence à apparaitre dans les derniers builds
de développement d'Eclipse 2. WSAD 4.0
étant basé sur une branche Eclipse1.0, le HCR
est absent.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
55
Outil
Le débuggage sur serveur distant (remote
debugging) est possible avec l'IBM
Distributed Debugger, téléchargeable gratuitement (sous une licence propriétaire).
Travail
en équipe
Dans ce domaine critique, IBM a résolument joué la carte des standards.
Le Versionning et Control Management
d'Eclipse est livré avec des clients CVS et
WebDAV. La connexion sur un serveur CVS/
Unix est supportée avec les protocoles
pserver ou extssh (plus sécurisé). La connexion vers CVSNT/Windows NT-2000 n'est
recommandée qu'avec pserver, car le mode
sécurisé n'est qu'expérimental avec CVSNT.
Si l'on travaille seul, l'installation d'un serveur CVS pour gérer les versions n'est pas indispensable : il existe un historique local de
taille paramétrable, mais qui n'assure pas de
protection contre la perte de code en cas de
crash.
Figure 4. Besoins sur un projet J2EE : WSAD permet d'aborder cette complexité avec un seul outil.
La modélisation ne fait pas partie des outils livrés avec WSAD. Elle sera par contre bientôt
accessible puisque des éditeurs comme Rational et TogetherSoft annoncent l'intégration de leurs
outils dans Eclipse.
Pari gagné
pour IBM ?
WSAD
Comme tout développement logiciel,
Eclipse n'est pas exempt de bugs, mais à l'utilisation ils sont peu gênants. Comme tout projet Open Source, une gestion des problèmes
connus est consultable.
Outre ses atouts techniques indéniables, le
succès d'Eclipse résidera d'abord dans l'attraction d'une communauté de développeurs, et
d'éditeurs tiers qui s'engagent sur cette plateforme de développement. L'avenir nous dira si
Eclipse est un succès.
Le JDT est un superbe produit d'appel
pour la plate-forme Eclipse. Il est cantonné en
standard à du développement J2SE, mais on
peut importer Tomcat et bénéficier d'un environnement de développement Servlet/JSP
solide entièrement Open Source. Les développeurs désirant un niveau de productivité supplémentaire se tourneront vers WSAD.
Durant la phase de réalisation d'un projet
J2EE de nombreuses technologies sont manipulées par les développeurs (voir figure 4).
IBM positionne WebSphere Studio Application Developer (WSAD) comme l'environnement de développement qui va permettre de
gérer toute la complexité technologique d'un
projet J2EE.
Un environnement
de développement J2EE 1.2
WSAD 4.0 est un environnement de développement intégrant les fonctionnalités de
VisualAge for Java et de WebSphere Studio. Il
est composé d'un workbench basé sur Eclipse
1.0 et d'un ensemble de plug-ins permettant
de développer, tester et déployer des applications J2EE 1.2. Si Eclipse n'appartient pas à
Figure 5 : WSAD apporte un ensemble de perspectives qui vont permettre de traiter la complexité
d'un projet J2EE.
IBM, WSAD fait lui bien partie de l'offre IBM,
et à ce titre il est payant.
Les plug-ins WSAD sont rassemblés sous
forme de perspectives, une perspective étant
un point de vue sur le projet. WSAD 4.0 comporte neuf perspectives majeures (figure 5).
Les perspectives Java, Debug et Aide proviennent directement d'Eclipse. Les perspectives Web, J2EE, Serveur, Profiling, Data et XML
représentent les ajouts de WSAD. Ces ajouts seront détaillés par la suite.
Aujourd'hui, WSAD4.0 n'existe que sur
Win32. Une version pour Linux est en développement et sortira au premier semestre
2002. Elle pourrait être basée sur Eclipse 2.0.
Perspective
Web
La perspective Web intègre un environnement complet de développement Web. Cet
environnement fournit les outils nécessaires
au développement d'applications Web, cela
inclut : les pages Web statiques, les JSP, les
Servlets ainsi que le descripteur XML de déploiement.
Tous ces aspects sont réunis dans un environnement de travail unique permettant à
tous les intervenants d'un projet Web (auteurs
de contenu, infographistes, développeurs et
webmasters) de travailler ensemble avec le
même outil.
Parmi les fonctionnalités offertes, citons :
• Création, validation, édition de fichiers, JSP
et HTML
• Edition de graphismes et d'animations
• Support de l'édition de feuilles de style en
cascade (CSS)
• Moteur JavaScript (Rhino)
• Support de DHTML
• Importation HTTP et FTP
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
•
•
•
•
•
56
Outil
Exportation vers un serveur via FTP
Import/export de fichiers WAR
Visualisation des liens
Gestion et parsing des liens
Edition graphique du fichier web.xml
WSAD se veut l'outil collaboratif idéal
pour la création, l'assemblage, la publication,
le déploiement et la maintenance dynamique
d'une application Web.
Il semble évident qu'au moins une ligne
du cahier des charges de WSAD fixait pour
objectif de faire aussi bien, voire mieux, que
Dreamweaver et FrontPage pour la partie
code client, et aussi bien que FireWorks pour
la partie Web design, à la fois sur le fond et
sur la forme. Sur la forme, l'objectif est atteint
avec brio, l'ergonomie est familière, la prise
en main immédiate. Sur le fond, si l'on fait
une synthèse des points forts et des points
faibles de l'application, on peut en déduire
que la boîte à outil Web de WSAD fait le poids
face aux leaders du marché. Un intégrateur
et un designer peuvent travailler avec cet
outil sans trop perdre en productivité, quitte
à utiliser de manière anecdotique quelques
outils externes.
Une partie du plug-in Web Art Designer
n'est pas encore codée en Java, mais sous
forme d'exécutable Windows. Elle est donc
absente de la version Linux de WSAD 4.0, actuellement en bêta.
Perspective
J2EE
La perspective J2EE donne accès aux informations nécessaires pour créer et
configurer une application d'entreprise. Depuis cette perspective, il est donc possible de
manipuler les composants Web mais aussi de
créer des EJB 1.1 et de paramétrer le serveur
d'applications. Depuis cette perspective, des
éditeurs graphiques permettent de manipuler les fichiers web.xml, ejb-jar.xml ou
application.xml, autant de fichiers à utiliser
pour le déploiement de l'application.
Pour les EJB, toutes les techniques de développement sont supportées : top-down
(génération du schéma de la base de donnée
à partir de l'EJB), bottom-up (génération du
Bean à partir du schéma) et in-the-middle (la
plus flexible). Bien qu'utilisable, c'est le module qui nous a semblé le moins performant.
Perspective
XML
Cette perspective donne accès à des outils
utiles lors du développement d'applications
XML :
• Editeur XML : avec cet outil il est possible
de créer et de valider des documents XML.
Il comprend une vue de conception et une
vue source.
• Editeur de DTD : cet éditeur autorise la création et la validation de DTD. Les DTD peuvent être converties en XML Schema et
vice-versa.
• Editeur de XML Schema : cet éditeur permet de créer et de valider des documents
XSD (XML Schema Definition).
• Mapping XML vers XML : cet outil propose
une interface graphique qui permet de
créer des feuilles de style XSLT de mapping
XML vers XML.
• Traceur XSLT : cet outil permet d'exécuter
pas à pas une transformation XSLT.
• Validateur de documents XML : vérifie le
respect de la DTD ou du XSD.
• Mapping RDB vers XML: cet outil convertit
automatiquement les résultats de requêtes
SQL en un fichier XML avec son XSD associé.
On retrouve dans cette perspective un
bon noyau d'outils pour les développements
XML. Ils sont encore de qualité
inégale : les éditeurs de document XML, DTD
et XML Schema sont agréables à utiliser. Par
contre les outils tournant autour de XSLT
manquent encore de maturité.
Perspective
Serveur
La perspective serveur permet de visualiser et de modifier la configuration des serveurs en charge d'exécuter les applications
Web et les EJB. Il est possible de configurer
un déploiement sur un serveur distant.
WSAD 4.0 est livré en standard avec un plugin WAS4 AEs, et d'autres connecteurs pour
Tomcat 3.2 et 4.0.
Perspective
Profiling
Le développement d'applications performantes est souvent une priorité pour les développeurs. Les outils de profiling permettent de
mesurer les performances d'applications, et de
localiser ainsi dès le stade du développement
les sources d'éventuels problèmes de performances. IBM a réalisé l'intégration de JInsight,
un outil de profiling présent à son catalogue
depuis quelques années. Pour de meilleures performances, il est essentiel de bien paramétrer
les filtres, pour restreindre le profiling aux seules classes qui sont intéressantes.
Cet outil est moins complet que ceux des
leaders du marché, qui ont déjà annoncé le
portage de leurs produits sur Eclipse. Il apportera néanmoins de grands services à ceux qui
n'en disposent pas.
Perspective
Data
Cette perspective est destinée aux administrateurs de données et aux développeurs
d'EJB. Elle permet de créer, importer ou modifier le schéma d'une base relationnelle. A
partir de ce schéma il est possible d'interroger ou de modifier la base, qui peut bien sûr
être distante. Des drivers jdbc pour toutes les
grandes bases sont fournis.
Services Web : ne parlez plus de SOAP ni de WSDL !
Avec WSAD, rendre le métier d'une entreprise accessible via les services Web est
aussi simple que d'installer WinZip ! Pas de
code à développer, juste un assistant qui
vous guide dans votre développement.
Grâce au niveau d'intégration atteint par
un outil comme WSAD, l'enjeu des services Web n'est désormais plus situé sur le développement, c'est uniquement une problématique de métier et d'architecture :
1. L'entreprise doit exposer un service à
valeur ajoutée.
2. Le concepteur doit répondre aux
bonnes questions, c'est-à-dire construire une architecture qui va garantir
la qualité et la sécurité des services
proposés.
Figure 6. Pour
développer
un service
Web avec
WSAD,
il n'y a pas
de code
à produire,
il suffit
d'utiliser
un assistant !
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
57
Outil
Figure 7. jadclipse est un plug-in qui permet de décompiler à la volée les fichiers .class. Dans cette écran,
nous voyons la décompilation de la classe java.lang.Integer par jadclipse.
Plug-in
commerciaux
D'après IBM, 150 sociétés ont rejoint la
plate-forme Eclipse. Les offres de ces éditeurs
viennent agréablement compléter les outils
disponibles. Le principe d'intégration au cœur
d'Eclipse prend tout son sens.
Si la plupart des plug-ins tiers ne seront
pas prêts avant mi 2002, Rational propose
déjà un plug-in ClearCase LT en bundle avec
WSAD 4.0 pour Windows. Il ne s'agit que du
premier pas de cet éditeur phare, qui s'est engagé à proposer l'intégralité de son offre intégré sur Eclipse : ClearQuest, ClearCase Entreprise et Rational Suite. L'offre ClearCase est
concurrencée par le client CVS présent en
standard. Cependant ces deux produits existaient déjà indépendamment d'Eclipse, et
chacun avait ses utilisateurs. Il en va de même
pour les futures offres Merant (PVCS), StarBase
(StarTeam), ou Telelogic (CM Synergy), toutes
situées sur le créneau de la gestion de configuration.
Sitraka (JProbe), un des leaders sur le marché des outils d'optimisation, est concurrencé
par l'outil intégré en standard dans WSAD 4.0.
Son intérêt réside surtout dans une offre
moins onéreuse que WSAD si l'on n´a besoin
que de l'outil d'optimisation. C'est aussi le cas
pour Macromedia (Dreamweaver, UltraDev,
Flash), en concurrence avec les outils de
WSAD.
Rational complètera avec un plug-in Rose
le module modélisation absent dans WSAD
4.0. Il se retrouvera en concurrence sur Eclipse
avec l'atelier de modélisation de TogetherSoft,
particulièrement performant pour le développement Java.
On retrouve comme partenaires de la première heure sur Eclipse les sociétés
Instantiations et Versata. La première proposait déjà des outils d'amélioration de la productivité sur VisualAge for Java, et la
deuxième offre un environnement de développement RAD.
On peut aussi citer des éditeurs leaders
sur leurs marchés, qui apporteront une plusvalue exceptionnelle à la plate-forme Eclipse
: Interwoven (gestion de la relation client),
Mercury Interactive (injecteur), Bowstreet
(Bowstreet Business Web Factory), Versant (cache de donnée enJin), WebGain (outil de
mapping TopLink). Beaucoup d'autres besoins seront couverts ! Citons par exemple le
framework de developpement d'applications
d'Altoweb, concurencé par le framework
Open Source Jakarta Struts.
Le nombre de sociétés annonçant leur
support rend crédible l'ambition d'un nouveau marché de composants logiciels
interopérables que permet l'architecture
ouverte d'Eclipse.
Plug-in
libres
Si l'architecture d'Eclipse va dynamiser le
marché des plug-ins commerciaux, elle va
aussi stimuler les projets Open Source. Pour
preuve, plusieurs projets Open Source sont
déjà sur les rails : jadclipse (voir figure 7),
Poseidon et struts-console.
D'un modèle de développement pour
hackers éclairés, l'Open Source est désormais
devenu un enjeu économique majeur. Eclipse
possède tous les atouts pour devenir rapidement la plate-forme de développement universelle. L'ignorer, c'est passer à côté d'une révolution en marche.
WSAD 4.0 est une offre qui utilise parfaitement l'intégration rendue possible par
Eclipse, et s'impose aujourd'hui comme un
environnement complet de développement
J2EE 1.2. Bonne aventure technologique !
ALLER PLUS LOIN
L'Open source :
http://www.opensource.org
IBM et l’Open Source :
http://www-124.ibm.com/developerworks/oss
Le projet Eclipse :
http://www.eclipse.org
WebSphere Studio Application Developer :
http://www-4.ibm.com/software/ad/studioappdev/
Plug-in Open Source pour le développement C/C++ :
http://www.alphaworks.ibm.com/tech/eclipse4c
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
58
Outil
Borland
○ ○ ○ ○ ○ ○ ○ ○ ○
JBuilder 7 Enterprise Edition
BÉNÉFICIANT D'UNE AVALANCHE DE NOUVEAUTÉS ET D'AMÉLIORATIONS,
BARDÉ D'OUTILS, JBUILDER 7 DEMEURE L'UN DES PRODUITS PHARES DU
MARCHÉ DES OUTILS DE DÉVELOPPEMENT JAVA.
Nicolas Dasriaux
est consultant et expert
de la plate-forme Java
et des serveurs d'application
J2EE chez Neoxia. Lors
de missions de conseil ou
de coaching, il accompagne
les équipes projet pour
les aider à tirer le meilleur
parti des technologies Java
(notamment en terme de
performance).
[email protected]
Figure 1 : visualisation UML de la
classe Personne
L
e marché des outils
de développement
Java est un marché
arrivé à maturité, ce
qui est une façon
élégante de dire que les éditeurs
y figurent en nombre restreint.
Avec la fin de WebGain, il ne reste
maintenant plus que Borland
avec JBuilder, IBM avec
WebSphere Studio Application
Developper (WSAD), et dans une
moindre mesure, Sun avec ONE
Studio (ex Forte for Java).
Dans un tel contexte, la nouvelle version de JBuilder doit se
distinguer. Comme à l’accoutumée, JBuilder 7 comprend une
avalanche de nouveautés et
d’améliorations très diverses, allant de l’accessoire à l’essentiel.
Plutôt que de se lancer dans une
fastidieuse énumération, il semble préférable de s’attarder sur les
fonctionnalités majeures de cette
nouvelle version. JBuilder 7 existe
en trois éditions : Personal, Standard Edition, et Enterprise. C’est
cette dernière édition qui est traitée dans cet article.
Explorer le code
Pouvoir naviguer dans un
code mal connu ou inconnu est
une nécessité en développement. Ainsi, quand un projet atteint une taille importante, il est
bien rare que chacun des développeurs connaisse la totalité du
code. Bien sûr, le bon déroulement d’un projet implique que
les développeurs sachent com-
muniquer leur connaissance de
telle ou telle partie du code.
Néanmoins, pour augmenter l’efficacité de l’équipe, il est également souhaitable de disposer
d’outils d’exploration du code. En
fait, c’est surtout lors de la reprise
d’un code écrit par des tiers, que
de tels outils deviennent absolument nécessaires. JBuilder 7 tient
compte de ces besoins en intégrant un ensemble de fonctionnalités d’exploration du code.
Tout d’abord, il est possible de visualiser, sous forme d’un diagramme UML (voir figure 1),
toute classe de l’application. En
fait, il s’agit d’une représentation
UML partielle, centrée sur la
classe visualisée, et déduite directement du code de la classe.
Grâce à ce diagramme, il est possible de connaître :
· la classe dont hérite la classe
visualisée et les interfaces
qu’elle implémente,
· les classes référencées par les
champs de la classe visualisée,
· les classes utilisées par la classe
visualisée pour son implémentation,
· les classes qui référencent la
classe visualisée,
· les classes qui utilisent la classe
visualisée.
Grâce à un outil de recherche,
il est possible d’énumérer toutes
les instructions qui utilisent une
classe donnée : accès à un champ
ou à une méthode, ou bien encore référence au type de la
classe. De cette manière, il est
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
59
Outil
Listing 1 : le test TestCompte (test de la classe Compte)
[…]
import junit.framework.*;
[…]
public class TestCompte extends TestCase {
[…]
public void testCrediter() {
Compte compte = new Compte();
compte.setSolde(1000.0);
compte.crediter(100.0);
assertEquals(1100.0, compte.getSolde(), 1e-10);
}
Figure 2 : refactoring, renommage de la méthode debit en debiter
possible d’évaluer l’étendue de
l’utilisation d’une classe, et donc
de déterminer l’impact d’une modification de cette classe sur l’application. Dans le même esprit,
l’outil de recherche permet également de rechercher tous les appels d’une méthode donnée, tous
les accès à un champ ou un paramètre donné. Ces fonctions de recherche sont accessibles depuis
l’éditeur de code, mais également
depuis la visualisation UML.
Restructurer le code
Bien gérer le code d’une application, c’est savoir entreprendre les travaux nécessaires, pour
lui conserver sa lisibilité, sa cohérence et sa maintenabilité. En
apparence, il semble simple de
renommer une méthode ou un
package, ou d’ajouter un paramètre à une méthode. En réalité, de
telles opérations peuvent avoir
des conséquences très lourdes
sur le code de l’application. Sans
l’aide d’outils spécifiques, entreprendre de telles actions de
refactoring peut se révéler très
coûteux en temps. Pourtant, il
serait bien dommage d’y renoncer. Afin de rendre possible de
tels refactoring, JBuilder 7 met, à
disposition des développeurs,
toute une panoplie de fonctionnalités. Elles sont accessibles directement dans l’éditeur de code
ou bien encore, depuis la visualisation UML. Lorsque l’on lance
une opération de refactoring,
JBuilder 7 propose un ensemble
de modifications à effectuer sur
le code. Il est alors possible de
parcourir ces modifications, puis
de demander à JBuilder 7 de mettre réellement en œuvre l’ensemble des modifications proposées.
Dans tous les cas, il est possible
de revenir sur son choix, et d’annuler les modifications effec-
tuées. Entre autres, JBuilder 7
permet de renommer une classe
ou un champ, de déplacer une
classe vers un autre package, ou
bien encore de renommer un
package. Il est également possible de renommer une méthode,
de modifier le nom d’un paramètre, et de supprimer ou ajouter un
paramètre. Dans ce dernier cas, il
faut saisir la valeur du nouveau
paramètre, qui sera utilisée lors
de chaque appel de la méthode.
Lorsque le code d’une méthode
comporte un nombre élevé de lignes, il est utile de pouvoir reporter une partie du code dans une
nouvelle méthode. JBuilder 7
permet de réaliser facilement
cette opération. Il détermine
automatiquement les paramètres de la nouvelle méthode, la
crée, et ajoute un appel dans la
méthode d’origine. JBuilder 7
automatise également d’autres
actions plus élémentaires, mais
fastidieuses, comme l’ajout d’un
bloc try / catch.
Tester le code
Par peur d’introduire des bogues ou des incompatibilités, il
est malheureusement trop fréquent de renoncer à un ajout de
fonctionnalités ou à une restructuration de code (refactoring). Le
plus souvent, on ne dispose, en
effet, pas de moyens rapides
pour vérifier l’innocuité de l’intervention. Les tests unitaires sont
l’un de ces moyens. Pour chaque
classe de l’application, il s’agit de
développer des tests, qui sont
ensuite incorporés au sein de
jeux de tests. Par la suite, la mise
en œuvre régulière des jeux tests
de permet de s’assurer de la nonrégression de l’application. Pour
être efficace, il est nécessaire de
disposer d’outils permettant de
rejouer les jeux de tests, puis de
}
public void testDebiter() {
Compte compte = new Compte();
compte.setSolde(1000.0);
compte.debit(100.0);
// Compare la valeur renvoyée par getSolde() à une valeur attendue.
// Cette valeur est volontairement éronnée et fait échouer le test.
assertEquals(900.50, compte.getSolde(), 1e-10 /* précision */);
}
Listing 2 : le jeu de tests AllTests (test des classes Compte et Personne)
[…]
import junit.framework.*;
public class AllTests extends TestCase {
[…]
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(jbuilder7.banque.test.TestCompte.class);
suite.addTestSuite(jbuilder7.banque.test.TestPersonne.class);
return suite;
}
}
synthétiser et visualiser les résultats. Dans ce but, JBuilder 7 propose un ensemble d’outils, fondés sur le framework de tests unitaires JUnit, d’origine open source.
Considérons le cas d’une application qui comprend les 2 classes Compte et Personne. Le listing 1 reprend le code de la classe
TestCompte, qui implémente les
tests relatifs à la class Compte. De
la même manière, la classe
TestPersonne implémente les
tests relatifs à la classe Personne.
Les deux classes TestCompte
et TestPersonne sont réunies
pour former le jeu de tests
AllTests (voir listing 2). Par la suite,
il est possible d’exécuter le jeu de
tests, autant de fois que désiré, et
directement dans l’environnement de JBuilder 7.
La figure 3 présente le résultat
de l’exécution du jeu de test
AllTests. Lors des tests, les méthodes debiter et getPrenom n’ont
pas eu le comportement escompté, et ceci est signalé dans
l’interface graphique des résultats.
Construire, conditionner
et déployer
Dans une application J2EE,
les composants sont conditionnés et livrés sous forme de fichiers JAR, WAR, EAR … Ces composants doivent, ensuite, être
déployés sur différents serveurs.
Le processus de conditionnement, et de déploiement des
composants se compose de
nombreuses tâches répétitives,
sujettes à erreur, et qui doivent
pouvoir être effectuées dans un
Figure 3 : exécution du jeu de test AllTests
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
60
Outil
Listing 3 : build.xml, un fichier Ant décrivant des cibles de
construction
<?xml version=»1.0"?>
<!DOCTYPE project>
<project name=»Banque» default=»dist» basedir=».»>
<property name=»src» value=»src»/>
<property name=»build» value=»build»/>
<property name=»dist» value=»dist»/>
Figure 4 : intégration d’un fichier Ant (build.xml) à un projet JBuilder
<target name=»init»>
<tstamp/>
<mkdir dir=»${build}»/>
</target>
<target name=»compile» depends=»init»>
<javac srcdir=»${src}» destdir=»${build}»/>
</target>
<target name=»dist» depends=»compile»>
<mkdir dir=»${dist}/lib»/>
<jar jarfile=»${dist}/lib/MyProject-${DSTAMP}.jar» basedir=»${build}»/>
</target>
<target name=»clean»>
<delete dir=»${build}»/>
<delete dir=»${dist}»/>
</target>
</project>
Figure 5 : utilisation d’OptimizeIt Code Coverage
temps réduit. Tout porte donc à
automatiser de tels processus.
Ainsi, l’automatisation permet
d’éviter de nombreuses erreurs
lors de la mise en pré-production
ou en production. Lors du développement, l’automatisation permet de déployer l’application sur
un environnement d’exécution
plus réaliste que la station de développement.
Dans
cette
optique,
JBuilder 7 intègre l’outil d’automatisation Ant. Issu du groupe
Apache, Ant est la version nouvelle génération de l’utilitaire
make. Ant est écrit en Java et
tourne donc à l’identique sous
Windows, Unix, Mac OS … Les
opérations à automatiser sont
décrites sous forme d’un fichier
XML (le plus souvent build.xml).
Ce dernier détail rend Ant bien
plus simple à utiliser que son "illustre ancêtre".
Par exemple, le fichier
build.xml ( listing 3) décrit quatre
cibles de construction : init (initialisation), compile (compilation),
dist (conditionnement), clean
(nettoyage). Le fichier build.xml
peut être ajouté à un projet
JBuilder. Les diverses cibles appa-
raissent alors dans l’arborescence
du projet et il est possible de lancer l’exécution d’une cible depuis
l’environnement (figure 4).
Optimiser le code
JBuilder 7 est fourni avec
OptimizeIt Suite, un outil de
profiling dont Borland a fait récemment l’acquisition. Cet outil
comprend trois modules :
• OptimizeIt Code Coverage :
ce module permet de connaître
les parties du code qui ont été
utilisées lors de l’exécution de
l’application (figure 5). Il est ainsi
possible de détecter du code
‘mort’, c’est à dire du code qui
n’est jamais exécuté. Lors de tests,
ce module permet également de
vérifier, qu’il ne reste pas de code
non testé.
• OptimizeIt Profiler : ce module permet d’identifier les goulets d’étranglement qui nuisent
aux performances de l’application. Il fournit également des
outils qui aident à détecter un
engorgement de mémoire
(memory leak) ou une production
excessive d’objets éphémères.
• OptimizeIt Thread Debugger :
ce module est spécifiquement
consacré au débogage des
threads. Il permet de surveiller
l’activité des threads et d’identifier les excès de verrous, et les
verrous mortels.
Grâce à OptimizeIt Suite, il est
possible d’identifier les facteurs
qui limitent la performance de
l’application. En tenant compte
de ces résultats, il est ensuite possible de procéder à des optimisations ou à des actions de
refactoring. JBuilder 7 comprend
une licence d’utilisation d’OptimizeIt Code Coverage uniquement. Pour pouvoir utiliser les
autres modules, il faut, soit faire
l’acquisition d’une licence
OptimizeIt Suite, soit acquérir
Borland Enterprise Studio for
Java 4 qui contient également
JBuilder 7. En ce qui concerne l’intégration, il est possible de lancer
une application sous OptimizeIt
directement depuis l’environnement de JBuilder. Borland promet
une intégration plus étroite dans
la prochaine version.
Développement J2EE
Pour le développement d’application J2EE 1.2 et 1.3, JBuilder
supporte un nombre important
de serveurs d’application :
· BEA WebLogic Application
Server 5.1 et 6.x (ou supérieur),
· IBM WebSphere Application
Server 3.5 et 4.0,
· Borland AppServer 4.5,
Borland Enterprise Server 5.0,
· iPlanet 6.x (ou supérieur),
· Sybase EAServer 4.1, grâce à un
Open Tool (extension JBuilder)
fournie par Sybase.
JBuilder 7 permet de développer des Servlets, des JSP, et
des EJB pour chacun de ces serveurs d’application. Il permet ensuite de les déployer, de les exécuter et de les déboguer directement depuis l’environnement de
développement.
JBuilder 7 sait également travailler avec le moteur de Servlets
Tomcat 3.3 et 4.0 du groupe Apache. Tomcat 3.3 et 4.0 sont
d’ailleurs installés en même
temps que JBuilder 7. Le serveur
d’application de Borland est également disponible sur le CDRom ; Borland Enterprise Server
AppServer Edition 5.0.2 est conforme à la spécification J2EE 1.3,
et comporte SonicMQ, une implémentation majeure de JMS.
DÉVELOPPEUR RÉFÉRENCE | Hors-série Java | 19 septembre 2002
Te c h n i q u e
61
Outil
Figure 6 : le concepteur visuel d’EJB
Conception visuelle
d’EJB
JBuilder 7 intègre un concepteur visuel d’EJB prenant en
charge la spécification EJB 2.0 (figure 6). Ce concepteur permet
de développer des EJB de tout
type : EJB session, EJB entité, message driven bean.
De manière visuelle, il est
possible de créer un EJB session,
entité, ou message driven. On
peut ensuite lui ajouter des
champs. Dans ce cas, il est possible d’indiquer à JBuilder 7, s’il doit
générer les méthodes get et set.
On peut également ajouter des
méthodes à l’EJB. Il est alors possible de spécifier si la méthode
est de type local, remote, ou s’il
elle n’apparaît pas dans l’interface
de l’EJB. Bien sûr, il est possible à
tout moment de renommer ou
de supprimer un EJB, un champ,
ou une méthode. Dans tous les
cas, JBuilder 7 met à jour automatiquement les interfaces home et
remote, mais également la classe
d’implémentation de l’EJB, ainsi
que les informations du
descripteur de déploiement.
Dans la pratique, l’intervention
sur le code est réduite à l’implémentation du corps des méthodes. L’édition du descripteur de
déploiement se fait également,
de manière visuelle, par l’intermédiaire de formulaires bien
conçus. Des onglets supplémen-
taires apparaissent pour
configurer les paramètres spécifiques au serveur d’application
utilisé (WebLogic, WebSphere,
etc.). La spécification EJB 2.0 introduit un nouveau modèle de
persistance pour les EJB entités
CMP. Ce modèle est particulièrement bien pris en charge par le
concepteur visuel. Ainsi, il est
possible de paramétrer le
mapping entre un EJB entité CMP
et les tables correspondantes.
Dans l’ensemble, le concepteur
visuel est bien conçu. On peut
juste regretter que les aspects
propriétaires des datasources ne
soient pas toujours très bien pris
en charge pour certains serveurs
d’application. En clair, il reste parfois certaines informations à
ajouter à la main dans les
descripteurs de déploiement
propriétaires.
Plus loin
Dans
sa
conception,
JBuilder 7 est un peu une tour de
contrôle, sur laquelle il est possible de greffer toute une
floppée d’extensions. Concrètement, c’est l’API Open Tool qui
permet d’étendre les fonctionnalités de JBuilder 7. Borland
Enterprise Studio for Java 4 se
fonde sur cette infrastructure
pour proposer un ensemble intégré de logiciels, articulé autour
de JBuilder 7. Cet ensemble se
compose de :
• JBuilder 7 Enterprise ;
• Borland OptimizeIt Suite 4.2 ;
• Rational Rose Professionnal J
2002 : conception UML, génération de code et processus de
développement itératif ;
• Rational Unified Process 2002 :
prise en charge du RUP ;
• Macromedia Dreamweaver
MX : conception de pages et de
sites Web statiques et dynamiques (prise en charge des JSP).
Bardé de cette panoplie
d’outils, JBuilder 7 devient une
plate-forme complète permettant la conception, le développement, le test, l’optimisation, le
refactoring, et le déploiement
d’applications de tout type.
Doté, de surcroît, d’une ergonomie éprouvée, JBuilder 7 demeure l’un des produits phares
du marché des outils de développement Java.
FICHE TECHNIQUE
Plates-formes : Windows NT4/2000/XP,
Linux, Solaris, Mac OS X
Editeur : Borland
URL : www.borland.fr
Téléchargement