5. Formulários dinâmicos com restrições de integridade
Vamos agora desenvolver uma nova aplicação chamada strutspersonne2 que utiliza
- um formulário dinâmico, tal como no strutspersonne1
- um ficheiro de declaração de restrições de integridade a verificar pelos campos deste formulário dinâmico
5.1. Declaração das restrições de integridade
A classe utilizada para armazenar os valores «nome» e «idade» da 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 criar a classe PersonneDynaForm para dispor de um método de validação capaz de verificar se os valores «nome» e «idade» do formulário dinâmico eram válidos. As verificações a realizar eram de dois tipos:
- os dois campos não podiam estar vazios
- o campo «idade» devia verificar o máscara (expressão regular) \s*\d+\s*
Estas duas verificações fazem parte das verificações que o ambiente StrutsValidator pode efetuar. Este ambiente vem com o Struts e inclui várias classes que se encontram em commons-validator.jar e jakarta-oro.jar. Se seguiu o procedimento, explicado no início deste documento, para a instalação das bibliotecas do Struts no Tomcat, essas 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 ficheiro 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 predefinida de StrutsValidator: org.apache.struts.validator.DynaValidatorForm. O programador já não precisa de escrever nenhuma classe. Especifica as restrições de integridade que o formulário deve verificar num ficheiro XML separado. O controlador Struts deve conhecer o nome desse 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> serve para carregar uma classe externa ao Struts. O seu principal atributo é classname, que indica o nome da classe a instanciar. O objeto instanciado pode precisar de ser inicializado. Isto é feito através das tags set-property, que têm dois atributos:
- property: o nome da propriedade a inicializar
- value: o valor da propriedade
Neste caso, a classe DynaValidatorForm precisa de conhecer duas informações:
- o ficheiro XML, que define as restrições de integridade padrão que a classe sabe verificar;
- o ficheiro XML, que define as restrições de integridade dos diferentes formulários dinâmicos da aplicação
Estas duas informações são aqui fornecidas pela propriedade pathnames. O valor desta propriedade é uma lista de ficheiros XML que o validador irá carregar:
- O validator-rules.xml é o ficheiro que define as restrições de integridade padrão. É fornecido com o Struts. Encontra-se em <struts>\lib, acompanhado do seu ficheiro de definição DTD:
![]()
- validation.xml define as restrições de integridade dos diferentes formulários dinâmicos da aplicação. É criado pelo programador. O seu nome é livre.
Estes dois ficheiros podem ser colocados em qualquer local abaixo de WEB-INF. No nosso exemplo, serão colocados diretamente abaixo de WEB-INF:

5.2. Definição das restrições de integridade dos 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>
As regras de escrita a respeitar são as seguintes:
- o conjunto de regras encontra-se numa baliza <form-validation>
- A baliza <global> serve para definir informações de âmbito global, c.a.d. É válida para todos os formulários, caso existam vários. Aqui, colocámos constantes na baliza <global>. Uma constante é definida pelo seu nome (baliza <constant-name>) e pelo seu valor (baliza <constant-value>). Definimos a constante «entierpositif» com o valor da expressão regular que um número inteiro positivo deve satisfazer: ^\s*\d+\s*$ (uma sequência de algarismos, eventualmente precedida e/ou seguida de espaços).
- A baliza <formset> define o conjunto de formulários para os quais existem restrições de integridade a verificar
- a baliza <form name="unFormulaire"> serve para definir as restrições de integridade de um formulário específico, aquele cujo nome é indicado pelo atributo name. Este nome deve existir na lista de formulários definidos em struts-config.xml. Neste caso, o formulário frmPersonne utilizado está 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 baliza <form> contém tantas balizas <field> quantas forem as restrições de integridade a verificar no formulário. Uma baliza <field> tem os seguintes atributos:
- property: nome do campo do formulário para o qual se definem restrições de integridade
- depends: lista das restrições de integridade a verificar.
- As restrições possíveis são as seguintes: required (o campo não pode estar vazio), mask (o valor do campo deve corresponder a uma expressão regular definida pela variável mask), integer: o valor do campo deve ser um inteiro, byte (octet), long (inteiro longo), float (real simples), double (real duplo), short (inteiro curto), date (o valor do campo deve ser uma data válida), range (o valor do campo deve estar dentro de um intervalo determinado), email: (o valor do campo deve ser um endereço de e-mail válido), ...
- as restrições de integridade são verificadas na ordem do atributo «depends». Se uma restrição não for verificada, as seguintes não são testadas.
- cada restrição está associada a uma mensagem de erro definida por uma chave. Aqui estão algumas delas na forma 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 estão definidas no ficheiro validator-rules.xml:
# Mensagens de erro do 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.
- Como se pode ver acima, as mensagens estão em inglês. Além disso, encontram-se em comentários no ficheiro, onde se indica que devem ser colocadas no ficheiro de mensagens da aplicação. Recorde-se que este 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/ressources/personneressources.properties.
As mensagens de erro devem, portanto, ser colocadas neste ficheiro. São adicionadas às que já existem:
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>
# Mensagens de erro do Struts Validator
# a chave está predefinida e não deve ser alterada
# a mensagem de erro associada é livre
# A mensagem pode ter até 4 parâmetros {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
Vamos analisar as restrições de integridade do 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>
Recorde-se que estas restrições de integridade se aplicam 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 nos dois ficheiros. A restrição de integridade no campo «nome» (property="nom") indica que o campo não pode estar vazio (depends="required"). Caso contrário, será gerado um objeto ActionError com a chave errors.required. A mensagem associada a esta chave encontra-se no ficheiro personne.ressources.properties:
Verifica-se que esta mensagem utiliza um parâmetro {0}. O valor deste é definido pela baliza <arg0> da restrição de integridade:
Mais uma vez, arg0 é designado por uma chave que também se encontra no ficheiro de mensagens:
Se juntarmos tudo isto, a mensagem de erro gerada caso o campo «nom» não esteja preenchido é:
Analisemos agora a segunda restrição, a relativa ao 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>
Existem duas restrições para o campo «age»: «required» e «mask». Podemos repetir a explicação anterior para a restrição «required». Conclui-se que a mensagem de erro associada a esta restrição será:
A segunda restrição é «mask». Isto significa que o conteúdo do campo deve corresponder a um padrão expresso por uma expressão regular. O valor desta última é definido numa baliza <var> que define uma variável com o nome «mask» (<var-name>), cujo valor é ${entierpositif} (<var-value>). «entierpositif» é uma constante definida na secção <global> do ficheiro, cujo valor é a expressão regular ^\s*\d+\s*$. A restrição de integridade consiste, portanto, em que a idade deve ser uma sequência de um ou mais algarismos, eventualmente precedida ou seguida de espaços. Se esta restrição não for verificada, 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 através da baliza <arg0>:
No ficheiro de mensagens, a chave personne.age está associada à seguinte mensagem:
A mensagem de erro que será gerada se a restrição «mask» não for verificada é, portanto:
5.3. As classes da aplicação
Numa aplicação Struts, as classes a escrever são as dos formulários (ActionForm ou derivadas) e as das 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 de 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 {
// temos um formulário válido; caso contrário, não teríamos chegado até aqui
DynaActionForm formulaire=(DynaActionForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//executar
}
A classe FormulaireAction esperava receber um formulário na forma de um objeto DynaActionForm (código em caixilhado). 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 do tipo DynaValidatorForm. Verifica-se que esta classe deriva da classe DynaActionForm. O método execute da nossa classe FormulaireAction continua, portanto, válido. Não é necessário reescrevê-lo.
5.4. Implantaçã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:
Feito isto, é necessário reiniciar o Tomcat. É possível verificar a validade do contexto acedendo ao URL:
http://localhost:8080/strutspersonne2/

5.4.2. As vistas
Vamos copiar a pasta «vues» da aplicação «strutspersonne1» para a pasta da aplicação «strutspersonne2». Com efeito, as vistas não sofreram alterações.

5.4.3. A pasta «WEB-INF»
Vamos copiar 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 passa a ter o seguinte conteúdo:
<?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, com exceção da definição dinâmica do formulário e da inserção do plugin de validação (partes emolduradas).
Adiciona-se ao ficheiro WEB-INF o seguinte ficheiro de validação 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>
Adicionamos ao ficheiro WEB-INF os ficheiros validator-rules.xml e validator-rules_1_1.dtd, que se encontram em <struts>\lib:

Na pasta WEB-INF/classes, resta apenas uma única classe:

Na pasta WEB-INF\classes\ressources, encontra-se 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>
# Mensagens de erro do Struts Validator
# a chave está predefinida e não deve ser alterada
# a mensagem de erro associada é livre
# a mensagem pode ter até 4 parâmetros {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. Testes
Estamos prontos para os testes. Seguem-se algumas capturas de ecrã que o leitor é convidado a reproduzir.
Pede-se o URLhttp://localhost:8080/strutspersonne2/formulaire.do:

Utiliza-se o botão [Envoyer] sem preencher os campos:

Repetimos o processo com um erro no campo «age»:

Obtém-se a seguinte resposta:

Vamos tentar novamente, desta vez introduzindo valores corretos:

Obtém-se a seguinte resposta:

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