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:
![]()
- 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:

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:
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:
Possiamo notare che questo messaggio utilizza un parametro {0}. Il suo valore è impostato dal tag <arg0> nel vincolo di integrità:
Anche in questo caso, arg0 è designato da una chiave che si trova anche nel file dei messaggi:
Mettendo tutto insieme, il messaggio di errore generato se il campo del nome non è compilato è:
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à:
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:
Il vincolo deve definire un valore per il parametro {0}. Lo fa utilizzando il tag <arg0>:
Nel file dei messaggi, la chiave person.age è associata al seguente messaggio:
Il messaggio di errore che verrà generato se il vincolo della maschera non viene verificato è quindi:
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:
Una volta fatto ciò, è necessario riavviare Tomcat. È possibile verificare la validità del contesto richiedendo l'URL:
http://localhost:8080/strutspersonne2/

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

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

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:

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

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

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:

Clicchiamo sul pulsante [Invia] senza compilare i campi:

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

Otteniamo la seguente risposta:

Prova di nuovo, inserendo questa volta i valori corretti:

Otteniamo la seguente risposta:

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.