Struts

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 :

  • Extension des Formulaires Struts (voir section Formulaire Struts), classe ValidatorActionForm, pour :
    • mettre en place une méthode de peuplage automatique des formulaires vers les objets métiers (DTO) et inversement.
    • intégrer le getter/setter dispatch, commun a tout formulaire de l'application.
  • Extension des Actions Struts (voir section Action Struts), classe DispatchAction pour :
    • mettre en place les dispatchs load et display communs qui permettent le chargement et rechargement d'une page (après une erreur).
    • mettre en place les méthodes de conversions de type qui permettent de transformer le type java.lang.String constituant les getter/setter des formulaires Struts vers les autres types java.lang.Integer, java.util.Date, ... utilisés dans les objets métiers (DTO), et inversement.
    • gérer l'affichage des erreurs (automatiquement) après une erreur retournée lors de l'appel d'une méthode d'un processus métier. Voir section service.
    • mettre en place un mécanisme qui permet de mettre le focus sur le premier champs du formulaire qui aurrait déclenché une erreur de validation.
  • Utilisation de Tiles pour :
    • insérer automatiquement html:form dans les pages avec formulaires.
    • insérer automatiquement le champs html:hidden dispacth.
  • Extension du RequestProcessor, pour gérer la session utilisateur et pouvoir ensuite utiliser les informations mise en session quelque soit le contexte dans lequel on se trouve.

Formulaire Struts

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.

Type de formulaire

Il existe deux grands types de formulaires :

  • les formulaires dynamiques qui sont décrits dans le struts-config. Ces formulaires sont ensuite utilisés par l'intermédiare de la classe de base DynaActionForm
  • les formulaires décrits dans une classe héritant d'une classe de base Struts ActionForm.

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é.

Type de données

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é.

Extension du formulaire

Le peuplage d'un formulaire à l'aide d'un objet métier nécéssite :

  • pour chacune des propriétés de l'objet métier la conversion des propriétés en String.
  • pour chacune des valeurs converties l'appel des setter du formulaire.

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 :

  • définir la propriété dispatch, qui est commun à tous les formulaires de GestCV. Ce dispatch permet d'appeler la méthode dispatch adéquate (save, load,..) (voir classe DispatchAction).
  • définir une méthode de peuplage du formulaire vers un objet métier et inversement. La méthode populateFormWithObject permet de peupler l'objet formualaire en utilisant un objet métier dans l'action Struts :
      // 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 :

  • le nom des getter/setter du formulaire et de l'objet métier doivent être identique.
  • si l'objet métier contient un getter de type autres que les types JAVA standard (Integer, Date, ...) le nom de la propriété du formulaire doit être IMPERATIVEMENT différent de ce nom du getter. Voir la section Conversion de données pour plus de détails.

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);
                  }             
          }     
  
  }

Action Struts

Dispatch load/display

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 :

  • les données que l'on doit charger au premier chargement de la page. Les actions de GestCV chargeront ce type de données dans l'ActionForward load.
  • les données que l'on doit charger dans tous les cas: au premier chargement, mais aussi après une erreur de saisie. Généralement ces types de données sont des listes (select HTML) utilisés dans le formulaire. Les actions de GestCV chargeront ce type de données dans l'ActionForward display.

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 :

  • dispatch display : qui charge la liste des agences et retourne sur la JSP.
  • dispatch load : qui charge le collaborateur puis appel la méthode display, pour effectuer le chargement de la liste des agences puis retourne sur la JSP.

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);
      }
      ....
}

Méthodes de conversion des données

@TODO

Affichage des erreurs

@TODO

Focus

@TODO

Tiles

@TODO

RequestProcessor

@TODO

Conversion de donnees

@TODO

Commons BeanUtils