IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 15 - Informatique distribuée

pages : 1 2 3 4 5 6 7 8 9 10 11 12 

L'objet sessionest fourni par défaut, il est donc disponible sans code supplémentaire. Les appels de getID( ), getCreationTime( ) et getMaxInactiveInterval( ) servent à afficher des informations sur l'objet session.

Quand on ouvre la session pour la première fois on a, par exemple, MaxInactiveInterval égal à 1800 secondes (30 minutes). Ceci dépend de la configuration du conteneur JSP/servlet. MaxInactiveInterval est ramené à 5 secondes afin de rendre les choses intéressantes. Si on rafraîchit la page avant la fin de l'intervalle de 5 secondes, alors on voit :

Session value for "My dog" = Ralph

Mais si on attend un peu plus longtemps, alors » Ralph « devient null.

Pour voir comment les informations de sessions sont répercutées sur les autres pages, ainsi que pour comparer le fait d'invalider l'objet session à celui de le laisser se terminer, deux autres JSP sont créées. La première (qu'on atteint avec le bouton » invalidate « de SessionObject.jsp) lit l'information de session et invalide explicitement cette session :

//:! c15:jsp:SessionObject2.jsp
<%--The session object carries through--%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<H1>Session value for "My dog"
<%= session.getValue("My dog") %></H1>
<% session.invalidate(); %>
</body></html>
///:~

Pour tester cet exemple, rafraîchir SessionObject.jsp, puis cliquer immédiatement sur le bouton invalidate pour activer SessionObject2.jsp. À ce moment on voit toujours » Ralph, « immédiatement (avant que l'intervalle de 5 secondes ait expiré). Rafraîchir SessionObject2.jsp pour voir que la session a été invalidée manuellement et que Ralph a disparu.

En recommençant avec SessionObject.jsp, rafraîchir la page ce qui démarre un nouvel intervalle de 5 secondes, puis cliquer sur le bouton « Keep Around »], ce qui nous amène à la page suivante, SessionObject3.jsp, qui N'invalide PAS la session :

//:! c15:jsp:SessionObject3.jsp
<%--The session object carries through--%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<H1>Session value for "My dog"
<%= session.getValue("My dog") %></H1>
<FORM TYPE=POST ACTION=SessionObject.jsp>
<INPUT TYPE=submit name=submit Value="Return">
</FORM>
</body></html>
///:~

Dû au fait que cette page n'invalide pas la session, » Ralph « est toujours là aussi longtemps qu'on rafraîchit la page avant la fin de l'intervalle de 5 secondes. Ceci n'est pas sans ressembler à un » Tomagotchi], et » Ralph « restera là tant que vous jouerez avec lui, sinon il disparaîtra.

Créer et modifier des cookies

Les cookies ont été introduits dans la section précédente concernant les servlets. Ici encore, la concision des JSP rend l'utilisation des cookies plus simple que dans les servlets. L'exemple suivant montre cela en piégeant les cookies liés à une demande en entrée, en lisant et modifiant leur date d'expiration, et en liant un nouveau cookie à la réponse :

//:! c15:jsp:Cookies.jsp
<%--This program has different behaviors under
different browsers! --%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<%
Cookie[ « cookies = request.getCookies();
for(int i = 0; i < cookies.length; i++) { %>
  Cookie name: <%= cookies[i].getName() %> <br>
  value: <%= cookies[i].getValue() %><br>
  Old max age in seconds:
  <%= cookies[i].getMaxAge() %><br>
  <% cookies[i].setMaxAge(5); %>
  New max age in seconds:
  <%= cookies[i].getMaxAge() %><br>
<% } %>
<%! int count = 0; int dcount = 0; %>
<% response.addCookie(new Cookie(
    "Bob" + count++, "Dog" + dcount++)); %>
</body></html>
///:~

Chaque navigateur ayant sa manière de stocker les cookies, le résultat sera différent suivant le navigateur (ce qui n'est pas rassurant, mais peut-être réparerez-vous un certain nombre de bugs en lisant cela). Par ailleurs, il se peut aussi que l'on ait des résultats différents en arrêtant le navigateur et en le relançant, plutôt que de visiter une autre page puis de revenir à Cookies.jsp. Remarquons que l'utilisation des objets session semble plus robuste que l'utilisation directe des cookies.

Après l'affichage de l'identifiant de session, chaque cookie du tableau de cookies arrivant avec l'objet request object est affiché, ainsi que sa date d'expiration. La date d'expiration est modifiée et affichée à son tour pour vérifier la nouvelle valeur, puis un nouveau cookie est ajouté à la réponse. Toutefois, il est possible que votre navigateur semble ignorer les dates d'expiration ; il est préférable de jouer avec ce programme en modifiant la date d'expiration pour voir ce qui se passe avec divers navigateurs.

Résumé sur les JSP

Cette section était un bref apperçu des JSP ; cependant avec les sujets abordés ici (ainsi qu'avec le langage Java appris dans le reste du livre, sans oublier votre connaissance personnelle du langage HTML) vous pouvez dès à présent écrire des pages Web sophistiquées via les JSP. La syntaxe JSP n'est pas particulièrement profonde ni compliquée, et si vous avez compris ce qui était présenté dans cette section vous êtes prêts à être productifs en utilisant les JSP. Vous trouverez d'autres informations dans la plupart des livres sur les servlets, ou bien à java.sun.com.

La disponibilité des JSP est très agréable, même lorsque votre but est de produire des servlets. Vous découvrirez que si vous vous posez une question à propos du comportement d'une fonctionnalité servlet, il est plus facile et plus rapide d'y répondre en écrivant un programme de test JSP qu'en écrivant une servlet. Ceci est dû en partie au fait qu'on ait moins de code à écrire et qu'on puisse mélanger le code Java et le code HTML, mais l'avantage devient particulièrement évident lorsqu'on voit que le Conteneur JSP se charge de la recompilation et du chargement du JSP à votre place chaque fois que la source est modifiée.

Toutefois, aussi fantastiques que soient les JSP, il vaut mieux garder à l'esprit que la création de pages JSP requiert un plus haut niveau d'habileté que la simple programmation en Java ou la simple création de pages Web. En outre, debugger une page JSP morcelée n'est pas aussi facile que débugger un programme Java, car (pour le moment) les messages d'erreur sont assez obcurs. Cela changera avec l'évolution des systèmes de développement, et peut-être verrons nous d'autres technologies construites au-dessus de Java plus adaptées aux qualités des concepteurs de site web.

RMI (Remote Method Invocation) : Invocation de méthodes distantes

Les approches traditionnelles pour exécuter des instructions à travers un réseau sur d'autres ordinateurs étaient aussi confuses qu'ennuyeuses et sujettes aux erreurs. La meilleure manière d'aborder ce problème est de considérer qu'en fait un objet donné est présent sur une autre machine, on lui envoie un message et l'on obtient le résultat comme si l'objet était instancié sur votre machine locale. Cette simplification est exactement celle que Java 1.1 Remote Method Invocation (RMI - Invocation de méthodes distantes) permet de faire. Cette section vous accompagne à travers les étapes nécessaires pour créer vos propres objets RMI.

Interfaces Remote

RMI utilise beaucoup d'interfaces. Lorsque l'on souhaite créer un objet distant, l'implémentation sous-jacente est masquée en passant par une interface. Ainsi, lorsque qu'un client obtient une référence vers un objet distant, ce qu'il possède réellement est une référence intermédiaire, qui renvoie à un bout de code local capable de communiquer à travers le réseau. Mais nul besoin de se soucier de cela, il suffit de juste d'envoyer des messages par le biais de cette référence intermédiaire.

La création d'une interface distante doit respecter ces directives :

  1. L'interface distante doit être public (il ne peut y avoir accès package, autrement dit d'accès friendly). Sinon, le client recevra une erreur lorsqu'il tentera d'obtenir un objet distant qui implémente l'interface distante.
  2. L'interface distante doit hériter de l'interface java.rmi.Remote.
  3. Chaque méthode de l'interface distante doit déclarer java.rmi.RemoteException dans sa clause throws en plus des autres exceptions spécifiques à l'application.
  4. Un objet distant passé en argument ou en valeur de retour (soit directement ou inclus dans un objet local), doit être déclaré en tant qu'interface distante et non comme la classe d'implémentation.

Voici une interface distante simple qui représente un service d'heure exacte :

//: c15:rmi:PerfectTimeI.java
// L'interface distante PerfectTime.
package c15.rmi;
import java.rmi.*;

interface PerfectTimeI extends Remote {
  long getPerfectTime() throws RemoteException;
} ///:~

Cela ressemble à n'importe quelle autre interface mis à part qu'elle hérite de Remote et que toutes ses méthodes émettent RemoteException. Rappelez vous qu'une interface et toutes ses méthodes sont automatiquement publiques.

Implémenter l'interface distante

Le serveur doit contenir une classe qui hérite de UnicastRemoteObject et qui implémente l'interface distante. Cette classe peut avoir aussi des méthodes supplémentaires, mais bien sûr seules les méthodes appartenant à l'interface distante seront disponibles pour le client puisque celui-ci n'obtiendra qu'une référence vers l'interface, et non vers la classe qui l'implémente.

Vous devez explicitement définir le constructeur de cet objet distant même si vous définissez seulement un constructeur par défaut qui appelle le constructeur de base. Vous devez l'écrire parce qu'il doit émettre RemoteException.

Voici l'implémentation de l'interface distante PerfectTimeI:

//: c15:rmi:PerfectTime.java
// L'implémentation de
// l'objet distant PerfectTime.
package c15.rmi;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.net.*;

public class PerfectTime
    extends UnicastRemoteObject
    implements PerfectTimeI {
  // Implémentation de l'interface:
  public long getPerfectTime()
      throws RemoteException {
    return System.currentTimeMillis();
  }
  // Doit implémenter le constructeur
  // pour émettre RemoteException:
  public PerfectTime() throws RemoteException {
    // super(); // Appelé implicitement
  }
  // Inscription auprès du service RMI :
  public static void main(String[ « args) {
    System.setSecurityManager(
      new RMISecurityManager());
    try {
      PerfectTime pt = new PerfectTime();
      Naming.bind(
        "//peppy:2005/PerfectTime", pt);
      System.out.println("Ready to do time");
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

Ici, main( ) se charge de tous les détails de mise en place du serveur. Une application qui met en service des objets RMI doit à un moment :

  1. Créer et installer un gestionnaire de sécurité qui supporte RMI. Le seul disponible pour RMI fourni dans la distribution Java est RMISecurityManager.
  2. Créer une ou plusieurs instances de l'objet distant. Ici, vous pouvez voir la création de l'objet PerfectTime.
  3. Enregistrer au moins un des objets distants grâce au registre d'objets distants RMI pour des raisons d'amorçage. Un objet distant peut avoir des méthodes qui retournent des références vers les autres objets distants. En le mettant en place, le client ne doit s'adresser au registre qu'une seule fois pour obtenir ce premier objet distant.

Mise en place du registre

Ici, vous pouvez voir un appel à la méthode statique Naming.bind( ). Toutefois, cet appel nécessite que le registre fonctionne dans un autre processus de l'ordinateur. Le nom du registre serveur est rmiregistry, et sous Windows 32-bit vous utiliserez :

start rmiregistry

pour démarrer celui-ci en fond. Sous Unix, ce sera :

rmiregistry &

Comme beaucoup d'applications réseau, le rmiregistry est localisé à l'adresse IP de la machine qui l'a démarré, mais il doit aussi écouter un port. Si vous invoquez le rmiregistry comme ci-dessus, sans argument, le port écouté par le registre sera par défaut 1099. Si vous souhaitez que ce soit un autre port, vous devez ajouter un argument à la ligne de commande pour le préciser. Dans cet exemple, le port sera 2005, ainsi le rmiregistry devra être démarré de cette manière sous Windows 32-bit :

start rmiregistry 2005

ou pour Unix:

rmiregistry 2005 &

L'information concernant le port doit aussi être fourni à la commande bind( ), ainsi que l'adresse IP de la machine où se trouve le registre. Mais il faut mentionner que cela peut être un problème frustrant en cas de tests de programmes RMI en local (de la même manière que les programmes réseau testés plus loin dans ce chapitre). En effet dans le JDK version 1.1.1, il y a quelques problèmes : [69]

  1. localhost ne fonctionne pas avec RMI. Aussi, pour faire une expérience avec RMI sur une seule machine, vous devez fournir le nom de la machine. Pour découvrir le nom de votre machine sous Windows 32-bit, allez dans le Panneau de configuration et sélectionnezRéseau. Sélectionnez l'onglet Identification, vous verrez apparaître le nom de l'ordinateur. Dans mon cas, j'ai appelé mon ordinateur Peppy. Il semble que la différence entre majuscules et minuscules soit ignorée.
  2. RMI ne fonctionnera pas à moins que votre ordinateur ait une connexion TCP/IP active, même si tous vos composants discutent entre eux sur la machine locale. Cela signifie que vous devez vous connecter à Internet pour essayer de faire fonctionner le programme ou vous obtiendrez quelques messages d'exception obscurs.

En ayant tout cela à l'esprit, la commande bind( ) devient :

Naming.bind("//peppy:2005/PerfectTime", pt);

Si vous utilisez le port par défaut 1099, nul besoin de préciser le port, donc vous pouvez dire :

Naming.bind("//peppy/PerfectTime", pt);

Vous devriez être capable de réaliser un test local en omettant l'adresse IP et en utilisant simplement l'identifiant :

Naming.bind("PerfectTime", pt);

Le nom pour le service est arbitraire ; il se trouve que c'est PerfectTime ici, comme le nom de la classe, mais un tout autre nom conviendrait. L'important est que ce nom soit unique dans le registre, connu par le client pour qu'il puisse se procurer l'objet distant. Si le nom est déjà utilisé dans le registre, celui renvoie une AlreadyBoundException. Pour éviter cela, il faut passer à chaque fois par un appel à rebind( ) à la place de bind( ), en effet rebind( ) soit ajoute une nouvelle entrée, soit remplace celle existant déjà.

Durant l'exécution de main( ), l'objet a été créé et enregistré, il reste ainsi en activité dans le registre, attendant qu'un client arrive et fasse appel à lui. Tant que rmiregistry fonctionne et que Naming.unbind( ) n'est pas appelé avec le nom choisi, l'objet sera présent. C'est pour cela que lors de la conception du code, il faut redémarrer rmiregistry après chaque compilation d'une nouvelle version de l'objet distant.

rmiregistry n'est pas forcément démarré en tant que processus externe. S'il est sûr que l'application est la seule qui utilise le registre, celui peut être mis en fonction à l'intérieur même du programme grâce à cette ligne :

LocateRegistry.createRegistry(2005);

Comme auparavant, nous utilisons le numéro de port 2005 dans cet exemple. C'est équivalent au fait de lancer rmiregistry 2005 depuis la ligne de commande, mais c'est souvent plus pratique lorsque l'on développez son code RMI puisque cela élimine les étapes supplémentaires qui consistent à arrêter et redémarrer le registre. Une fois cette instruction exécutée, la méthode bind( ) de la classe Naming peut être utilisée comme précédemment.

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Jean-Pierre Vidal, Alban Peignier ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java