Skip to content

5. Moduli dinamici con vincoli di integrità

Svilupperemo ora una nuova applicazione chiamata strutspersonne2 utilizzando

  • un modulo dinamico come in strutspersonne1
  • un file che dichiara i vincoli di integrità da verificare dai campi di questo modulo dinamico

5.1. Dichiarazione dei vincoli di integrità

La classe utilizzata per memorizzare i valori di nome ed età nell'applicazione strutspersonne1 era stata dichiarata come segue:

    <form-beans>
        <form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

Abbiamo dovuto scrivere la classe PersonneDynaForm per fornire un metodo validate in grado di verificare che i valori di nome ed età nel modulo dinamico fossero validi. C'erano due tipi di controlli da eseguire:

  • entrambi i campi non devono essere vuoti
  • il campo dell'età doveva corrispondere alla maschera (espressione regolare) \s*\d+\s*

Queste due validazioni sono tra quelle che l'ambiente StrutsValidator è in grado di eseguire. Questo ambiente è fornito insieme a Struts e include una serie di classi presenti nei file commons-validator.jar e jakarta-oro.jar. Se avete seguito la procedura, spiegata all'inizio di questo documento, per l'installazione delle librerie Struts all'interno di Tomcat, queste librerie sono già disponibili in Tomcat. Assicuratevi che siano disponibili anche in JBuilder. Anche questo è stato spiegato all'inizio di questo documento.

La nuova dichiarazione del modulo in struts-config.xml diventa la seguente:

    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

La differenza è quindi minima. La classe associata al modulo dinamico è ora una classe StrutsValidator predefinita: org.apache.struts.validator.DynaValidatorForm. Lo sviluppatore non deve più scrivere una classe. Invece, specifica i vincoli di integrità che il modulo deve verificare in un file XML separato. Il controller Struts deve conoscere il nome di questo file. Per ottenere ciò, nel file struts-config.xml compare una nuova sezione di configurazione:

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

La sezione <plug-in> viene utilizzata per caricare una classe esterna a Struts. Il suo attributo principale è classname, che specifica il nome della classe da istanziare. L'oggetto istanziato potrebbe dover essere inizializzato. Ciò avviene utilizzando il tag set-property, che presenta due attributi:

  • property: il nome della proprietà da inizializzare
  • value: il valore della proprietà

In questo caso, la classe DynaValidatorForm deve conoscere due informazioni:

  • il file XML che definisce i vincoli di integrità standard che la classe può convalidare.
  • il file XML che definisce i vincoli di integrità per i vari moduli dinamici dell'applicazione

Queste informazioni sono fornite qui dalla proprietà pathnames. Il valore di questa proprietà è un elenco di file XML che il validatore caricherà:

  • validator-rules.xml è il file che definisce i vincoli di integrità standard. È incluso in Struts. Si trova in <struts>\lib insieme al suo file di definizione DTD:

Image

  • validation.xml definisce i vincoli di integrità per i vari moduli dinamici dell'applicazione. Viene creato dallo sviluppatore. Il suo nome è arbitrario.

Questi due file possono essere collocati in qualsiasi posizione all'interno di WEB-INF. Nel nostro esempio, saranno collocati direttamente all'interno di WEB-INF:

Image

5.2. Scrittura dei vincoli di integrità per i moduli dinamici

Il file validation.xml conterrà i nostri vincoli di integrità per i campi nome ed età del modulo frmPersonne di tipo org.apache.struts.validator.DynaValidatorForm. Il suo contenuto è il seguente:

<form-validation>
    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>
</form-validation>    

È necessario seguire le seguenti regole di scrittura:

  • Tutte le regole sono contenute all'interno di un tag <form-validation>
  • Il tag <global> viene utilizzato per definire informazioni con ambito globale, ovvero valide per tutti i moduli nel caso in cui ve ne siano più di uno. Qui abbiamo inserito delle costanti all'interno di <global>. Una costante è definita dal suo nome (il tag <constant-name>) e dal suo valore (il tag <constant-value>). Definiamo la costante `entierpositif` con l'espressione regolare che deve essere verificata per un numero intero positivo: `_^\s*\d+\s*$` (una sequenza di cifre eventualmente preceduta e/o seguita da spazi).
  • Il tag <formset> definisce l'insieme dei moduli per i quali devono essere verificati i vincoli di integrità
  • Il tag <form name="unFormulaire"> viene utilizzato per definire i vincoli di integrità per un modulo specifico, quello il cui nome è specificato dall'attributo name. Questo nome deve esistere nell'elenco dei moduli definiti in struts-config.xml. In questo caso, il modulo frmPersonne utilizzato è definito in struts-config.xml dalla seguente sezione:
    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
  • Un tag form contiene tanti tag <field> quanti sono i vincoli di integrità da verificare per il form. Un tag <field> ha i seguenti attributi:
    • property: nome del campo del modulo per il quale sono definiti i vincoli di integrità
    • dipende: elenco dei vincoli di integrità da verificare.
      • I possibili vincoli sono i seguenti: obbligatorio (il campo non deve essere vuoto), maschera (il valore del campo deve corrispondere a un'espressione regolare definita dalla variabile maschera), intero: il valore del campo deve essere un numero intero, byte (byte), long (numero intero lungo), float (numero in virgola mobile a precisione singola), double (numero in virgola mobile a doppia precisione), short (numero intero corto), date (il valore del campo deve essere una data valida), range (il valore del campo deve rientrare in un determinato intervallo), email: (il valore del campo deve essere un indirizzo email valido), ...
      • I vincoli di integrità vengono controllati nell'ordine specificato dall'attributo depends. Se un vincolo fallisce, quelli successivi non vengono verificati.
      • Ogni vincolo è collegato a un messaggio di errore definito da una chiave. Ecco alcuni esempi nel formato constraint (chiave): required (errors.required), mask (errors.invalid), integer (errors.integer), byte (errors.byte), long (errors.long), ...
      • I messaggi di errore associati alle chiavi precedenti sono definiti nel file validator-rules.xml:
   # Struts Validator Error Messages
   errors.required={0} is required.
   errors.minlength={0} can not be less than {1} characters.
   errors.maxlength={0} can not be greater than {1} characters.
   errors.invalid={0} is invalid.

   errors.byte={0} must be a byte.
   errors.short={0} must be a short.
   errors.integer={0} must be an integer.
   errors.long={0} must be a long.
   errors.float={0} must be a float.
   errors.double={0} must be a double.

   errors.date={0} is not a date.
   errors.range={0} is not in the range {1} through {2}.
   errors.creditcard={0} is an invalid credit card number.
   errors.email={0} is an invalid e-mail address.
  • Come possiamo vedere sopra, i messaggi sono in inglese. Inoltre, sono inclusi come commenti nel file, il che specifica che dovrebbero essere inseriti nel file dei messaggi dell'applicazione. Ricordiamo che questo è definito in una sezione del file struts-config.xml:
    <message-resources 
      parameter="ressources.personneressources"
    null="false"
  />    

L'attributo parameter indica che i messaggi dell'applicazione si trovano nel file

WEB-INF/classes/resources/personneressources.properties.

I messaggi di errore devono quindi essere inseriti in questo file. Vengono aggiunti a quelli esistenti:

personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

   # Struts Validator error messages
    # the key is predefined and must not be changed
    # the associated error msg is free
    # the msg can have up to 4 parameters {0} to {3}

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>
   errors.minlength=<li>Le champ [{0}] foit avoir au moins {1} caractère.</li>
   errors.maxlength=<li>Le champ [{0}] ne peut avoir plus de {1} caractères.</li>
   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

   errors.byte=<li>{0} doit être un octet.</li>
   errors.short=<li>{0} doit être un entier court.</li>
   errors.integer=<li>{0} doit être un entier.</li>
   errors.long=<li>{0} doit être un entier long.</li>
   errors.float=<li>{0} doit être un réel simple.</li>
   errors.double=<li>{0} doit être un réel double.</li>

   errors.date=<li>{0} n'est pas une date valide.</li>
   errors.range=<li>{0} doit être dans l'intervalle {1} à {2}.</li>
   errors.creditcard=<li>{0} n'est pas un numéro de carte valide.</li>
   errors.email=<li>{0} n'est pas une adresse électronique valide.</li>

   personne.nom=nom
     personne.age=age

Esaminiamo uno per uno i vincoli di integrità nel file validation.xml per spiegarli:

    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
...
    </form>
    </formset>

Si noti che questi vincoli di integrità si applicano ai campi name e age di un modulo dinamico definito in struts-config.xml:

        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            

È importante che i nomi del modulo e dei suoi campi siano identici in entrambi i file. Il vincolo di integrità sul campo name (property="name") specifica che il campo non deve essere vuoto (depends="required"). In caso contrario, verrà generato un oggetto ActionError con la chiave errors.required. Il messaggio associato a questa chiave si trova nel file person.resources.properties:

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>

Possiamo notare che questo messaggio utilizza un parametro {0}. Il suo valore è impostato dal tag <arg0> nel vincolo di integrità:

             <arg0 key="personne.nom"/>      

Anche in questo caso, arg0 è designato da una chiave che si trova anche nel file dei messaggi:

   personne.nom=nom

Mettendo tutto insieme, il messaggio di errore generato se il campo del nome non è compilato è:

<li>Le champ [nom] doit être renseigné.</li>

Analizziamo ora il secondo vincolo, quello relativo al campo "età":

    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
...
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>

Ci sono due vincoli per il campo età: required e mask. Possiamo ripetere la spiegazione precedente per il vincolo required. Ciò significa che il messaggio di errore associato a questo vincolo sarà:

<li>Le champ [age] doit être renseigné.</li>

Il secondo vincolo è "maschera". Ciò significa che il contenuto del campo deve corrispondere a un modello espresso da un'espressione regolare. Il valore di questo vincolo è definito in un tag <var> che definisce una variabile denominata mask (<var-name>) con il valore ${positiveInteger} (<var-value>). positiveInteger è una costante definita nella sezione <global> del file con il valore dell'espressione regolare ^\s*\d+\s*$. Il vincolo di integrità è quindi che l'età deve essere una sequenza di una o più cifre, facoltativamente preceduta o seguita da spazi. Se questo vincolo non viene soddisfatto, verrà generato un oggetto ActionError con la chiave errors.invalid. Nel file dei messaggi, questa chiave è associata al seguente messaggio:

   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

Il vincolo deve definire un valore per il parametro {0}. Lo fa utilizzando il tag <arg0>:

             <arg0 key="personne.age"/>      

Nel file dei messaggi, la chiave person.age è associata al seguente messaggio:

     personne.age=age

Il messaggio di errore che verrà generato se il vincolo della maschera non viene verificato è quindi:

<li>Le champ [age] est incorrect.</li>

5.3. Le classi dell'applicazione

In un'applicazione Struts, le classi da scrivere sono quelle per i moduli (ActionForm o derivate) e quelle per le azioni (Action o derivate). Nella nuova applicazione strutspersonne2, non esiste più una classe per il modulo. Il contenuto del modulo è definito in struts-config.xml e i vincoli di integrità associati in WEB-INF/validation.xml. Nell'applicazione strutspersonne1, la classe FormulaireAction per l'elaborazione del modulo era la seguente:

package istia.st.struts.personne;

....

public class FormulaireAction
  extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,
    ServletException {
     // we have a valid form, otherwise we wouldn't have got here
    DynaActionForm formulaire=(DynaActionForm)form;
    request.setAttribute("nom",formulaire.get("nom"));
    request.setAttribute("age",formulaire.get("age"));
    return mapping.findForward("reponse");
  }//execute
}

La classe FormAction si aspettava di ricevere un modulo sotto forma di oggetto DynaActionForm (codice incapsulato). Tuttavia, nel file struts-config.xml, la classe del modulo è definita come segue:

        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
...
        </form-bean>            

Il modulo verrà quindi inserito in un oggetto DynaValidatorForm. Si scopre che questa classe deriva dalla classe DynaActionForm. Il metodo execute della nostra classe FormulaireAction rimane quindi valido. Non è necessario riscriverlo.

5.4. Distribuzione e test dell'applicazione strutspersonne2

5.4.1. Creazione del contesto

Abbiamo chiamato questa nuova applicazione strutspersonne2. Creiamo una nuova definizione nel file <tomcat>\conf\serveur.xml di Tomcat 4.x:

                <Context path="/strutspersonne2" docBase="e:/data/serge/web/struts/personne2" />

Una volta fatto ciò, è necessario riavviare Tomcat. È possibile verificare la validità del contesto richiedendo l'URL:

http://localhost:8080/strutspersonne2/

Image

5.4.2. Le viste

Copiare la cartella views dall'applicazione strutspersonne1 alla cartella dell'applicazione strutspersonne2. Le viste non sono cambiate.

Image

5.4.3. La cartella WEB-INF

Copiare la cartella WEB-INF dall'applicazione strutspersonne1 nella cartella dell'applicazione strutspersonne2. Alcuni file sono cambiati:

Image

Il file di configurazione struts-config.xml assume il seguente aspetto:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

    <action-mappings>

      <action
          path="/main"
          name="frmPersonne"
            validate="true"
            input="/erreurs.do"
            scope="session"
          type="istia.st.struts.personne.FormulaireAction"
      >
            <forward name="reponse" path="/reponse.do"/>
        </action>

      <action
          path="/erreurs"
          parameter="/vues/erreurs.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/reponse"
          parameter="/vues/reponse.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/formulaire"
          parameter="/vues/formulaire.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
    </action-mappings>

    <message-resources 
      parameter="ressources.personneressources"
    null="false"
  />    

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

</struts-config>

Questo file è identico a quello dell'applicazione strutspersonne1, ad eccezione della definizione dinamica del modulo e dell'inclusione del plugin di validazione (sezioni in riquadro).

Aggiungere il seguente file validation.xml a WEB-INF:

<form-validation>
    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>
</form-validation>

Aggiungere i file validator-rules.xml e validator-rules_1_1.dtd, che si trovano in <struts>\lib, a WEB-INF:

Image

Nella cartella WEB-INF/classes è ora presente una sola classe:

Image

Nella cartella WEB-INF\classes\resources, troverai il seguente file di messaggi, personneressources.properties:

personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

   # Struts Validator error messages
    # the key is predefined and must not be changed
    # the associated error msg is free
    # the msg can have up to 4 parameters {0} to {3}

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>
   errors.minlength=<li>Le champ [{0}] foit avoir au moins {1} caractère.</li>
   errors.maxlength=<li>Le champ [{0}] ne peut avoir plus de {1} caractères.</li>
   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

   errors.byte=<li>{0} doit être un octet.</li>
   errors.short=<li>{0} doit être un entier court.</li>
   errors.integer=<li>{0} doit être un entier.</li>
   errors.long=<li>{0} doit être un entier long.</li>
   errors.float=<li>{0} doit être un réel simple.</li>
   errors.double=<li>{0} doit être un réel double.</li>

   errors.date=<li>{0} n'est pas une date valide.</li>
   errors.range=<li>{0} doit être dans l'intervalle {1} à {2}.</li>
   errors.creditcard=<li>{0} n'est pas un numéro de carte valide.</li>
   errors.email=<li>{0} n'est pas une adresse électronique valide.</li>

   personne.nom=nom
  personne.age=age

Image

5.5. Test

Siamo pronti per i test. Di seguito sono riportati alcuni screenshot che il lettore è invitato a riprodurre.

Richiediamo l'URL http://localhost:8080/strutspersonne2/formulaire.do:

Image

Clicchiamo sul pulsante [Invia] senza compilare i campi:

Image

Proviamo di nuovo inserendo un valore errato nel campo dell'età:

Image

Otteniamo la seguente risposta:

Image

Prova di nuovo, inserendo questa volta i valori corretti:

Image

Otteniamo la seguente risposta:

Image

5.6. Conclusione

Abbiamo dimostrato che l'uso di forme dinamiche con vincoli di integrità "standard" evita la necessità di creare classi per rappresentarle. Se i vincoli di integrità si discostano dallo standard, allora siamo nuovamente tenuti a creare classi per verificare questi nuovi vincoli.