5. Moduli dinamici con vincoli di integrità
Ora svilupperemo una nuova applicazione chiamata strutspersonne2 che utilizza
- un modulo dinamico come in strutspersonne1
- un file di dichiarazione dei vincoli di integrità da verificare tramite i campi di questo modulo dinamico
5.1. Dichiarazione dei vincoli di integrità
La classe utilizzata per memorizzare i valori «nome» e «età» dell’applicazione strutspersonne1 era 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 creare la classe PersonneDynaForm per disporre di un metodo di validazione in grado di verificare che i valori “nome” e “età” del modulo dinamico fossero validi. Le verifiche da effettuare erano di due tipi:
- entrambi i campi dovevano essere non vuoti
- il campo «age» doveva soddisfare il pattern (espressione regolare) \s*\d+\s*
Queste due verifiche fanno parte di quelle che l’ambiente StrutsValidator è in grado di eseguire. Questo ambiente è fornito con Struts e comprende una serie di classi presenti in commons-validator.jar e jakarta-oro.jar. Se avete seguito la procedura, illustrata all’inizio del documento, per l’installazione delle librerie Struts all’interno di Tomcat, tali librerie sono già disponibili in Tomcat. Assicuratevi che lo siano anche in JBuilder. Anche questo è stato spiegato all’inizio del 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 predefinita di StrutsValidator: org.apache.struts.validator.DynaValidatorForm. Lo sviluppatore non deve più scrivere alcuna classe. Specifica i vincoli di integrità che il modulo deve verificare in un file separato denominato XML. Il controller Struts deve conoscere il nome di questo file. A tal fine, 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> serve a caricare una classe esterna a Struts. Il suo attributo principale è classname, che indica il nome della classe da istanziare. L'oggetto istanziato potrebbe richiedere un'inizializzazione. Ciò avviene tramite i tag set-property, che presentano 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 è in grado di verificare;
- il file XML che definisce i vincoli di integrità dei diversi moduli dinamici dell'applicazione
Queste due informazioni sono fornite 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. È fornito con Struts. Si trova in <struts>\lib insieme al relativo file di definizione DTD:
![]()
- validation.xml definisce i vincoli di integrità dei vari moduli dinamici dell’applicazione. Viene creato dallo sviluppatore. Il nome è a scelta.
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. Definizione dei vincoli di integrità dei 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>
Le regole di scrittura da rispettare sono le seguenti:
- l'insieme delle regole si trova all'interno di un tag <form-validation>
- Il tag <global> serve a definire informazioni con ambito globale, c.a.d, valide per tutti i moduli, qualora ce ne fossero più di uno. In questo caso, all’interno di <global> sono state inserite delle costanti. Una costante è definita dal proprio nome (tag <constant-name>) e dal proprio valore (tag <constant-value>). Definiamo la costante «entierpositif» con come valore l’espressione regolare che deve soddisfare 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"> serve a definire i vincoli di integrità di un modulo specifico, quello il cui nome è specificato dall’attributo name. Questo nome deve essere presente 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 modulo. Un tag <field> presenta i seguenti attributi:
- property: nome del campo del modulo per il quale si definiscono i vincoli di integrità
- depends: elenco dei vincoli di integrità da verificare.
- I vincoli possibili sono i seguenti: required (il campo non deve essere vuoto), mask (il valore del campo deve corrispondere a un'espressione regolare definita dalla variabile mask), integer: il valore del campo deve essere un numero intero, byte (byte), long (intero lungo), float (numero reale semplice), double (numero reale doppio), short (intero corto), date (il valore del campo deve essere una data valida), range (il valore del campo deve rientrare in un intervallo specificato), email: (il valore del campo deve essere un indirizzo e-mail valido), ...
- i vincoli di integrità vengono verificati nell’ordine specificato dall’attributo depends. Se un vincolo non viene verificato, quelli successivi non vengono controllati.
- Ogni vincolo è associato a un messaggio di errore definito da una chiave. Eccone alcuni nella forma vincolo (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:
# Messaggi di errore di Struts Validator
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 si può vedere sopra, i messaggi sono in inglese. Inoltre, sono presenti come commenti nel file in cui è indicato che devono essere inseriti nel file dei messaggi dell'applicazione. Ricordiamo che quest'ultimo è 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/ressources/personneressources.properties.
I messaggi di errore devono quindi essere inseriti in questo file. Vengono aggiunti a quelli già 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>
# Messaggi di errore di Struts Validator
# la chiave è predefinita e non deve essere modificata
# il messaggio di errore associato è libero
# il messaggio può avere fino a 4 parametri da {0} a {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à del file validation.xml per spiegarli:
<formset>
<form name="frmPersonne">
<field property="nom" depends="required">
<arg0 key="personne.nom"/>
</field>
...
</form>
</formset>
Si ricorda che questi vincoli di integrità si applicano ai campi "nome" e "età" 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 relativi campi siano identici in entrambi i file. Il vincolo di integrità sul campo "nome" (property="nom") specifica che il campo non deve essere vuoto (depends="required"). In caso contrario, verrà generato un oggetto ActionError con chiave errors.required. Il messaggio associato a questa chiave si trova nel file personne.ressources.properties:
Si nota che questo messaggio utilizza un parametro {0}. Il valore di quest’ultimo è definito dal tag <arg0> del vincolo di integrità:
Anche in questo caso, arg0 è indicato da una chiave che si trova anch’essa nel file dei messaggi:
Mettendo tutto insieme, il messaggio di errore generato se il campo «nome» non è compilato è:
Analizziamo ora il secondo vincolo, quello relativo al campo «age»:
<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 "age": "required" e "mask". È possibile ripetere la spiegazione precedente per il vincolo "required". Ne consegue che il messaggio di errore associato a questo vincolo sarà:
Il secondo vincolo è mask. Ciò significa che il contenuto del campo deve corrispondere a un modello espresso da un’espressione regolare. Il valore di quest'ultimo è definito in un tag <var> che definisce una variabile denominata mask (<var-name>) il cui valore è ${entierpositif} (<var-value>). entierpositif è una costante definita nella sezione <global> del file con valore pari all'espressione regolare ^\s*\d+\s*$. Il vincolo di integrità consiste quindi nel fatto che l’età deve essere una sequenza di una o più cifre, eventualmente preceduta o seguita da spazi. Se questo vincolo non viene verificato, verrà generato un oggetto ActionError con 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 tramite il tag <arg0>:
Nel file dei messaggi, la chiave personne.age è associata al seguente messaggio:
Il messaggio di errore che verrà generato se il vincolo mask non viene verificato è quindi:
5.3. Le classi dell’applicazione
In un'applicazione Struts, le classi da scrivere sono quelle dei moduli (ActionForm o derivate) e quelle delle azioni (Action o derivate). Nella nuova applicazione strutspersonne2 non è più presente 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 di 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 {
// il modulo è valido, altrimenti non saremmo arrivati a questo punto
DynaActionForm formulaire=(DynaActionForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//esegui
}
La classe FormulaireAction si aspettava di ricevere un modulo sotto forma di oggetto DynaActionForm (codice in riquadro). 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 di tipo DynaValidatorForm. Si dà il caso che questa classe derivi 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 strutspersonne2 questa nuova applicazione. 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
Si copierà la cartella "vues" dell'applicazione strutspersonne1 nella cartella dell'applicazione strutspersonne2. Infatti, le viste non sono cambiate.

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

Il file di configurazione struts-config.xml diventa il seguente:
<?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'inserimento del plugin di convalida (parti evidenziate).
In WEB-INF si aggiunge il seguente file di convalida validation.xml:
<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>
Si aggiungono a WEB-INF i file validator-rules.xml e validator-rules_1_1.dtd, che si trovano in <struts>\lib:

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

Nella cartella WEB-INF\classes\ressources si trova il seguente file dei 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>
# Messaggi di errore di Struts Validator
# la chiave è predefinita e non deve essere modificata
# il messaggio di errore associato è libero
# il messaggio può avere fino a 4 parametri da {0} a {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.
Si richiede l'http://localhost:8080/strutspersonne2/formulaire.do: URL:

Si utilizza il pulsante [Envoyer] senza compilare i campi:

Si riprova con un errore nel campo «età»:

Si ottiene la seguente risposta:

Riproviamo inserendo questa volta i valori corretti:

Si ottiene la seguente risposta:

5.6. Conclusione
Abbiamo dimostrato che l’utilizzo di moduli dinamici con vincoli di integrità “standard” evita la creazione di classi per rappresentarli. Se i vincoli di integrità esulano dallo standard, si è nuovamente costretti a creare classi per verificare questi nuovi vincoli.