7. Aplicação QuiEst
Aqui descrevemos uma aplicação Struts ligeiramente mais sofisticada do que as anteriores, que foram mantidas simples para fins educativos.
7.1. A classe Users
Temos uma classe Java que armazena informações sobre os utilizadores de uma máquina Unix. Estas informações são armazenadas em três ficheiros específicos:
- /etc/passwd: lista de utilizadores
- /etc/group: lista de grupos
- /etc/aliases: lista de aliases de e-mail
O conteúdo destes três ficheiros é o seguinte:
- /etc/passwd
As linhas neste ficheiro têm o seguinte formato:
login:pwd:uid:gid:id:dir:shell
onde
login do utilizador | |
a sua palavra-passe encriptada | |
ID do utilizador | |
o seu ID de grupo | |
a sua identidade | |
o seu diretório de login | |
o seu shell |
Assim, a linha de um utilizador pode ter o seguinte aspeto:
O utilizador anterior tem o ID 110 e pertence ao grupo 57. A definição do grupo 57 pode ser encontrada no ficheiro /etc/group.
- /etc/group
As linhas neste ficheiro têm o seguinte formato:
nomGroupe:pwd:gid:membre1,membre2,....
com
nome do grupo | |
senha encriptada - este campo está normalmente vazio | |
ID do grupo | |
logins de utilizador - este campo pode estar vazio |
Assim, a linha para o grupo 57 acima poderia ser a seguinte:
o que indica que o grupo 57 se chama iup2-auto.
- /etc/aliases
As linhas neste ficheiro têm o seguinte formato:
com
alias | |
uma ou mais tabulações | |
o nome de utilizador do utilizador a quem o alias pertence |
Assim, a linha
guillaume.dupond: dupond
significa que o alias guillaume.dupond pertence ao utilizador com o nome de utilizador dupond. Lembre-se de que os aliases são utilizados em endereços de e-mail. Assim, se no exemplo anterior a máquina Unix se chamar shiva.istia.univ-angers.fr, um e-mail endereçado a guillaume.dupond@shiva.istia.univ-angers.fr será entregue na caixa de correio do utilizador com o nome de utilizador dupond nessa máquina.
Não iremos analisar aqui toda a interface da classe users, mas apenas o seu construtor e alguns métodos:
import java.io.*;
import java.util.*;
public class users{
// attributes
private Hashtable usersByLogin=new Hashtable(); // login --> login, pwd, ..., dir
private ArrayList erreurs=new ArrayList(); // list of error messages
....
// manufacturer
public users(String usersFileName, String groupsFileName, String aliasesFileName) throws Exception {
// usersFileName: name of the user file with lines of the form
// login:pwd:uid:gid:id:dir:shell
// groupsFileName: name of the group file with lines of the form
// name:pwd:number:member1,member2,...
// aliasesFileName: name of alias file with lines of the form
// alias:[tab]login
// builds the usersByLogin dictionary
....
}// manufacturer
// user list
public Hashtable getUsersByLogin(){
return usersByLogin;
}
// errors
public ArrayList getErreurs(){
return erreurs;
}
Um dicionário (Hashtable) cujas chaves são os logins do ficheiro passwd. O valor associado a cada chave é uma matriz de cadeias de caracteres (String[7]) cujos elementos são os 7 campos da linha do ficheiro passwd associada ao login. Alguns campos podem estar vazios se a linha tiver menos de 7 campos. | |
lista de mensagens de erro - vazia se não houver erros |
7.2. A aplicação web
Propomos a criação da seguinte aplicação web (página de formulário):
![]() |
N.º | nome | Tipo de HTML | função |
1 | cmbLogins | <select ...>...</select> | exibe uma lista de todos os logins para os quais é possível solicitar informações |
2 | btnSearch | <input type="submit" ...> | para iniciar a pesquisa |
Quando o utilizador clica no botão [Pesquisar] (2), o login de (1) é consultado a partir de um objeto U do tipo utilizadores. Se o login existir, é devolvida a seguinte resposta (página de informação):
![]() |
Como mostra o URL do navegador acima, os parâmetros do formulário são enviados ao servidor através de uma solicitação GET. Podemos, portanto, fornecer diretamente ao navegador este URL contendo os parâmetros. É isso que estamos a fazer aqui, para introduzir um login que não existe. Obtemos a seguinte resposta (página de erro):
![]() |
7.3. A arquitetura da aplicação
![]() |
Esta arquitetura inclui os seguintes componentes:
- as visualizações:
- logins.jsp, utilizada para apresentar a lista de inícios de sessão (visualização 1)
- infos.jsp, utilizada para apresentar informações relacionadas com um login (vista 2)
- erreurs.jsp, utilizada para apresentar uma lista de erros (vista 3)
- Formulários do tipo ActionForm utilizados pelas ações:
- formLogins, utilizado para recolher dados do formulário logins.jsp
- as ações:
- SetupLoginAction, que prepara o conteúdo de form.jsp e, em seguida, apresenta esta vista
- InfosLoginAction, que processa o conteúdo de logins.jsp depois de este ter sido enviado para o servidor
- ForwardAction, que trata do link [Voltar ao formulário] nas vistas infos.jsp e erreurs.jsp
- a classe de negócios users utilizada pelas ações para recuperar os seus dados
- o modelo fornecido pelos três ficheiros simples passwd, group e aliases
7.4. Os ficheiros de configuração da aplicação web
7.4.1. O ficheiro server.xml
O contexto da aplicação será denominado /strutsquiest2. Por conseguinte, adicionaremos a seguinte linha ao ficheiro server.xml do Tomcat:
Depois de fazer isto, poderá ser necessário reiniciar o Tomcat para que reconheça o novo contexto. Podemos verificar se está válido acedendo ao URL http://localhost:8080/strutsquiest2.
7.4.2. O ficheiro web.xml
O ficheiro de configuração web.xml da aplicação será o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>strutsquiest2</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
Este ficheiro web.xml introduz uma nova funcionalidade. O controlador Struts já não é org.apache.struts.action.ActionServlet, mas sim uma classe derivada a que chamámos aqui istia.st.struts.quiest.Quiest2ActionServlet. Isto permitir-nos-á recuperar os dois parâmetros de inicialização: passwdFileName (localização do ficheiro de senhas) e groupFileName (localização do ficheiro de grupos). O ficheiro de aliases não é necessário nesta aplicação.
7.4.3. O ficheiro struts-config.xml
O ficheiro struts-config.xml 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="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="istia.st.struts.quiest.ApplicationResources"
null="false"
/>
</struts-config>
Contém três secções principais:
- a declaração de formulários na secção <form-beans>
- a declaração de ações na secção <action-mappings>
- a declaração do ficheiro de recursos em <message-resources>
7.4.4. Os objetos de formulário da aplicação (beans)
<form-beans>
<form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
Existe apenas um form bean na nossa aplicação, denominado formLogins e de um tipo derivado de DynaActionForm. Será utilizado nas seguintes situações:
- para conter os dados necessários para exibir a vista n.º 1
- para recuperar os valores do formulário na Vista #1 quando o utilizador o enviar
A estrutura do bean formLogins está associada ao formulário na vista n.º 1. Vamos analisá-la:
![]() |
N.º | nome | Tipo de HTML | função |
1 | cmbLogins | <select ...>...</select> | exibe uma lista de todos os logins para os quais é possível solicitar informações |
2 | btnSearch | <input type="submit" ...> | para iniciar a pesquisa |
Vamos distinguir vários casos:
- Do cliente para o servidor, o objeto formLogins é utilizado para armazenar os valores do formulário HTML acima, que serão enviados através do botão [Submit]. Por isso, requer um campo cmbLogins para receber o valor do campo cmbLogins do HTML, ou seja, o login escolhido pelo utilizador.
- Do servidor para o cliente, o objeto formLogins é utilizado para fornecer o conteúdo inicial da vista n.º 1. O seu campo tLogins servirá como conteúdo para a lista n.º 1. O seu campo cmbLogins será utilizado para definir o item da lista n.º 1 a ser selecionado.
7.4.5. Ações da aplicação
As ações são tratadas por objetos do tipo Action ou tipos derivados. As ações são configuradas dentro das tags <action-mappings>:
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
A ação /init
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
Vamos descrever como funciona a ação /init:
- A ação /init ocorre normalmente apenas uma vez durante o primeiro ciclo de pedido-resposta, quando o utilizador solicita o URL http://localhost:8080/strutsquiest2/init.do
- O objeto formsLogins é criado ou reciclado. É recuperado (reciclagem) ou colocado (criação) na sessão, conforme especificado pelo atributo scope.
- O seu método reset é chamado. Note-se que este método não faz nada por predefinição na classe ActionForm e nas suas derivadas. É chamado imediatamente antes de os dados da solicitação do cliente serem copiados para o objeto ActionForm e serve para limpar o objeto antes dessa cópia. O que é a solicitação do cliente neste caso? A ação /init é acionada quando a URL solicitada é http://localhost:8080/strutsquiest2/init.do. Esta URL pode ser solicitada via GET ou POST. Basta incluir nesta solicitação parâmetros com os nomes dos campos do formLogins para que estes sejam inicializados, conforme mostrado no exemplo a seguir:

- A solicitação contém o parâmetro cmbLogins (afterpak). O controlador Struts, portanto, copiou o valor desse parâmetro para o campo cmbLogins de formLogins. A ação SetupLoginsAction foi então executada e concluiu-se com a exibição da vista logins.jsp. Esta vista possui um formulário no qual determinados campos recebem os seus valores de formLogins. Assim, o campo de seleção HTML denominado cmbLogins recebeu o seu valor do campo cmbLogins (=afterpak) em formLogins. É por isso que a lista de logins aparece posicionada no login afterpak.
- Também poderíamos tentar passar um parâmetro tLogins da seguinte forma:
Isto inicializaria o campo tLogins de formLogins com uma matriz {"login1","login2"}. No entanto, como veremos mais adiante, a ação SetupLoginsAction atribui um valor ao campo tLogins e substitui a matriz assim criada por uma nova matriz. É esta última matriz que aparece na vista logins.jsp.
- A discussão anterior, embora um pouco complexa, demonstra que não podemos assumir que a ação /init será acionada sem parâmetros do cliente. Pode, portanto, ser útil usar o método reset para limpar formLogins. Neste caso, precisaríamos de estender a classe DynaActionForm. Não o fizemos aqui.
- Assim que o método reset de formLogins é chamado, o controlador copia os dados da solicitação do cliente para os campos com o mesmo nome em formLogins. Normalmente, a ação /init é chamada sem parâmetros do cliente, mas mostrámos anteriormente que nada impede o cliente de invocar a ação /init com parâmetros arbitrários. No final desta fase, os campos cmbLogins e tLogins podem, portanto, muito bem ter um valor. Vimos que o campo cmbLogins manteria este valor, mas não o campo tLogins.
- O controlador verifica então o atributo validate da ação. Aqui, este tem o valor “false”. O método validate de formLogins não será chamado. Por isso, não o escreveremos.
- O objeto SetupLoginsAction é criado ou reciclado, caso já existisse, e o seu método execute é chamado. O seu único objetivo é atribuir um valor ao campo tLogins de formLogins. Este valor é a matriz de logins, que será solicitada à classe de negócios users. Esta operação pode falhar. É por isso que a ação /init pode ser seguida por duas visualizações:
- a vista errors.jsp, se a classe de utilizadores não conseguiu fornecer a matriz de logins
- a vista logins.jsp, caso contrário
- o controlador exibirá uma destas duas vistas
- o ciclo de pedido-resposta para a ação /init está concluído.
A ação /infosLogin
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
Vamos descrever como funciona a ação /infosLogin:
- A ação /infosLogin é normalmente acionada quando o utilizador clica no botão [Pesquisar] na vista logins.jsp. Em seguida, é enviada uma solicitação ao servidor, conforme especificado pela tag HTML <form> na vista:
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
- Podemos ver que a solicitação é enviada ao servidor utilizando o método GET. O utilizador pode, portanto, digitá-la manualmente:

- O objeto formsLogins é criado ou reciclado. É recuperado (reciclagem) ou colocado (criação) na sessão, conforme especificado pelo atributo scope.
- O seu método reset é chamado imediatamente antes de os dados da solicitação do cliente serem copiados para o objeto ActionForm. Normalmente, tem o formato http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=xx, onde xx é um login escolhido da lista de logins. Mas também pode ser qualquer coisa se o utilizador tiver usado a URL anterior passando parâmetros arbitrários. Considere a seguinte sequência de páginas:

- A ação /infosLogin foi chamada com a cadeia de parâmetros cmbLogins=xx&tLogins=login1&tLogins=login2. Os campos cmbLogins e tLogins de formLogins receberão, portanto, os valores "xx" e {"login1","login2"}, respetivamente. A ação /infosLogin irá solicitar as informações associadas ao login "xx" à classe de negócios do utilizador. A classe do utilizador irá responder que este login não existe. Daí a vista apresentada acima. Agora, vamos utilizar o link [Voltar ao formulário] acima:

- A ação /retourLogins é acionada pelo link [Voltar ao formulário]. Esta ação simplesmente exibe a vista logins.jsp sem qualquer ação intermediária. Recorde-se que o campo tLogins é utilizado para preencher a lista de logins na vista logins.jsp. Uma vez que o utilizador alterou este valor para {"login1","login2"}, estes dois logins aparecem agora na lista. Mais uma vez, nunca é demais salientar a necessidade absoluta de ter em conta parâmetros arbitrários definidos por um utilizador ou por um programa no funcionamento de uma aplicação. A solução para o problema aqui apresentado seria que o link [Voltar ao formulário] apontasse para a ação /init. Isto garantiria que a lista correta de logins fosse devolvida.
- Voltemos a uma solicitação normal para a ação /infosLogin, como:
http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=afterpak
- O controlador Struts atribuirá um valor ao campo cmbLogins do objeto ActionForm. Ao campo tLogins, no entanto, não será atribuído um valor (não existe um campo correspondente na solicitação enviada). Este comportamento funciona para nós. Portanto, não precisaremos escrever um método de reinicialização personalizado para formLogins.
- Assim que o método de reinicialização de formLogins for chamado, o controlador copia os dados da solicitação do cliente para os campos com o mesmo nome em formLogins. O campo cmbLogins receberá um valor: o login escolhido pelo utilizador (afterpak).
- O controlador verifica então o atributo validate da ação. Aqui, este tem o valor “false”. O método validate de formLogins não será chamado.
- O objeto InfosLoginAction é criado ou reutilizado, caso já existisse, e o seu método execute é chamado. A sua função é recuperar as informações associadas ao login cmbLogins. Estas informações serão solicitadas à classe de negócio do utilizador. Esta operação pode falhar (por exemplo, o login não existe). É por isso que a ação /infosLogin pode ser seguida por duas vistas:
- a vista errors.jsp, caso a classe do utilizador não tenha conseguido fornecer as informações solicitadas
- a vista infos.jsp, caso contrário
- o controlador exibirá uma destas duas vistas
- o ciclo de solicitação-resposta para a ação /infosLogin está concluído.
A ação /retourLogins
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- A ação /retourLogins é acionada ao clicar no link [Voltar ao formulário] nas páginas erreurs.jsp e infos.jsp.
- Aqui, não existe nenhum formulário associado à ação. Por isso, passamos imediatamente à execução do método execute de um objeto ForwardAction, que irá devolver um objeto ActionForward a apontar para a vista /vues/logins.jsp.
7.4.6. O ficheiro de mensagens da aplicação
A terceira secção do ficheiro struts-config.xml é o ficheiro de mensagens:
O ficheiro ApplicationResources.properties encontra-se em WEB-INF/classes/istia/st/struts/quiest. O seu conteúdo é o seguinte:
errors.header=<ul>
errors.footer=</ul>
parametreManquant=<li>Le paramètre [{0}] n'a pas été initialisé</li>
usersException=<li>Erreur d'initialisation de l'application : {0}</li>
loginInconnu=<li>Le login [{0}] n'existe pas</li>
7.5. Ver código
Os leitores são encorajados a rever a lição sobre o tratamento de formulários caso não compreendam o código da vista apresentado abaixo.
7.5.1. A visualização logins.jsp
Lembre-se de que esta vista é apresentada em dois casos:
- quando a ação /init é chamada durante o primeiro ciclo de pedido-resposta
- quando a ação /retourLogins é chamada durante os ciclos subsequentes
O código da vista logins.jsp é o seguinte:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Quiest - formulaire</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<center>
<h2>Application QuiEst</h2>
<hr>
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
<table>
<tr>
<td>Login cherché</td>
<td>
<html:select name="formLogins" property="cmbLogins">
<html:options name="formLogins" property="tLogins"/>
</html:select>
</td>
<td>
<html:submit value="Chercher"/>
</td>
</tr>
</table>
</html:form>
</center>
</body>
</html>
7.5.2. A vista infos.jsp
Esta vista é apresentada quando a ação /infosLogin é chamada com sucesso. O seu código é o seguinte:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Esta vista utiliza um objeto chamado infosLoginBean, que é inserido na solicitação pela ação /infosLogin. Este objeto possui dois campos:
String titre; // titre à afficher dans la vue
String[] infosLogin; // tableau des informations à afficher dans la vue
Discutiremos esta classe com mais detalhe quando abordarmos o código da classe InfosLoginAction.
7.5.3. A vista errors.jsp
Esta vista é apresentada quando as ações /init ou /infosLogin resultam num erro. O seu código é o seguinte:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Application QuiEst - erreurs</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2 align="center">Application QuiEst - Erreurs</h2>
<hr>
<h2>Les erreurs suivantes se sont produites</h2>
<html:errors/>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
7.6. Classes Java
O ficheiro web.xml faz referência a uma classe Java:
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
....
</servlet>
...
</web-app>
O ficheiro de configuração struts-config.xml faz referência a duas classes Java:
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
7.6.1. A classe Quiest2ActionServlet
A classe Quiest2ActionServlet deriva da classe ActionServlet, a classe controladora do Struts. Derivamos da classe ActionServlet para personalizar o seu método init. Este método, executado apenas uma vez durante o carregamento inicial do servlet, permite-nos construir um objeto de negócio do tipo users. Este objeto só precisa de ser construído uma vez, e o método init é um bom local para realizar essa construção. O objeto users requer dois ficheiros para ser construído: os ficheiros passwd e group. As localizações destes dois ficheiros são passadas como parâmetros para o servlet no ficheiro web.xml da aplicação:
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
O código do servlet é o seguinte:
package istia.st.struts.quiest;
import java.util.*;
import javax.servlet.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class Quiest2ActionServlet
extends ActionServlet {
// servlet attributes
private users u = null;
private ActionErrors erreurs = new ActionErrors();
private String[] tLogins;
//init
public void init() throws ServletException {
// don't forget to initialize the parent class
super.init();
// local variables
final String[] initParams = {"passwdFileName", "groupFileName"};
Properties params = new Properties();
// retrieve servlet initialization parameters
ServletConfig config = getServletConfig();
String servletPath = config.getServletContext().getRealPath("/");
for (int i = 0; i < initParams.length; i++) {
String valeur = config.getInitParameter(initParams[i]);
if (valeur == null) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("parametreManquant", initParams[i]));
valeur = "";
}
// the parameter
params.setProperty(initParams[i], valeur);
} //for
// return if there were initialization errors
if (erreurs.size() != 0) {
return;
}
// create a users object
try {
u = new users(servletPath + "/" + params.getProperty("passwdFileName"),
servletPath + "/" + params.getProperty("groupFileName"), null);
}
catch (Exception ex) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("usersException", ex.getMessage()));
return;
} //catch
// retrieve the list of logins
tLogins = new String[u.getUsersByLogin().size()];
Enumeration eLogins = u.getUsersByLogin().keys();
for (int i = 0; i < tLogins.length; i++) {
tLogins[i] = (String) eLogins.nextElement();
}
// sort logins
Arrays.sort(tLogins);
} //init
// servlet private info access method
public Object[] getInfos() {
return new Object[] {erreurs, u, tLogins};
}
}
Em resumo, o método init funciona da seguinte forma:
- Primeiro, o método init da classe pai (ActionServlet) é chamado para que seja inicializado corretamente
- depois, os parâmetros de inicialização são lidos. Se algum estiver em falta, o atributo privado ActionErrors errors é preenchido.
- Se os parâmetros de inicialização estiverem presentes, é construído um objeto users. Esta construção pode lançar uma exceção. Neste caso, o atributo errors de ActionErrors é preenchido.
- Se a construção for bem-sucedida, a lista de todos os logins é recuperada do objeto criado e ordenada numa matriz, que é armazenada no atributo privado `String[] tLogins`.
- O objeto users construído é armazenado no atributo privado users u.
- O método público getInfos recupera os três atributos privados (u, errors, tLogins) para uma matriz de objetos.
7.6.2. A classe SetupLoginsAction
O objetivo desta ação é inicializar o objeto DynaActionForm formLogins. Este objeto, colocado na sessão, não precisará de ser reinicializado posteriormente. A SetupLoginsAction, portanto, é executada apenas uma vez. O seu código é o seguinte:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class SetupLoginsAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// prepares the form to be displayed
// retrieve info from the controller servlet
// infos=(ActionErrors errors, users u, String[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// were there any initialization errors?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// put the logins in the form
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
}
Tal como acontece com todas as ações do Struts, o código encontra-se no método execute. Este método:
- recupera do controlador Struts as informações que este armazenou através do seu método init. Isto é feito pelo método getServlet() da classe Action.
- Entre estas informações encontra-se o atributo ActionErrors do controlador. Se esta lista de erros não estiver vazia, é colocada no pedido e a vista errors.jsp é apresentada.
- Se a lista de erros estiver vazia, a lista de logins inicialmente criada pelo controlador é atribuída ao campo tLogins do bean formLogins. A vista logins.jsp é então solicitada, o que exibirá a lista de logins.
7.6.3. As classes InfosLoginBean e InfosLoginAction
O objetivo da ação InfosLoginAction é recuperar as informações associadas ao login escolhido pelo utilizador e apresentá-las ao utilizador. As informações serão recolhidas num objeto do tipo InfosLoginBean:
package istia.st.struts.quiest;
public class InfosLoginBean implements java.io.Serializable{
// bean holding the info needed for the info page
private String titre;
private String[] infosLogin;
// manufacturer
public InfosLoginBean(String titre, String[] infosLogin){
this.titre=titre;
this.infosLogin=infosLogin;
}
// getters
public String getTitre(){
return this.titre;
}
public String[] getInfosLogin(){
return this.infosLogin;
}
public String getInfosLogin(int i){
return this.infosLogin[i];
}
}
A classe anterior é um bean, ou seja, uma classe Java em que um atributo privado T unAttribut é automaticamente associado a dois métodos privados:
- void setUnAttribut(T value) { unAttribut = value; }
- T getUnAttribut(){ return unAttribut;}
Repare na sintaxe especial dos métodos get e set. Se o atributo for uma matriz T[] unAttribut, podemos criar métodos get e set para os elementos da matriz:
- void setUnAttribut(T value, int i) { unAttribut[i] = value; }
- T getUnAttribut(int i) { return unAttribut[i]; }
Para compreender melhor isto, vamos analisar o código da vista infos.jsp, que deve ser enviada após a ação InfosLoginAction:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Vamos considerar a seguinte tag:
Ela instrui o sistema a escrever o valor do campo título (propriedade) do objeto infosLoginBean (nome) incluído na solicitação (escopo). O valor a ser escrito será obtido através de request.getAttribute("infosLoginBean").getTitre(). Portanto, o método getTitre deve existir na classe InfosLoginBean. É esse o caso. A tag
instrui o sistema a gravar o valor do elemento infosLogin[0] do objeto infosLoginBean incluído na solicitação. O valor a ser gravado será obtido através de request.getAttribute("infosLoginBean").getInfosLogin(0). Por conseguinte, o método getInfosLogin(int i) deve existir na classe InfosLoginBean. E existe.
O objetivo da classe InfosLoginAction é construir o referido objeto InfosLoginBean a partir de um login escolhido pelo utilizador. O seu código é o seguinte:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class InfosLoginAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// should display login information
// retrieve info from the controller servlet
// infos=(ActionErrors errors, users u, LoginBean[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// were there any initialization errors?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// first retrieve this login
String login = (String) ( (DynaActionForm) form).get("cmbLogins");
// do we have anything?
if (login == null) {
// not normal - the login form is returned
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
// we have a login - we're looking for it
String[] infosLogin = (String[]) ( (users) infos[1]).getUsersByLogin().get(login);
// have we found?
if (infosLogin == null) {
// login not found - error page displayed
ActionErrors erreurs2=new ActionErrors();
erreurs2.add(ActionErrors.GLOBAL_ERROR, new ActionError("loginInconnu", login));
this.saveErrors(request, erreurs2);
return mapping.findForward("afficherErreurs");
}
// the login has been found - we put the information found in the query
String titre="Application QuiEst - login["+login+"]";
InfosLoginBean infosLoginBean= new InfosLoginBean(titre,infosLogin);
request.setAttribute("infosLoginBean",infosLoginBean);
return mapping.findForward("afficherInfos");
}
}
O método execute funciona da seguinte forma:
- Recupera as informações recolhidas pelo controlador Struts durante a inicialização. Se o controlador detetou algum erro, a execução pára nesse ponto e solicita ao utilizador que visualize esses erros.
- Verificamos se existe um login. Se o utilizador preencheu o formulário de seleção de login, o login está presente. No entanto, o utilizador pode muito bem digitar a URL da ação diretamente no navegador sem passar quaisquer parâmetros. Se não houver nenhum login, voltamos a exibir a lista de logins.
- Se houver um login, recuperamos as informações associadas à classe de negócios do utilizador. Se a classe não conseguir encontrar o login procurado, é apresentada a página de erro. Caso contrário, é criado um objeto InfosLoginBean para armazenar as informações necessárias à vista infos.jsp. Este objeto é colocado no pedido e a página infos.jsp é apresentada.
7.7. Implantação
A estrutura do diretório da aplicação é a seguinte:
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() |
7.8. Conclusão
Utilizámos o Struts numa aplicação realista, recorrendo a uma classe de negócio. Demonstrámos também que é necessário prestar especial atenção ao pedido enviado por um cliente e que não se devem fazer suposições sobre a sua natureza. Um pedido pode ser de qualquer tipo e todas as aplicações devem começar por verificar a sua validade.











