Accueil > Le langage JSP et les Servlets Java > Apprentissage du framework Struts Jakarta - Partie 3

Apprentissage du framework Struts Jakarta - Partie 3

Par Sue Spielman, 14/11/2001

Traduit par Thierry, 05/09/2002

Voici le dernier article de la série sur le framework Struts.

Dans la première partie, Introduction au Framework Struts Jakarta, j’ai défini ce qu’était le framework Struts, j’ai parlé de ce qu’il pouvait accomplir et j’ai effectué un survol des divers composants utilisés. Dans la deuxième partie, Apprentissage au Framework Struts Jakarta - Partie 2, j’ai décrit les différentes phases de construction d’une petite application simple en partant de zéro et en se servant de Struts 1.0. Ce troisième article va vous montrer comment utiliser les balises Struts pour accéder au fichier ApplicationResource à partir d’un JSP.

Les classes action sont le lien entre le framework Struts et la logique business de l’application. Vous aurez tendance à garder les classes action aussi fines que possible, d’abord pour que la vraie logique de l’application soit placée dans un tiers logique séparé. Si vous faites du développement d’applications à n tiers, vous ferez en sorte que les interfaces entre les différents tiers soient aussi propres que possible. Le fait que la méthode principale d’une classe action soit nommée “perform” (traite) indique que les classes action aient pour vocation de faire quelque chose. Toute action doit être une extension de org.apache.struts.action.Action. Pour une petite application, il est possible de s’en tenir qu’à cette extension; cependant, j’ai trouvé qu’il y avait souvent des fonctions communes dans les classes action d’une application. Il est plus conceptuel (à mon avis) de créer une classe de base qui sera utilisée par toutes les actions de l’application. J’ai conçu l’application StrutsSample selon ce mode, avec une méthode typique qui pourra être utile à une classe de base tout comme à des bouts de définitions de redirections globales.

package com.oreilly.actions;
import java.io.IOException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.Enumeration;
import java.util.Properties;
import java.rmi.RemoteException;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

Ceci est la classe de base à partir de laquelle toute classe action qui utilise Struts peut être dérivée. Elle prend en considération quelques aspects d’architecture générale qui seraient plus probablement requis par une application réelle. Pour le propos de cet article, les méthodes qui ne sont pas directement en relation avec le framework Struts seront enfouies dans une boite noire et commentées de manière à ce que vous puissiez vous en servir comme squelette que vous complèterez des méthodes qui répondront à vos besoins au fur et à mesure que vous progresserez dans votre développement. Toutes les classes action doivent être dérivées de org.apache.struts.action.Action.

public abstract class AbstStrutsActionBase extends Action
{
/* Retours génériques typiques utilisés de façon à ce que le
* structs-config.xml puisse se brancher par défaut sur les
* redirections globales.
*/

protected static final String SUCCESS = "success";
protected static final String FAILURE = "failure";
protected static final String ERROR = "error";
protected static final String LOGIN = "login";
protected static final String CONFIRM = "confirm";h
protected Context jndiContext = null;

/**
* Constructeur par défaut
*/

public AbstStrutsActionBase()
{
}

/**

Ceci est une méthode “boite-noire” — en quête de l’instance EJB nécessaire. Typiquement, les classes action fonctionnent avec des beans de session EJB (ou juste des JavaBeans) qui contiennent la logique business de votre application. Au sein de projets à grande échelle, vous aurez à coeur de maintenir une nette séparation des tiers. Vous récupèrerez le contexte JNDI (NDT : Java Naming and Repertory Interface - Interface Java de gestion du nommage et des répertoires) et une instance de ce contexte, puis effectuerez une recherche du nom JNDI récupéré de l’EJB dont vous souhaitez récupérer l’interface. Voici juste un extrait de ce qui est requis :

  • Paramètre String contenant le nom JNDI à rechercher.
  • Object retourné contenant l’objet recherché.
  • Lance NamingException si la recherche du nom a failli.
  • Lance MissingResourceException si l’on ne peut récupérer le bloc de ressource.
*/
public Object lookup(String jndiName) throws NamingException,
       MissingResourceException
{
//Set up the JNDI properties to get the initial context for calls to EJB objects
if (jndiContext == null) {
ResourceBundle resource = ResourceBundle.getBundle("strutssample.properties");
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
           resource.getString(Context.INITIAL_CONTEXT_FACTORY));
properties.setProperty(Context.PROVIDER_URL,
           resource.getString(Context.PROVIDER_URL));
properties.setProperty(Context.SECURITY_PRINCIPAL,
           resource.getString(Context.SECURITY_PRINCIPAL));
properties.setProperty(Context.SECURITY_CREDENTIALS,
           resource.getString(Context.SECURITY_CREDENTIALS));
jndiContext = new InitialContext(properties);
}

Note : En production, vous souhaiterez probablement ici un bloc try/catch robuste pour mémoriser toutes les erreurs ou informations importantes. Dans cet extrait, les exceptions seront simplement jetées et l’objet sera retourné.

return (jndiContext.lookup(jndiName));
}

Ceci est l’action principale appelée à partir du framework Struts. Vous pouvez faire en sorte que cette action appelle une méthode abstraite performAction qui serait alors implémentée par chaque classe action, et qui effectuerait toutes les spécificités communes à toutes les actions comme l’enregistrement des évènements dans un fichier log. Dans notre exemple, nous ne faisons qu’utiliser le perform sous forme de classe abstraite.

  • Paramètre mapping: L’ActionMapping utilisé pour sélectionner cette instance.
  • Paramètre actionForm: Le Bean optionel ActionForm de cette requête (au cas où).
  • Paramètre request: La requête HTTP en cours de traitement.
  • Paramètre response: La réponse HTTP en cours de génération.
  • Activation de IOException si une erreur d’entrée/sortie survient.
  • Activation de ServletException si un évènement exceptionnel survient dans la Servlet.
  • Retourner l’endroit où le contrôle sera redirigé après le traitement de la requête.
public abstract ActionForward perform(ActionMapping mapping,
       ActionForm form, HttpServletRequest request,
       HttpServletResponse response)
       throws IOException,ServletException;
}

/*

Le fait d’avoir ici une autre méthode abstraite est utile pour effectuer des traitements de logging par exemple, comme illustré ci-dessous :

{
ActionForward forward = null;
// Simple log to the servlet log for informational purposes
getServlet().log("AbstStrutsActionBase.perform()
      [Action Class: "+this.getClass().getName()+" ]");
getServlet().log("AbstStrutsActionBase.perform()
      [Form Class : "+(form == null ? "null" : form.getClass().getName())+" ]");
}

Chaque classe Action devrait être une extension de AbstStrutsActionBase et implémenter sa propre méthode perform spécifique aux tâches effectuées par l’action. Si nous nous penchons sur la LoginAction, nous pouvons voir comment ce prinicpe y est appliqué.

package com.oreilly.actions;
import java.io.IOException;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import com.oreilly.forms.LoginForm;

/*

LoginAction montre comment une classe Action est appelée au sein du framework Struts. En ce qui concerne notre bout de code, nous montrons simplement comment le perform est appelé, un extrait d’action et un retour.

*/

public class LoginAction extends AbstStrutsActionBase
{

Ceci est une méthode boite-noire utilisée pour authentifier l’utilisateur. Il devrait y avoir normalement ici un Bean (ou un EJB) pour prendre en charge le traitement. Une recherche serait effectuée (comme montré dans la classe de base) et créerait une interface pour vérifier la présence du nom de l’utilisateur dans une base de données.

  • Paramètre String du nom de l’utilisateur.
  • Paramètre String du mot de passe.
  • Retourne un booléen dont la valeur “vrai” équivaut à “authentifié”, “faux” à “non authentifié”.
public boolean authenticate(String username, String password)
{

Effectuez ici la recherche et les appels appropriés. Ce code n’est pas fourni en tant que partie de notre extrait d’application. Il n’a pour but que de montrer l’interaction entre la classe action et le tiers supportant la logique business.

return(true);
}

Surpassez la classe de base avec une implémentation spécifique de la méthode perform.

  • Paramètre mapping: L’ActionMapping utilisé pour sélectionner cette instance.
  • ParamètreactionForm: Le bean optionnel AbstActionFormBase de cette requêté (au cas où).
  • Paramètrerequest: La requête HTTP en cours de traitement.
  • Paramètreresponse: La réponse HTTP en cours de génération.
  • Exception IOException: Si une erreur d’entrée/sortie survient.
  • Exception ServletException: Si une erreur Servlet survient.
public ActionForward perform(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
       throws IOException, ServletException
{
// On suppose que la connexion a échoué, ainsi la page de connexion
//  est réaffichée si l'utilisateur n'est pas authentifié
boolean validLogin=false;
ActionForward actionForward = mapping.findForward(LOGIN);
// On crée le container pour toute erreur pouvant survenir
ActionErrors errors = new ActionErrors();
// On extrait les attributs et les paramètres dont nous aurons besoin
// pour le formualire à venir.
LoginForm loginForm = (LoginForm) form;
String userName = null;
String password = null;
if (loginForm != null){
userName = loginForm.getUserName();
password = loginForm.getPassword();
validLogin = authenticate(userName, password);
}
if (validLogin){
// On redirige le contrôle à l'URI 'success' spécifiée dans le structs-config.xml
actionForward = mapping.findForward(SUCCESS);
// On sauvegarde quelque chose pour l'utiliser plus tard.
request.getSession(true).setAttribute("USERNAME", userName);
} else {
errors.add("login", new ActionError("error.login.authenticate"));
}
// Si un message est requis, on sauvegarde les clés des messages d'erreur spécifiés
// dans la requête HTTP pour utilisation par la balise.
if (!errors.empty()) {
saveErrors(request, errors);
}
// On redirige le contrôle vers l'URI appropriée telle que déterminée par l'action.
return (actionForward);
}
}

Gardez en mémoire que la LoginAction est définie en tant qu’instance de l’action login dans le fichier struts-config.xml. Quand la classe est instanciée, le framework Struts appelle la méthode perform. La signature de la méthode

public ActionForward perform(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,
       ServletException

inclut : le mapping, un mécanisme de localisation des mappings d’action disponibles, le formulaire qui fournit les données à cette action, et les HttpServletRequest et HttpServletResponse standard.

Avec ces informations, une Action peut rassembler toutes les données dont elle a besoin pour effectuer ses tâches. Dans notre exemple, les informations nécessaires sont tirées du formulaire, principalement le nom de l’utilisateur et le mot de passe. Ceci est requis pour authentifier l’utilisateur. L’appel de la méthode authenticate est le point central de la logique business de l’application. Au sein de cette requête, il devrait y avoir la recherche de l’EJB ou l’accès à une base de données. Vous pouvez maintenant comprendre pourquoi j’ai introduit une méthode simple de recherche dans la classe AbstStrutsActionBase — cette recherche pourrait être utilisée par toutes les classes Action si le système était basé sur des EJB.

En fonction de la valeur retournée, l’action passera à l’étape appropriée. Si l’utilisateur a été authentifié, nous pouvons donc poursuivre et sauvegarder quelques informations au sein de la requête pour utilisation ultérieure et retourner une redirection réussie. Cette redirection est utilisée par l’ActionMapping qui recherche dans le fichier struts-config.xml où aller ensuite. Si nous jétons de nouveau un oeil à l’ActionMapping concernant l’action “login”, une réussite nous mènera vers Welcome.jsp. En cas d’échec, une erreur est positionnée, et la page d’erreur appropriée est affichée.

Développer la logique business de l’application

Dans une application réelle, c’est là que nous devrions incorporer la logique business. Dans notre exemple, cela signifierait d’écrire l’EJB qui est appelé dans la méthode authenticate de la classe LoginAction. Comme nous pouvons le voir, il est possible d’obtenir une situation correcte pouvant être exécutée en plaçant la logique business dans une boite noire. Je vous recommande de faire en sorte que le framework de votre application soit correctement finalisé avant d’attaquer le développement du composant de la logique business. Cela fera une chose de moins à débugger. Nous allons passer outre la logique business parce qu’elle sort du cadre de cet article, mais je pense que vous vous en êtes fait une idée de ce qu’elle représente.

Créer les JSP qui prendront en charge les flux de l’ActionMappings

C’est ici que toutes les pièces devraient commencer à se mettre en place. En utilisant le fichier struts-config.xml, vous voulez être sûr qu’il y aura un JSP correspondant à ceux définis dans l’ActionMappings. Dans notre cas, nous avons Login.jsp, Welcome.jsp, et Errorpage.jsp. Il est important de noter que même si j’ai mappé la redirection vers un JSP, il est tout à fait logique et nécessaire de mapper vers d’autres actions dans une application réelle. Notre Login.jsp ressemble à cela :

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
<head>
<title><bean:message key="login.title"/></title>
</head>
<body>
<html:errors/>
<h3>Enter your username and password to login:</h3>
<html:form action="login.action" focus="userName" >
<html:text property="userName" size="30" maxlength="30"/>
<html:password property="password" size="16" maxlength="16" redisplay="false"/>
<html:submit property="submit" value="Submit"/>
<html:reset/>
</html:form>
</body>
</html>

Struts fournit une librairie compréhensible de balises (basée sur les librairies JSP de balises personnalisées). Ces librairies facilitent la construction de l’interface utilisateur. L’avantage de se servir de ces librairies tient dans le fait qu’elles fournissent des fonctionnalités supplémentaires. Par exemple, l’extrait suivant pourrait être placé dans un formulaire JSP :

<input type="text" name="userName" value="">

En utilisant les librairies de balises Struts, la ligne du dessus deviendrait :

<html:text property ="userName">

Un exemple de librairie de balises Struts est défini par l’instruction :

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>.

Bien que je n’ai pas l’intention d’aller plus en détail dans la description de toutes les balises disponibles dans les diverses librairies de balises (taglib) de Struts, j’en ai utilisée quelques-unes dans notre exemple. Si vous êtes sur le point de construire des JSP compliqués, je vous suggère de vous familiariser par vous même avec tout ce qui est disponible. Elles peuvent être très utiles et vous économiser pas mal de temps. Vous trouverez plus de détails dans dans les Struts Developers Guides.

Il a quelques points à éclaircir dans notre JSP. D’abord, la ligne

<title><bean:message key="login.title"/></title>

montre l’utilisation du fichier ApplicationResource que nous avons mentionné plus haut. Cela vous libère du coding en dur des textes dans votre application.

De plus, <html:errors/> est ce qui utilise la collection ActionErrors que nous avons créée pour retourner toutes les erreurs survenant durant la validation des données au sein de LoginForm, ou de LoginAction.

Le formulaire défini dans la balise

<html:form action="login.action" focus="userName">

représente ce qui actionne réellement le process Struts. L’action qui est définie ici, login.action, doit être appariée à un ActionMapping dans le fichier struts-config.xml. A partir de là, les actions, formulaires et redirections appropriés sont définis. A la soumission de ce formulaire (submit), l’action est attrapée par la ActionServlet de Struts. Nous parlerons de son mode de définition en dessous.

Notre Welcome.jsp montre simplement comment il est possible d’utiliser les mécanismes standard disponibles en JSP pour passer des informations à partir des actions vers les pages :

<html>
<title>Welcome to Struts</title>
<body>
<p>Welcome <%= (String)request.getSession().getAttribute("USERNAME") %></p>
</p>You have logged in successfully!</p>
</body>
</html>

L’attribut USERNAME a été positionné dans la méthode perform() du LoginAction.

Construire les fichiers appropriés de configuration

Nous avons déjà pas mal parlé du fichier struts-config.xml. Habituellement, le contenu de ce fichier est construit au fur et à mesure. Cependant, à cette étape de la démarche de développement, il est bon de faire un pas en arrière et d’examiner de la manière la plus claire possible le fichier de configuration et de s’assurer que tout y est correct. Les noms finaux des classes Action, les JSP et les formulaires devraient être correctement identifiés et définis dans ce fichier. La seule chose dont nous n’avons pas encore parlée est le fichier web.xml. Ce fichier est utilisé par le container JSP, Tomcat dans ce cas, pour donner des informations spécifiques concernant l’application. Le fichier web.xml pour l’application StrutsSample ressemble à ceci :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Voici la configuration de l'application web qui permet au "strutsSample"
de fonctionner sous Apache Tomcat.
-->

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>oreilly</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>com.oreilly.ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>oreilly</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>Login.jsp</welcome-file>
</welcome-file-list>

<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/struts-form.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-form.tld</taglib-location>
</taglib>

</web-app>

La balise <servlet> sert à définir l’instance de org.apache.struts.action.ActionServlet. Dans ce cas, nous l’appelons oreilly. Deux paramètres init sont importants ici : l’application qui définit le fichier de ressources qui contient toutes les chaines de caractères de l’application, et l’emplacement du fichier struts-config.xml. Vous aurez peut être remarqué que la forme de l’URL du “servlet mapping” est la même que celle que nous avions utilisée dans notre attribut de formulaire action. Le “servlet mapping” indique à Tomcat d’envoyer toutes les requêtes qui se terminent par .action à la servlet oreilly. Vouos pouvez spécifier n’importe quelle extension ici. Vous verrez des références à l’extension .do dans Struts, mais je trouve que .action est plus explicite. La balise “welcome-file-list” est la page par défaut à afficher dans l’application. Enfin, nous listons toutes les librairies de balises Struts que nous serons amenés à utiliser.

Construire/Tester/Déployer

Maintenant que tout est en place, il est relativement facile de construire, tester et déployer notre application. Avec Ant, la construction se fait relativement sans peine. Si vous n’avez jamais utilisé Ant auparavant, je vous recommanderais vraiment d’y jeter un oeil. Il est facile à apprendre et permet de maintenir proprement un environnement de construction. J’ai ajouté un fichier build.xml qui fonctionne avec notre exemple. Vous pouvez télécharger tous les fichiers utilisés dans cet article, construire puis créer un fichier strutsSample.war en tapant simplement Ant dans le répertoire où se trouve le fichier build.xml. Vous devez d’abord télécharger et installer la dernière Version de Ant. Cela prend à peu près 10 minutes pour le téléchargement et l’installation.

La structure de notre application est comme suit :

StrutsSample directory
*.jsp
WEB_INF directory
Struts config files (struts-config.xml, web.xml)
Classes directory (strutsSample application package structure)
Lib directory (struts.jar)

Une fois que votre application est placée dans un fichier .war, vous n’avez plus qu’à la placer dans le répertoire webapps de Tomcat, et démarrer Tomcat. Le fichier .war sera alors déployé et un nouveau contexte sera créé pour l’application dans Tomcat. Le fichier web.xml que nous avons créé indique à Tomcat où aller chercher le reste des informations requises pour l’application. Vous devriez alors être capable d’accéder à l’application en tapant l’adresse http://localhost:8080/strutsSample. Par défaut, Tomcat utilise le port 8080, donc aucun paramètre spécial n’est requis. Login.jsp affichera la page d’accueil et vous pourrez tester le tout.

Conclusion

Dans cette série d’articles, nous avons examiné complètement le process d’obtention des besoins d’une application jusqu’à sa construction à partir de zéro par l’utilisation du framework Struts. Bien que les fichiers à considérer soient plus nombreux dans Struts qu’avec des JSP, il y a vraiment beaucoup de bénéfices à en tirer de part l’utilisation d’un modèle MVC et de part son application à un environnement complexe. La majeure partie du temps passé à élaborer une première application est passé à comprendre comment assembler les différentes parties qui entrent en action.

Grace à l’aide apportée par ces articles sur Struts, vous devriez avoir maintenant une bonne compréhension de ce que sont réellement les différents composants, où ils s’intègrent, de ce qui leur est nécessaire, et avoir une bonne démarche de développement à suivre. Struts n’en est qu’à ses balbutiements et je pense qu’il prouvera sa valeur en tant qu’outil à faire ce que nous aimons tous faire : construire des applications.

Textes originaux en anglais sur O’Reilly - ONJava.com : JSP and Servlets technologies par Sue Spielman

Thierry Le langage JSP et les Servlets Java , , , , ,

  1. Pas encore de commentaire
  1. Pas encore de trackbacks
S'abonner aux commentaires de cet article