Fiches / Articles

Cadre Fonctionnel

Développement de Servlet et présentation de données

Cadre Technique

NT/Tomcat

Identifiant 

SYS_SERVLET_01

Référent technique 


Version 

1.0

Auteur 

Alexandre Brillant

Date

04/01

Source

Documentation Tomcat / Programmation Java côté serveur de Andrew Patzer

La conception de servlet est :

Son principal inconvénient est l'intégration de la présentation dans les traitements logiques ce qui limite les changements possibles.

Exemple de servlet 

import javax.servlet.* ;
import javax.servlet.http.* ;
import java.io.* ;

public class TestServlet extends
HttpServlet {

public void init( ServletConfig config ) {
 super.init( config );
 message = config.getInitParameter( "message" );
}

public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 res.setContentType(  "text/html" );

 String userName = req.getParameter( "user" );

 PrintWriter pw = res.getWriter(); pw.println( "<html><body>"
+ userName + ":" + message + "</body><html>"
);

}

protected String message;

}

Lorsqu'une servlet est démarrée la méthode init est appelée avec un objet ServletConfig en paramètre. Cet objet contient tous les paramètres présents dans le script de déploiement de la servlet.

Lorsqu'une requête est reçue par une servlet, la méthode service est appelée avec les objets ServletRequest et ServletResponse en paramètre. Cette dernière décide ensuite d'appeler une méthode adaptée au type de requête HTTP.

Les méthodes doGet et doPost servent respectivement à traiter les requêtes HTTP de type GET et de type POST. La première étant liée à la notion de chaîne de requête (Paramètres dans l'URL) alors que la seconde est propre à l'utilisation de formulaire. D'autres méthodes existent pour les extensions du protocole HTTP (put, trace...).

Les paramètres de la requête sont obtenus grâce à l'objet HttpServletRequest et la méthode getParameter. La méthode getParameterNames permet d'obtenir une énumération de tous les noms de paramètre. Dans un formulaire c'est le nom du champ qui correspond à ce paramètre.

Il est important avant de retourner une réponse de préciser le type MIME du document. Il s'agit d'une convention indiquant au browser comment traiter le document. La plupart du temps, la méthode setContentType spécifiera le type MIME text/html correspondant à des pages HTML.

Exemples de type MIME : image/gif, image/jpeg, text/html



Le document en réponse est transmis grâce aux méthodes getOutputStream ou getWriter à partir de l'objet de réponse HttpServletResponse.

Terminaison

Il peut arriver qu'une instance de servlet soit détruite en cas d'arrêt du serveur ou de problème mémoire. La méthode destroy est alors appelée pour libérer toutes les ressources.

L'objet de réponse HttpServletResponse permet de retourner des erreurs HTTP par la méthode sendError. Les codes d'erreurs sont présents sous forme de constante dans l'objet HttpServletResponse (SC_BAD_REQUEST,SC_NOT_FOUND,...).

La journalisation des problèmes peut aussi avoir lieu grâce à la méthode log de l'objet ServletContext.

Exemple :



public void doGet( HttpServletRequest req, HttpServletResponse
res) throws ServletException, IOException {

...

try {

...

} catch( Exception ex ) {
  ex.printStackTrace();
  getServletContext().log( ex.getMessage );
  res.sendError( res.SC_INTERNAL_SERVER_ERROR, ex.getMessage()
);

}



Session utilisateur

HTTP est un protocole sans état, c'est à dire ne conservant pas d'information concernant les utilisateurs. Pour maintenir des informations au cours d'une utilisation (exemple du caddie) la servlet permet l'utilisation de session. Cette session est maintenue grâce à des cookies, des URL longues ou des champs cachés.

Exemple :


public void doGet( HttpServletRequest req, HttpServletResponse
res) throws ServletException, IOException {

 // Création d'une session et stockage d'un paramètre

 HttpSession session = req.getSession( true );
 String userName = req.getParameter( "user" );	
 session.putValue( "username", userName );
 …

}

La méthode getSession de l'objet de requête HttpServletRequest permet d'obtenir une session. Lorsque le paramètre est à "true" l'objet est soit crée lors d'une première connexion soit obtenu à partir d'une précédente connexion, s'il est à "false" la session n'est jamais créée.

Les méthodes putValue, getValue et removeValue servent respectivement au stockage, à la consultation d'un objet et à la suppression d'un objet. Ces objets résidant toujours côté serveur n'ont pas à implémenter l'interface "Serializable".

La terminaison d'une session peut se faire par la méthode invalidate(). La plupart du temps une session dure 30mn. Cependant, il est possible configurer le moteur de servlet pour une autre valeur.

L'objet Cookie permet de redéfinir l'équivalent des sessions pour un contrôle plus fin par domaine d'application des sessions.

Contexte des servlets

L'objet ServletContext permet de partager des ressources entre Servlet indépendamment des sessions utilisateurs (exemple pour un chat). Les méthodes setAttribute, getAttribute et removeAttribute servent respectivement à stocker, consulter et supprimer un attribut.

Exemple :


public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

  String username = req.getParameter( "username" );

  Vector v = (Vector)getServletContext().getAttribute(
"userlist" );

  If ( v == null ) {
  	v = new Vector();
	getServletContext().setAttribute( "userlist", v );
  }

  v.add( username );
}
   …



Cet exemple stocke tous les utilisateurs connectés dans un attribut userlist. Cet objet peut ainsi être utilisé par une autre servlet.

Il est possible de faire communiquer des servlets par l’objet RequestDispatcher obtenu à partir de l’objet ServletContext. Cette technique permet de faire du chaînage de servlet. Les méthodes forward et include servent respectivement à la transmission et à l’intégration de requêtes d’autres servlets.

Exemple :


public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

 RequestDispatch rd = getServletContext().getRequestDispatcher( « /servlet/servlet2 » ) ;

 rd.include(  req , res ) ;

 res.getWriter().println( « Fin de document »
) ;

}

Cet exemple inclut le résultat d’une autre servlet et le complète par le message « Fin de document ».

Différentes techniques permettent de limiter l'impact du code HTML dans un code de traitement.

Une solution simple est d'agencer des objets correspondant aux balises HTML masquant toute la partie génération du source HTML.

Exemple tiré du package com.wrox.util :


public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 HTML h = new HTML( "Titre1" );
 h.add( HTML.line, "", false );
 h.add( HTML.NORMAL, new Date().toString(), true );
 PrintWriter output = res.getWriter(); 
 output.println( h.getPage() ) ;
}

Cet exemple émet une page de titre « Titre1 » contenant la date courante

HexaMaj :

HexaMaj est une API devéloppée par hexadev (www.hexadev.com) intégrant la notion de modèle de présentation. Un modèle représente un document HTML éditable avec DreamWeaver . Dans ce modèle des balises particulières délimitent des zones de données à mettre à jour. La fusion des données de l’application et du modèle construise la page HTML finale.

L’API de base offre plusieurs types de tags :

JSP (Java Server Pages) :

Afin de respecter la séparation Vue et Controller (paradigme MVC Model View Controller), les JSP peuvent être utilisées en complément pour transmettre la page HTML final.

Une JSP n’est qu’une forme particulière de servlet mélangeant dans du code HTML, du code Java. On peut limiter la portée du code Java intégré pour n’effectuer qu’une transformation simple de donnée en une partie HTML.

On utilisera l’objet de communication inter-servlet RequestDispatcher. La servlet exécute des traitements pour l’obtention de données et les retransmet sous une forme plus simple vers une JSP.

Exemple :


public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

// Récupération et traitement des données

…

// Stockage dans la session

…

// Transmission à un JSP

RequestDispatcher rd = getServletContext().getRequestDispatcher( « /jsp/dataView1.jsp ») ;

rd.forward( req, res ) ;

…

}

XSLT (XSL Transformation)

Lorsque les données sont déjà en XML, il peut être avantageux d’appliquer une transformation XSL vers HTML. Cela élimine l’étape d’analyse et de transformation des données au niveau JSP. Cela n’interdit plus l’utilisation d’un format différent de HTML.

Quelques stratégies de développement

Le problème du mélange du code de présentation et de traitement peut aussi se retrouver à un niveau plus fin. Par exemple, le code permettant de contrôler l’identité d’un utilisateur et le code traitement qui suit ne devrait pas être à un même niveau. Sans pour autant introduire un chaînage de servlets, un découpage en classe et sous classe permet déjà d’améliorer la visibilité du code.

La couche la plus haute est responsable des premiers traitements et de la transmission des prochains traitements à une sous classe. Ce passage se fait simplement par la surcharge d’une méthode.

Exemple :

classe abstraite UserServlet :


protected doUserValid(HttpServletRequest req, HttpServletResponse res) {}

public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 HttpSession session = req.getSession( true ) ;
 if ( session.getValue( « username » ) !=null ) 
 	doUserValid( req, res ) ;
	else {
	  res.sendError( res.SC_UNAUTHORIZED, «Pas d’utilisateur reconnu !») ;
	}

classe ActionServlet dérivant de UserServlet :
   public void doUserValid( HttpServletRequest req, HttpServletResponse res ) {
	…
}

Dans cet exemple, seul ActionServlet est déclarée comme étant une servlet, la partie contrôle de l’utilisateur est masquée par la classe UserServlet.

Découpage par délégation :

Cette méthode n’est pas antagoniste de la précédente, elle permet de distribuer des traitements sur une seule servlet. L’idée est d’associer à un URL une paramètre d’action qui décidera quelle classe de traitement appelée.

Lors de l’initialisation de la servlet, on créé une table de correspondance (table de hachage) entre un nom d’action et une classe implémentant une interface de traitement (par exemple Action). Lorsqu’une requête est reçue, on vérifie si un paramètre dans la chaîne de requête ou dans un champ caché d’un formulaire action existe. Si oui, on transmet à l’objet associé les paramètres de type HttpServletRequest et HttpServletResponse.

L’avantage est une uniformisation de l’initialisation des servlets et une possibilité de changer les traitements en cours d’exécution.

Exemple :


public class MajorServlet etends HttpServlet {

	public void init( ServletConfig config ) {
	 super.init( config ) ;
	 actions = new HashMap() ;
	 actions.put( « processField1 », new ProcessField() ) ;
	 actions.put( « processField2 », new ExtProcessField() ) ;
	}

}	

public void doGet( HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 string action = req.getParameter( « action ») ;
 if ( !actions.containsKey( action ) )
 defaultAction( req, res ) ;
 else
  ( ( Action )actions.get( action ) ).processAction( req, res ) ;
}	
…

}