<application> <display-name>A1</display-name> <description>Application description</description> ... <module> <web> <web-uri>w1.war</web-uri> <context-root>monsite_a1</context-root> </web> </module> ... <module> <ejb>ejb1.jar</ejb> </module> ... </application>Nous ne donnons pas une vue exhaustive de ces descripteurs de déploiement, la suite du chapitre montrant des éléments d'autres types de descripteur pour illustrer diverses constructions J2EE.
// Définition de la variable contenant le lien vers l'usine à instances // d'un composant CMx qui dans le cas présent sera le composant CM1 private CmxHome usineCmx; // Appelée à la création de l'instance du composant \emph{servlet} public void init(ServletConfig config) throws ServletException { Context envContext = new InitialContext().lookup("java:comp/env"); usineCmx = (CmxHome) PortableRemoteObject.narrow( envContext.lookup("ejb/CMx"), CmxHome); ... }Il est clair ici que le code du composant référençant est indépendant de celui du composant référencé. En effet, aucune information relative à CM1 n'est présente dans le code de CP2. Pour lier CP2 à un autre CMx, il suffit de changer le lien dans son descripteur de déploiement en spécifiant un autre composant : par exemple, on se lie à un ejb1b avec < ejb-link > ../ejb1b.jar/ejb1b < /ejb-link > . Le mécanisme de mise en liaison dans le code est donc le même quel que soit le type de composant ou de ressource : il s'effectue par un lookup approprié sur le contexte correspondant à l'environnement propre au composant. L'exemple ci-dessus est compliqué par l'utilisation d'une fonction de conversion de type, nécessaire dans le cas où on se lie à un composant offrant une interface accessible à distance (interface Remote). Il faut noter que ce mécanisme est en train d'évoluer quelque peu dans le cadre des nouvelles versions des spécifications, notamment dans le cadre d'EJB 3.0. En effet, l'affectation de la variable de liaison à partir du lookup pourra être prise en charge par le conteneur. Il suffira pour cela d'annoter la variable ou un accesseur à celle-ci pour définir cette liaison (injection de la mise en liaison par le conteneur. L'utilisation des annotations est généralisée dans cette version des spécifications, ce qui fait que les informations de description d'un composant qui étaient auparavant présentes dans le descripteur de déploiement sont de retour dans son code, même si elles sont isolées du code proprement dit à travers ce mécanisme d'annotation. Cette nouvelle approche a ses avantages et ses inconvénients. Elle rend le code moins lisible à cause de la surchage due aux annotations. Par contre, la description d'un composant est attachée à ce dernier plutôt que d'être agglomérée dans un descripteur général. Cela devrait faciliter l'émergence de bibliothèques de composants et de vrais outils d'assemblage s'appuyant sur celles-ci, donnant ainsi accès à un véritable environnement de réutilisation de code. Dans une telle démarche, on privilégie l'utilisation des annotations pour toutes les informations relatives au comportement du composant (propriétés déclaratives telles que les propriétés transactionnels ou les propriétés liés à la persistance), et l'utilisation des descripteurs pour toutes les informations d'assemblage.
UserTransaction systx; // Un composant \emph{servlet} se liant au gestionnaire de transaction // lors de son initialisation. public void init(ServletConfig config) throws ServletException { systx = new InitialContext().lookup("java:comp/UserTransaction"); systx.begin(); // démarrage d'une transaction ... // actions transactionnelles systx.commit(); // validation de la transaction courante ... }Cette liaison est faite ici à l'initialisation d'un composant de présentation. L'accès à ce contexte JNDI peut néanmoins s'opérer dans n'importe quelle séquence de code s'exécutant dans le cadre d'un serveur J2EE.
http://machine[:port]chemin[[;infosupp]?requête]Par exemple, l'URL http://www.monsite.com:8080/a1/w2/s1?p1=val1&p2=val2 permet d'activer une des servlets de la figure 5.8. Nous observons dans cet exemple qu'un conteneur de servlets HTTP est présent sur la machine www.monsite.com et qu'il attend les requêtes HTTP sur le port 8080. Le chemin identifiant la ressource Web à invoquer est /a1/w2/s1. Le reste de l'URL, commençant au caractère ?, permet de dire que cette ressource Web est une requête à laquelle est passée deux paramètres p1 et p2 auxquels sont respectivement associés les valeurs val1 et val2 (ces paramètres sont transformés en attributs de la requête HttpServletRequest construite par le conteneur). C'est la partie chemin qui sert au processus de démultiplexage mis en œuvre par le conteneur de servlets. En effet, lors du déploiement d'une application Web (contenant en ensemble de servlets à déployer), elle définit un contexte auquel est associé un nom correspondant à la racine du chemin de démultiplexage des servlets qu'elle contient. Par exemple, le déploiement de l'application Web spécifiée par w2.war définit un contexte associé à la racine /a1/w2 (racine définie dans le descripteur de déploiement contenu dans le paquetage). Les servlets qu'elle contient sont ensuite créées dans ce contexte où leur est associé un nom défini lui aussi dans le descripteur de déploiement ; il y a dans le cas présent une seule servlet appelée s1. Le processus de démultiplexage consiste donc à rechercher un contexte dont le nom correspond à un préfixe du chemin d'une URL puis à rechercher dans ce contexte une servlet dont le nom correspond au reste du chemin auquel on a soustrait ce préfixe. Nous n'allons pas détailler les informations relatives à HTTP mais simplement rappeler brièvement en quoi elles consistent. Elles sont pour l'essentiel introduites dans le cadre des interfaces de manipulation des requêtes et des réponses, correspondant à des extensions des interfaces préalablement définies par les servlets de base. Par ailleurs, la classe abstraite HttpServlet propose un raffinement de l'opération service en appels vers des opérations correspondant aux actions définies par le protocole HTTP, à savoir doGet pour une requête GET, doPost pour une requête POST, doHeader pour une requête HEADER, etc. Ces opérations ont la même signature que l'opération service, ayant en paramètre les objets représentant la requête et la réponse.
<%@ page contentType="text/html; charset=UTF-8" %> <html> <head><title>La date</title></head> <body bgcolor="white"> <jsp:useBean id="date" class="monorg.MaDate"/> <jsp:setProperty name="date" property="localisation" value="French"/> <b>Nous sommes le : </b>${date.date} </body> </html>Nous observons aussi les échanges avec le monde Java par l'utilisation d'un bean. La directive JSP useBean crée un bean de la classe donnée en paramètre et l'affecte à la variable date. La directive suivante affecte la propriété localisation du même bean (appel de la méthode setLocalisation de la classe MaDate définie ci-dessous). Enfin, on récupère la chaine de caractères correspondant à la date à afficher par l'instruction ${date.date}.
package monorg; class MaDate { public setLocalisation(String loc) { ... } public String getDate() { return ... } }Notons que le code d'une page JSP peut accéder à toutes les informations de l'environnement de servlet et a donc accès à toutes les APIs de ce dernier. Les pages JSP sont déployées comme des servlets. En effet, au déploiement d'une page JSP, le conteneur génére la classe servlet correspondante, la compile et la charge. Lors de chaque interaction avec cette servlet, le conteneur vérifie que la servlet est plus jeune que la page JSP associée. Si ce n'est pas le cas, le conteneur met à jour la classe servlet, supprime les anciennes instances et en recrée de nouvelles pour traiter les requêtes. Cela offre une grande souplesse notamment en phase de développement. D'autres mécanismes sont aussi offerts par JSP, et notamment les tags et les librairies de tags ainsi que le langage d'expression. Cela sort du sujet du présent chapitre dont l'objectif est de présenter les principes des technologies fournies par l'environnement J2EE. Nous ne les présentons donc pas.