Logo XStream

  1. Présentation
  2. Installation
  3. Utilisation
[bas de page]

Présentation

Apache Struts est un framework libre utilisé dans le développement d’applications JEE et dont la finalité est de faciliter l’implémentation du pattern MVC. Les servlets tels qu’ils apparaissaient dans les projets classiques sont remplacés par un servlet de contrôle et d’aiguillage des requêtes de l’utilisateur. Struts permet par ailleurs de valider les données que ce dernier pourrait envoyer via des formulaires et intègre un certain nombre de taglibs censées faciliter et optimiser l’écriture des JSP.

Struts est un framework dont la structure est très particulière. Son installation est simplifiée par l’importation de projets-type mais sa prise en main demande relativement peu d’efforts. Le paramétrage des actions est assez lourd et demande, quant à lui, d’avoir effectué une analyse complète du projet pour bien respecter la conception MVC.

Pour des petits projets, mieux vaut s’abstenir d’utiliser Struts. Enfin, si Struts v1 a été largement utilisé depuis sa distribution d’origine, un suivi de moins en moins continu est assuré par Apache au profit de Struts v2.

[bas de page]

Installation

[haut de page]

Le protocole d'installation qui suit a été rédigé pour l'environnement de développement Eclipse.

  1. Commencez par télécharger la dernière version stable de Struts v1 sur le site officiel. Dans notre cas, il s'agit de la version 1.3.10 dont voici un lien direct. A cette adresse, il est possible de télécharger la distribution complète de Struts v1 qui comprend les sources individuelles et sous forme de bibliothèques, la documentation et des exemples de projets présentant la configuration adéquate.

  2. Il est ensuite conseillé d’importer un de ces fichiers .war sous Eclipse (de préférence struts-1.3.10-all/struts-1.3.10/apps/struts-blank-1.3.10.war) et de construire simplement son propre projet autour. On peut, de ce fait, remarquer la présence d’éléments primordiaux et spécifiques au framework struts v1 :
    • Les librairies Java pour la compilation du projet (et l’utilisation des taglibs).
    • WebContent/WEB-INF/web.xml
    • WebContent/WEB-INF/struts-config.xml
    • WebContent/WEB-INF/validation.xml
    • /src/MessageResources.properties

    Si vous ne passez pas par Eclipse, l'image suivante indique la structure que le projet doit avoir :

    Structure d'un projet JEE avec Struts v1

    Charge à l'utilisateur de concevoir un projet à déployer sous Tomcat ayant cette structure. Voici l'allure des fichiers principaux :

web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE web-app PUBLIC
	"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
	"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <display-name>Nom de l'application</display-name>
  
  <servlet>
    <servlet-name>nom_front_controller</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
 </servlet>

  <servlet-mapping>
    <servlet-name>nom_front_controller</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

</web-app>

struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
  <!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>

  <form-beans>
  <!-- sample form bean descriptor for an ActionForm
    <form-bean name="inputForm" type="app.InputForm"/>
  end sample -->
  </form-beans>
	
  <global-exceptions>
  <!-- sample exception handler
    <exception key="expired.password" type="app.ExpiredPasswordException"
	path="/changePassword.jsp"/>
  end sample -->
  </global-exceptions>

  <global-forwards>
  <!-- Default forward to "Welcome" action -->
  <!-- Demonstrates using index.jsp to forward -->
    <forward name="welcome" path="/Welcome.do"/>
  </global-forwards>
  
  <action-mappings>
  <!-- Default "Welcome" action -->
  <!-- Forwards to Welcome.jsp -->
    <action path="/Welcome" forward="/pages/Welcome.jsp"/>
  <!-- sample input and input submit actions
    <action path="/Input" type="org.apache.struts.actions.ForwardAction"
	parameter="/pages/Input.jsp"/>
    <action path="/InputSubmit" type="app.InputAction" name="inputForm"
	scope="request" validate="true" input="/pages/Input.jsp"/>
    <action path="/edit*" type="app.Edit{1}Action" name="inputForm"
	scope="request" validate="true" input="/pages/Edit{1}.jsp"/>
  end samples -->
  </action-mappings>

  <message-resources parameter="MessageResources" />

  <plug-in className="org.apache.struts.tiles.TilesPlugin" >
    <set-property property="definitions-config"
	value="/WEB-INF/tiles-defs.xml" />
    <set-property property="moduleAware" value="true" />
  </plug-in>

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames"
	value="/org/apache/struts/validator/validator-rules.xml,
	/WEB-INF/validation.xml"/>
  </plug-in>

</struts-config>

[bas de page]

Utilisation

[haut de page]

Struts réalise le design pattern JEE Front Controller via une unique instance de la classe ActionServlet (et portant le nom logique « nom_front_controller » dans notre exemple). Dans la conception du projet, cet unique servlet intercepte toutes les requêtes, récupère les données sollicitées, commande un certains nombre de traitements par le biais d’objets Action et affiche finalement la vue. Le fichier classique web.xml nomme l’ActionServlet, l’associe au traitement des urls se terminant par .do (convention) et indique l’emplacement du fichier de configuration.

Le fichier struts-config.xml sert à faire le lien entre les requêtes et les traitements. L’exemple le plus simple est celui d’une bête redirection de la requête vers une JSP :

<action-mappings>
  <action path="/simpleAction" forward="/index.jsp" />
</action-mappings>


Ainsi, si l’url vaut /simpleAction.do (on se situera toujours par rapport à la racine du projet), alors la vue renvoyée correspondra à la page index.jsp et aucun traitement n’aura été effectué.

Considérons maintenant un exemple avec traitement :

SimpleAction.java
package exemple;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class SimpleAction extends Action {
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) {
		System.out.println("Hello!");
		return new ActionForward("/index.jsp");
	}
}

<action-mappings>
  <action path="/simpleAction" type="exemple.SimpleAction" />
</action-mappings>


Avant que la page index.jsp ne soit affichée, les instructions contenues dans la méthode execute() de la classe SimpleAction sont effectuées.

Pour avoir une vision plus globale de ce que permet struts, considérons le mini-projet suivant :

Une classe Personne qui a tout d'un Bean :

Personne.java
package bean;

public class Personne {
	private String nom;
	private String prenom;
	
	public Personne(){
		
	}

	public String getNom() {
		return nom;
	}
	public void setNom(String nom) {
		this.nom = nom;
	}
	public String getPrenom() {
		return prenom;
	}
	public void setPrenom(String prenom) {
		this.prenom = prenom;
	}
}


Un formulaire simple à deux champs :

form.jsp
<html:form method="post" action="/simpleAction.do">
	Nom : <html:text property="nom" /><br />
	Prenom : <html:text property="prenom" /><br />
	<html:submit value="Envoyer" />
</html:form>


Une classe héritant d'ActionForm :

SimpleForm.java
package exemple;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

public class SimpleForm extends ActionForm {
	private String nom;
	private String prenom;

	public ActionErrors validate(ActionMapping mapping,
			HttpServletRequest request) {

		ActionErrors errors = new ActionErrors();
		if(nom==null || nom.trim().equals("")){
			errors.add("erreur_nom",
				new ActionMessage("SimpleForm.nom.empty"));
		}
		if(prenom==null || prenom.trim().equals("")){
			errors.add("erreur_prenom",
				new ActionMessage("SimpleForm.prenom.empty"));
		}
		return errors;
	}

	public String getNom() {
		return nom;
	}
	public void setNom(String nom) {
		this.nom = nom;
	}
	public String getPrenom() {
		return prenom;
	}
	public void setPrenom(String prenom) {
		this.prenom = prenom;
	}
}


Une version légèrement modifiée de SimpleAction :

SimpleAction.java
package exemple;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import bean.Personne;

public class SimpleAction extends Action {
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) {
		
		SimpleForm myForm = (SimpleForm) form;
		Personne p = new Personne();
		p.setNom(myForm.getNom());
		p.setPrenom(myForm.getPrenom());
		
		request.setAttribute("personne", p);
		
		return mapping.findForward("toAfficher");
	}
}


Déclaration du Bean de formulaire dans struts-config.xml :

<form-beans>
  <form-bean name="simpleForm" type="exemple.SimpleForm" />
</form-beans>


Réalisation du mapping entre la requête, la vérification du formulaire et le traitement, toujours dans struts-config.xml :

<action-mappings>
  <action path="/simpleAction" type="exemple.SimpleAction" name="simpleForm"
	input="/errors.jsp">
    <forward name="toAfficher" path="/afficher.jsp" />
  </action>
</action-mappings>


Lorsque la requête simpleAction.do est réalisée, c’est-à-dire lorsque l’utilisateur envoie son formulaire, l’ActionServlet sollicite le Bean formulaire dont le nom logique est simpleForm (une instance de la classe exemple.SimpleForm). Les attributs de cette instance prennent alors les valeurs renseignées par l’utilisateur dans son formulaire. Le plus simple est de recourir aux balises appropriées de la taglib HTML en prenant soin de faire coïncider les valeurs des attributs property avec les libellés des attributs du formulaire. Si le contraire n’est pas spécifié dans le fichier de configuration, la méthode validate() est ensuite appelée. Elle a pour but de vérifier les champs du formulaire et de créer autant d’erreurs (objets ActionMessage) que de champs invalides. Validate() retourne une référence de l’objet ActionErrors contenant les erreurs éventuelles et deux cas peuvent alors se présenter :

  1. Le formulaire n’a pas été validé car des erreurs ont été générées. Dans ce cas, la main passe à la page référencée par la valeur de l’attribut input dans struts-config.xml : errors.jsp. Dans cette page, on peut utiliser le tag <html:errors/> pour afficher les erreurs encore attachées à la requête.
  2. Aucune erreur n’a été générée, le formulaire est donc validé. Un objet de la classe SimpleAction prend ensuite le relai et peut, comme dans notre cas, créer un Bean à partir des données recueillies dans le Bean formulaire. On peut passer une référence de ce Bean dans le scope request afin que les données qu’il contient soient affichées dans la vue. Enfin, contrairement au cas précédent, nous laissons l’objet SimpleAction rechercher l’adresse de la vue dans un contexte local propre à la servlet de traitement (ici, le nœud forward du nœud action) ou dans un contexte global :
    <global-forwards>
      <forward name="toAfficher" path="/afficher.jsp" />
    </global-forwards>


Struts permet donc de fabriquer facilement des Beans à partir de formulaires dont les champs sont passés au crible, pour peu que l’on ait la patience de réaliser les mappings nécessaires.

MessageResources.properties


Ce fichier contient une liste de correspondance entre couples de chaînes de caractères. Lorsque des messages (pouvant contenir des balises HTML) reviennent fréquemment, il peut être judicieux d’enregistrer ce message sous une entrée générique afin de pouvoir modifier d’un seul coup sa valeur sur l’intégralité des pages JSP l’employant. Jusqu’à 3 paramètres peuvent venir s’ajouter au message afin de le personnaliser dans certains cas (comme des erreurs dans des formulaires). Et ces fichiers de ressources peuvent être rattachés à une langue (locale) afin de faciliter l’aspect traduction d’un site Internet. Suivent quelques exemples d’utilisation.

Dans le cas précédent, des erreurs pouvaient survenir lors de la vérification des champs du formulaire. Ces erreurs étaient générées par la commande :

errors.add("erreur_prenom", new ActionMessage("SimpleForm.prenom.empty")) ;

Le libellé SimpleForm.prenom.empty est une clé dans la Map des ressources. Dans le fichier MessageResources.properties, on peut ainsi voir :

MessageResources.properties
errors.header=<ul>
errors.prefix=<li>
errors.suffix=</li>

errors.footer=</ul>
SimpleForm.nom.empty=Le nom n’a pas été renseigné.
SimpleForm.prenom.empty=Le prénom n’a pas été renseigné.


Ce qui donne à l'affichage :

On peut accéder directement aux messages prédéfinis en utilisant le tag message de la librairie Bean :
<bean :message key="SimpleForm.prenom.empty" />

Les Taglibs de Struts


Comme nous l’avons vu au travers des différents exemples qui précèdent, Struts met à la disposition de l’utilisateur plusieurs librairies de tags destinés à remplacer les JSTL et les tags HTML classiques : Bean Tags, HTML Tags, Logic Tags, Nested Tags et Tiles Tags. Voici quelques exemples bien choisis qui ont été employés dans notre projet :

Pour afficher la valeur d'un attribut d'un Bean :
<bean:write name="monBean" property="saPropriete" scope="request" />

Tous les éléments d'un formulaire HTML ont leur équivalent dans la librairie Struts HTML : form, button, checkbox, multibox, option(s), password, radio, reset, etc. Du fait de leur usage très courant, citons les liens et les images :

<html:img src="adresse_de_l_image" alt="texte_alternatif" />
<html:link page="/adresse" />

Ces balises, comme la plupart dans Struts, disposent d'un nombre impressionnant d'attributs dont l'utilisation judicieuse dépend de la compréhension des subtilités de la documentation (action, forward, href ou page pour les liens ?).

Les tags de la librairie Logic servent principalement à effectuer des tests sur les variables manipulées (beaucoup équivalent au classique <c:if test="${}"> avec utilisation des EL). On trouve également le tag iterate pour remplacer le forEach de la librairie Core et le tag redirect pour faire des redirections au sens HTTP :
<logic:redirect href="index.jsp"/>

La pertinence de certains tags dans des cas de développement simples reste encore à démontrer (en particulier les tags HTML). Beaucoup servent à optimiser la composition des JSP en limitant l’emploi des JSTL mais leur usage aboutit invariablement au même « défaut » : l’orientation du projet vers Struts v1 de sorte que ses possibilités d'adaptabilité, de conversion ou de migration vers d'autres outils s'en trouvent réduites.


http://struts.apache.org/
http://jmdoudoux.developpez.com/cours/developpons/java/chap-struts.php
http://javaweb.developpez.com/faq/struts/

[haut de page]