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 "&amp;" à 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