5. Formulários dinâmicos com restrições de integridade
Vamos agora desenvolver uma nova aplicação chamada strutspersonne2 utilizando
- um formulário dinâmico como no strutspersonne1
- um ficheiro que declara as restrições de integridade a serem verificadas pelos campos deste formulário dinâmico
5.1. Declaração de restrições de integridade
A classe utilizada para armazenar os valores de nome e idade na aplicação strutspersonne1 foi declarada da seguinte forma:
<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>
Tivemos de escrever a classe PersonneDynaForm para fornecer um método validate capaz de verificar se os valores de nome e idade no formulário dinâmico eram válidos. Havia dois tipos de verificações a realizar:
- ambos os campos não podiam estar vazios
- o campo da idade tinha de corresponder à máscara (expressão regular) \s*\d+\s*
Estas duas validações estão entre as que o ambiente StrutsValidator pode realizar. Este ambiente é fornecido com o Struts e inclui várias classes que se encontram nos ficheiros commons-validator.jar e jakarta-oro.jar. Se seguiu o procedimento, explicado no início deste documento, para instalar as bibliotecas Struts no Tomcat, estas bibliotecas já estão disponíveis no Tomcat. Certifique-se de que também estão disponíveis no JBuilder. Isto também foi explicado no início deste documento.
A nova declaração do formulário no struts-config.xml passa a ser a seguinte:
<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>
Portanto, há muito pouca diferença. A classe associada ao formulário dinâmico é agora uma classe StrutsValidator predefinida: org.apache.struts.validator.DynaValidatorForm. O programador já não precisa de escrever uma classe. Em vez disso, especifica as restrições de integridade que o formulário deve verificar num ficheiro XML separado. O controlador Struts deve conhecer o nome deste ficheiro. Para tal, surge uma nova secção de configuração no ficheiro struts-config.xml:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"
/>
</plug-in>
A secção <plug-in> é utilizada para carregar uma classe externa ao Struts. O seu principal atributo é classname, que especifica o nome da classe a ser instanciada. O objeto instanciado poderá necessitar de ser inicializado. Isto é feito utilizando a tag set-property, que possui dois atributos:
- property: o nome da propriedade a ser inicializada
- value: o valor da propriedade
Aqui, a classe DynaValidatorForm precisa de saber duas informações:
- o ficheiro XML que define as restrições de integridade padrão que a classe pode validar.
- o ficheiro XML que define as restrições de integridade para os vários formulários dinâmicos da aplicação
Esta informação é fornecida aqui pela propriedade pathnames. O valor desta propriedade é uma lista de ficheiros XML que o validador irá carregar:
- validator-rules.xml é o ficheiro que define as restrições de integridade padrão. Está incluído no Struts. Pode ser encontrado em <struts>\lib juntamente com o seu ficheiro de definição DTD:
![]()
- validation.xml define as restrições de integridade para os vários formulários dinâmicos da aplicação. É criado pelo programador. O seu nome é arbitrário.
Estes dois ficheiros podem ser colocados em qualquer local dentro de WEB-INF. No nosso exemplo, serão colocados diretamente dentro de WEB-INF:

5.2. Escrever restrições de integridade para formulários dinâmicos
O ficheiro validation.xml conterá as nossas restrições de integridade para os campos nome e idade do formulário frmPersonne do tipo org.apache.struts.validator.DynaValidatorForm. O seu conteúdo é o seguinte:
<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>
Devem ser seguidas as seguintes regras de escrita:
- Todas as regras estão contidas dentro de uma tag <form-validation>
- A tag <global> é utilizada para definir informações com âmbito global, ou seja, válidas para todos os formulários, caso existam vários formulários. Aqui, colocámos constantes dentro de <global>. Uma constante é definida pelo seu nome (a tag <constant-name>) e pelo seu valor (a tag <constant-value>). Definimos a constante `entierpositif` com a expressão regular que deve ser verificada para um inteiro positivo: `_^\s*\d+\s*$` (uma sequência de dígitos possivelmente precedida e/ou seguida por espaços).
- A tag <formset> define o conjunto de formulários para os quais existem restrições de integridade a verificar
- A tag <form name="unFormulaire"> é utilizada para definir as restrições de integridade para um formulário específico, aquele cujo nome é especificado pelo atributo name. Este nome deve existir na lista de formulários definidos em struts-config.xml. Aqui, o formulário frmPersonne utilizado é definido em struts-config.xml pela seguinte secção:
<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>
- Uma tag de formulário contém tantas tags <field> quantas forem as restrições de integridade a verificar no formulário. Uma tag <field> possui os seguintes atributos:
- property: nome do campo do formulário para o qual as restrições de integridade são definidas
- depends: lista de restrições de integridade a verificar.
- As restrições possíveis são as seguintes: obrigatório (o campo não pode estar vazio), máscara (o valor do campo deve corresponder a uma expressão regular definida pela variável máscara), inteiro: o valor do campo deve ser um número inteiro, byte (byte), long (inteiro longo), float (ponto flutuante de precisão simples), double (ponto flutuante de precisão dupla), short (inteiro curto), data (o valor do campo deve ser uma data válida), intervalo (o valor do campo deve estar dentro de um determinado intervalo), e-mail: (o valor do campo deve ser um endereço de e-mail válido), ...
- As restrições de integridade são verificadas na ordem especificada pelo atributo depends. Se uma restrição falhar, as seguintes não são testadas.
- Cada restrição está associada a uma mensagem de erro definida por uma chave. Aqui estão alguns exemplos no formato restrição (chave): required (errors.required), mask (errors.invalid), integer (errors.integer), byte (errors.byte), long (errors.long), ...
- As mensagens de erro associadas às chaves anteriores são definidas no ficheiro 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.
- Como podemos ver acima, as mensagens estão em inglês. Além disso, estão incluídas como comentários no ficheiro, o que especifica que devem ser colocadas no ficheiro de mensagens da aplicação. Recorde-se que isto está definido numa secção do ficheiro struts-config.xml:
O atributo parameter indica que as mensagens da aplicação se encontram no ficheiro
WEB-INF/classes/resources/personneressources.properties.
As mensagens de erro devem, portanto, ser colocadas neste ficheiro. São adicionadas às já existentes:
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
Vamos analisar as restrições de integridade no ficheiro validation.xml, uma a uma, para as explicar:
<formset>
<form name="frmPersonne">
<field property="nom" depends="required">
<arg0 key="personne.nom"/>
</field>
...
</form>
</formset>
Note que estas restrições de integridade aplicam-se aos campos nome e idade de um formulário dinâmico definido em 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 que os nomes do formulário e dos seus campos sejam idênticos em ambos os ficheiros. A restrição de integridade no campo name (property="name") especifica que o campo não pode estar vazio (depends="required"). Se não for esse o caso, será gerado um objeto ActionError com a chave errors.required. A mensagem associada a esta chave encontra-se no ficheiro person.resources.properties:
Podemos ver que esta mensagem utiliza um parâmetro {0}. O seu valor é definido pela tag <arg0> na restrição de integridade:
Mais uma vez, arg0 é designado por uma chave que também se encontra no ficheiro de mensagens:
Juntando tudo, a mensagem de erro gerada se o campo do nome não for preenchido é:
Vamos agora analisar a segunda restrição, a que se aplica ao campo «idade»:
<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>
Existem duas restrições para o campo «idade»: «required» e «mask». Podemos repetir a explicação anterior para a restrição «required». Isto significa que a mensagem de erro associada a esta restrição será:
A segunda restrição é máscara. Isto significa que o conteúdo do campo deve corresponder a um padrão expresso por uma expressão regular. O valor desta restrição é definido numa tag <var> que define uma variável chamada mask (<var-name>) com o valor ${positiveInteger} (<var-value>). positiveInteger é uma constante definida na secção <global> do ficheiro com o valor da expressão regular ^\s*\d+\s*$. A restrição de integridade é, portanto, que a idade deve ser uma sequência de um ou mais dígitos, opcionalmente precedida ou seguida por espaços. Se esta restrição não for cumprida, será gerado um objeto ActionError com a chave errors.invalid. No ficheiro de mensagens, esta chave está associada à seguinte mensagem:
A restrição deve definir um valor para o parâmetro {0}. Faz-o utilizando a tag <arg0>:
No ficheiro de mensagens, a chave person.age está associada à seguinte mensagem:
A mensagem de erro que será gerada se a restrição da máscara não for verificada é, portanto:
5.3. As classes da aplicação
Numa aplicação Struts, as classes a escrever são as destinadas aos formulários (ActionForm ou derivadas) e as destinadas às ações (Action ou derivadas). Na nova aplicação strutspersonne2, já não existe uma classe para o formulário. O conteúdo do formulário é definido em struts-config.xml e as restrições de integridade associadas em WEB-INF/validation.xml. Na aplicação strutspersonne1, a classe FormulaireAction para o processamento do formulário era a seguinte:
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
}
A classe FormAction esperava receber um formulário na forma de um objeto DynaActionForm (código em caixa). No entanto, no ficheiro struts-config.xml, a classe do formulário está definida da seguinte forma:
<form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
...
</form-bean>
O formulário será, portanto, colocado num objeto DynaValidatorForm. Verifica-se que esta classe deriva da classe DynaActionForm. O método execute da nossa classe FormulaireAction permanece, portanto, válido. Não é necessário reescrevê-lo.
5.4. Implementação e teste da aplicação strutspersonne2
5.4.1. Criação do contexto
Chamámos a esta nova aplicação strutspersonne2. Criamos uma nova definição no ficheiro <tomcat>\conf\serveur.xml do Tomcat 4.x:
Depois de fazer isto, o Tomcat deve ser reiniciado. Pode verificar a validade do contexto solicitando o URL:
http://localhost:8080/strutspersonne2/

5.4.2. As Visualizações
Copie a pasta views da aplicação strutspersonne1 para a pasta da aplicação strutspersonne2. As visualizações não sofreram alterações.

5.4.3. A pasta WEB-INF
Copie a pasta WEB-INF da aplicação strutspersonne1 para a pasta da aplicação strutspersonne2. Alguns ficheiros foram alterados:

O ficheiro de configuração struts-config.xml fica assim:
<?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>
Este ficheiro é idêntico ao da aplicação strutspersonne1, exceto pela definição dinâmica do formulário e pela inclusão do plugin de validação (secções em caixas).
Adicione o seguinte ficheiro validation.xml à pasta 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>
Adicione os ficheiros validator-rules.xml e validator-rules_1_1.dtd, localizados em <struts>\lib, à pasta WEB-INF:

Na pasta WEB-INF/classes, existe agora apenas uma classe:

Na pasta WEB-INF\classes\resources, encontrará o seguinte ficheiro de mensagens, 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. Testes
Estamos prontos para os testes. Abaixo encontram-se algumas capturas de ecrã que convidamos o leitor a reproduzir.
Solicitamos o URL http://localhost:8080/strutspersonne2/formulaire.do:

Clicamos no botão [Submeter] sem preencher os campos:

Tentamos novamente com um erro no campo da idade:

Recebemos a seguinte resposta:

Tente novamente, desta vez introduzindo os valores corretos:

Recebemos a seguinte resposta:

5.6. Conclusão
Demonstrámos que a utilização de formulários dinâmicos com restrições de integridade «padrão» evita a necessidade de criar classes para as representar. Se as restrições de integridade se desviarem do padrão, então somos novamente obrigados a criar classes para verificar estas novas restrições.