Cette section décrit l'utilisation de Struts dans gestCV. Il est important de connaître Struts pour comprendre les mécanismes mis en place dans gestCV. Ces mécanismes ont pour but de simplifier le développement et d'éviter les redondances de code.
Les thèmes traités seront :
Lorsqu'un formulaire est postée, Struts permet de récupérer les données saisies du formulaire dans une classe de type Formulaire. Chacun des champs du formulaire HTML doit etre décrit dans le formulaire.
Il existe deux grands types de formulaires :
GestCV utilise la deuxième méthode de description des formulaires Struts. En effet les formulaires dynamiques ne permettent pas le mécanisme d'héritage, qui permet d'enrichir son formulaire avec des propriétés/méthods, qui seraient communes a tous les formulaires. Plus exactement, GestCV utilise la classe ValidatorActionForm qui permet de bénéficier du Struts Validator (règles de validation du formulaire décrites dans un fichier XML).
La classe formulaire contiendra des getter/setter pour chacun des champs du formulaire HTML à posté.
Un des problèmes récurrents lors de la mise en place d'une Application WEB est le type de donnée utilisé. En effet un formulaire HTML est constitué de chaîne de caractère java.lang.String, et les objets métiers doivent être constitués d'objet de type plus complexes comme java.lang.Integer, java.lang.Date,...
Une conversion de type de données de String vers les types complexes et inversement doit être effectuée à un moment ou un autre. Le premier réflexe est de typer les getter/setter des formulaires Struts, mais cette solution engendre de nombreux problèmes lorsque les types complexes sont utilisés.
Dans le cas d'un formulaire qui aurrait un champs typé (ex : Integer), lorsque l'utilisateur postera le formulaire en renseignant le champs typé qui ne correspondrait pas au type (ex : saisie de la chaîne de caractère "12345-" ), la page se réaffiche en vidant le champs du formulaire, ce qui peut être perturbant pour l'utilisateur qui a pu se tromper sur sa saisie (l'utilisateur voulait saisir 123456 au lieu de 12345-).
Pour éviter tous ses problèmes, les getter/setter des formulaires de GestCV seront toujours de type java.lang.String. La conversion de données des formulaires vers les objets métiers, et inversement s'effectue dans l'Action Struts, après que le formulaire soit peuplé.
Le peuplage d'un formulaire à l'aide d'un objet métier nécéssite :
Ceci est très rébarabatif, surtout si le formulaire contient un nombre conséquent de propriétés. GestCV implémente un formulaire CommonsForm, qui permet en une seule ligne de code de peupler le formulaire à l'aide d'un objet métier et inversement.
Tous les formulaires de GestCV hérite de la classe CommonsForm. Ce formulaire étendu, permet de :
// Peuplage du formulaire myForm en utilisant la DTO, myDTO provenant de l'appel d'une méthode de recherche d'un service myForm.populateFormWithObject(myDTO);
La méthode populateObjectWithForm permet de peupler l'objet DTO en utilisant le formulaire dans l'action Struts :
// Peuplage de la DTO myDTO en utilisant le formulaire MyForm (saisi par l'utilisateur) MyDTO myDTO = new MyDTO(); myForm.populateObjectWithForm(myDTO);
Ces deux méthodes permettent d'une part de convertir les types de données des propriétés getter/setter des DTO et formulaires et d'autres part de peupler en une seule ligne de code le formulaire vers un objet métier et inversement.
Il est important de respecter deux règles :
Voici la classe CommonsForm :
public class CommonsForm extends ValidatorActionForm { private static Log log = LogFactory.getLog(CommonsForm.class); private String dispatch; public String getDispatch() { return dispatch; } public void setDispatch(String dispatch) { this.dispatch = dispatch; } /** * Copie les valeurs de l'objet Form CommonsForm * avec l'objet o en utilisant les getter/setter * du form et de l'objet o (DTO, BO, VO...). * @param o Objet (DTO, BO, VO...) * a utiliser pour populer le form */ public void populateFormWithObject(Object o) { try { CommonsBeanUtils.copyProperties(this, o); } catch (Exception e) { throw new CommonsRuntimeException("Erreur dans la methode populateFormWithObject : ", e); } } /** * Copie les valeurs de l'objet o (DTO, BO, VO...) * avec l'objet Form CommonsForm * en utilisant les getter/setter * du form et de l'objet o. * @param o Objet (DTO, BO, VO...) * a populer avec les valeurs du form */ public void populateObjectWithForm(Object o) { try { CommonsBeanUtils.copyProperties(o, this); } catch (Exception e) { throw new CommonsRuntimeException("Erreur dans la methode populateObjectWithForm : ", e); } } }
Struts met à disposition une classe DispatchAction, qui permet d'implémenter plusieurs ActionForward pour une même action. GestCV implémentera pour chacune des actions la méthode load, qui permet de charger les données d'une page.
Il est important de distinguer deux types de données qui doivent être chargés dans un formulaire de modification :
Prenons le cas du formulaire de modification d'un collaborateur, qui contient une liste d'agence (select HTML). Au premier chargement de la page, les informations du collaborateur sont récupérées de la base pour mettre à jour le formulaire, et la liste des agences est chargée.
Le prénom étant obligatoire, si l'utilisateur ne remplit pas ce champs, une erreur s'affiche (après avoir cliqué sur le bouton Sauver), indiquant à l'utilisateur que ce champs est obligatoire :
Dans ce cas-ci, seul la liste des agences est chargée. En effet, le collaborateur n'est pas rechargé de la base, sinon il remettrait à jour le formulaire en perdant les informations saisis par l'utilisateur.
Ce scénario met en évidence l'interêt d'implementer deux dispatch :
Au niveaux Struts, la méthode load ressemblerait à :
public ActionForward load(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { // CHARGEMENT DE L'IDENTITE DU COLLABORATEUR ET MISE A JOUR DU FORMULAIRE .... // APPEL DU DISPLAY POUR CHARGER LISTE AGENCE return display(mapping, actionForm, request, response); }
la méthode display ressemblerait à :
public ActionForward display(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { // CHARGEMENT DE LA LISTE DES AGENCES .... // RETOUR SUR LA JSP return mapping.findForward("display"); }
la méthode save, qui permet de sauvegarder un collaborateur ressemblerait à :
public ActionForward save(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception { // VALIDATION DU FORMULAIRE ActionErrors errors = form.validate(mapping, request); if (!errors.isEmpty()) { // ERREUR DE VALIDATION, SAUVEGARDE DES ERREUR ET RECHARGEMENT DE LA PAGE // SANS RECHARGER LE COLLABORATEUR DE LA BASE saveErrors(request, errors); return display(mapping, actionForm, request, response); } .... }
@TODO
@TODO
@TODO
@TODO
@TODO