Você está na página 1de 24

J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.

br

Tutorial - Struts Framework


Welington B. Souza wbsouza@yahoo.com.br

Introdução

O objetivo deste tutorial é dar uma visão geral sobre o funcionamento da Struts Framework. Aqui você irá
aprender o necessário para começar a desenvolver uma aplicação web usando a Struts. Embora esta framework
implemente o padrão MVC, isso não quer dizer que vamos seguir a risca as explicações de cada camada, mas sim
expondo o conteúdo teórico dependendo da necessidade do tutorial, visando a facilidade no entendimento dos
conceitos à medida em que eles forem necessários.

A Struts Framework é um projeto open source mantido pela Apache Software Foundation. É uma implementação
do design pattern MVC (Model-View-Controller) para aplicações java com internet. O objetivo do pattern MVC é
separar de maneira clara a camada de apresentação (View) da camada de Negócio (Model).

A arquitetura MVC - Model-View-Controller (Modelo-Visualização-Controle) é um padrão que separa de maneira


independente o Modelo, que representa os objetos de negócio (Model) da camada de apresentação, que
representa a interface com o usuário ou outro sistema (View); e o Controle de fluxo da aplicação (Controller).

Figura 1 - O Padrão MVC

A Struts Foi escrita por Craig McClanahan em Maio de 2000, e desde então vem sendo melhorado pela comunidade
open-source. Foi desenvolvida com o objetivo de fornecer uma framework para facilitar o desenvolvimento de
aplicações para web.

Motivos utilizar a Struts Framework


Se tornou um padrão de mercado;
Garantia de que alguém (Apache Group) irá manter a framework (correção de bugs e novos releases);
Integração com a maioria das IDEs de mercado
Não reinventar a roda, focando os seus esforços em regras de negócio;
Separar a camada de negócio da camada de apresentação;
Já incorpora diversos design patterns
Criação de aplicações padronizadas, facilitando a manutenção;
Criação de Aplicações Internacionalizadas;
Possibilidade de gerar a saída de acordo com o dispositivo usado (HTML, SHTML, WML, etc);
Aumentar a produtividade

Licença

A Struts está disponível sobre a licença "free-to-use-license" da Apache Software Foundation (veja
http://www.apache.org/LICENSE-1.1).

Detalhes do funcionamento

1 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

Figura 2 - Fluxo de Navegação nos componentes da Struts

1. O usuário faz uma solicitação através de uma url no browser. Ex: http://localhost:8080/cadastro/listUsers.do. Note que no final da
url tem um .do que será usado para invocar (na verdade mapear) o servlet controller da struts.
2. Se for a primeira solicitação que o container recebeu para esta aplicação, ele irá invocar o metodo init() da ActionServlet (controller
da Struts) e irá carregar as configurações do arquivo struts-config.xml em estruturas de dados na memória. Vale lembrar que esta
passagem só será executada uma única vez, pois nas solicitações subsequentes, a servlet consulta estas estruturas na memória para
decidir o fluxo a ser seguido.
3. Baseado no fluxo definido no arquivo struts-config.xml, e que neste momento já se encntra carregado em estruturas na memória, o
ActionSerlet identificará qual o ActionForm (classe para a validação dos dados) irá invocar. A classe ActionForm através do método
validate irá verificar a integridade dos dados que foram recebidos na solicitação que vem do browser.
4. O controle da aplicação é retomado pelo ActionServlet, que verifica o resultado da verificação do ActionForm.
Se faltou alguma coisa (campo não preenchido, valor inválido, etc), o usuário recebe um formulário html (geralmente o
mesmo que fez a solicitação), informando o motivo do não atendimento da solicitação, para que o usuário possa preencher
corretamente os dados para fazer uma nova solicitação.
Se não faltou nenhuma informação, ou seja, todos os dados foram enviados corretamente, o controller passa para o
proximo passo (Action).
5. O ActionServlet, baseado no fluxo da aplicação (estruturas já carregadas em memória) invoca uma classe Action. A classe Action
passará pelo método execute que irá delegar a requisição para a camada de negócio.
6. A camada de negócio irá executar algum processo (geralmente popular um bean, ou uma coleção). O resultado da execução deste
processo (objetos já populados) será usado na camada de apresentação para exibir os dados.
7. Quando o controle do fluxo da aplicação votar ao Action que invocou o processo da camada de negócio, será analisado o
resultado, e definido qual o mapa adotado para o fluxo da aplicação. Neste ponto, os objetos que foram populados na camada de
negócio serão "atachados" como atributos na seção do usuário.
8. Baseado no mapeamento feito pelo o Action, o Controller faz um forward para o JSP para apresentar os dados.
9. Na camada de apresentação (View), os objetos que foram setados como atributos da sessão do usuário serão consultados para
montar o html para o browser.
10. Chega o html da resposta requisitada pelo usuário.

O Controller já vem implementado na Struts, embora, caso seja possível estendê-lo a fim de adicionar
funcionalidade. O fluxo da aplicação é programado em um arquivo XML através das ações que serão executadas. As
ações são classes base implementadas pela framework seguindo o padrão MVC. Assim devemos estendê-las a fim
de adicionar a funcionalidade desejada.

A geração da interface é feita através de custom tags, também já implementadas pela Struts, evitando assim o uso
de Scriptlets (códigos java entre <%> e <%>), deixando o código JSP mais limpo e fácil de manter.

Baixando e instalando a documentação

A Struts Framework pode ser baixada em http://jakarta.apache.org/struts. Neste tutorial vamos usar a versão 1.1,
pois incorpora uma série de melhorias em relação a versão anterior, e já temos uma versão estável para
produção.Após o download, descompactar o arquivo; dentro do diretório descompactado, possui um diretório
chamado webapps contendo as aplicações exemplo. Se você tiver usando o TomCat ou outro WebContainer,
copie o arquivo struts-documentation.war, para o diretório <webapps> do seu container.

Neste ponto a documentação da Struts já se enconrtra instalada na sua máquina.Para acessá-la vá para
http://localhost:8080/struts-documentation.

Projeto do Tutorial

Para entender melhor o funcionamento, vamos fazer uma aplicação prática. Desenvolveremos um cadastro de

2 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

Usuários com inclusão, alteração e exclusão. Portanto, um nome sugestivo para a nossa aplicação web é "cadastro"

Para criar uma nova aplicação com a Struts Framework, devemos seguir os seguintes passos:

Vá para a linha de comando do seu sistema operacional;


Vá para o diretório onde a Struts framework foi descompactada, e entre no diretório webapps;
Faça a seguinte sequencia de comandos:

mkdir cadastro
cd cadastro
jar -xvf ../struts-blank.war

Agora temos uma aplicação web (em branco) baseada na template da Struts para começar a brincar.
Mova este diretório para o local que mais lhe convier, uma vez que os fontes da sua aplicação ficarão abaixo desta estrutura.

Estrutura de diretórios da aplicação web com Struts:


RootDir
|
+-- META-INF
| Contém meta informação. Usado pelo Web Container, utilitários, etc.
|
+-- WEB-INF
|
+-- classes
| | Este diretório contém as classes java da sua aplicação (camada de negócio).
| |
| +-- java
| |
| +-- resources
| |
| +-- application.properties
| Contém as mensagens (textos fixos) da aplicação,
| inclusive as mensagens de erros.
| Este arquivo é responsável pela internacionalização da aplicação.
+--- lib
| |
| +-- struts.jar
| Contém as classes da Struts (Controller, Helper class,
| Biblioteca de Tags, etc)
| Também no diretório lib contém outras bibliotecas da aplicação web.
|
+--*.tld
| Contém os XML descriptors da biblioteca de tags da Struts.
|
+-- struts-config.xml
| Arquivo de configuração da Struts.
| Mais detalhes sobre este arquivo serão vistos adiante.
|
+-- web.xml
Arquivo de configuração da aplicação web, relativo ao Web Container.
Mais detalhes sobre este arquivo serão vistos adiante.

O arquivo web.xml

O arquivo web.xml deverá ficar algo parecido com:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!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>

<!-- Standard Action Servlet Configuration (with debugging) -->


<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>

3 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

<!-- Standard Action Servlet Mapping -->


<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<!-- The Usual Welcome File List -->


<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<!-- Struts Tag Library Descriptors -->


<taglib>
<taglib-uri>/tags/struts-bean</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-html</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-logic</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-nested</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/tags/struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>

</web-app>

Temos no arquivo web.xml, três importantes seções:

A definição da Servlet que representa o Controller (ActionServlet)


O URL mapping da servlet, para informar como chamar esta servlet
As definições da biblioteca de tags da Struts

Podemos ver pelo servlet-mapping que a nossa servlet será invocada por
http://localhost:8080/cadastro/<algumaCoisa>.do, substituindo-se <algumaCoisa> pela ação desejada.

Banco de dados

Por questões de facilidade de obtenção e configuração e Usaremos o MySQL como banco de dados. O MySql pode
ser baixado em http://www.mysql.com

Script de criação do banco de dados:

CREATE DATABASE strutsdemo;

USE strutsdemo;

CREATE TABLE usuario(


id_usuario INTEGER NOT NULL,
nome VARCHAR(50) NULL,
login VARCHAR(20) NULL,
senha VARCHAR(20) NULL,
sexo CHAR NULL,
ativo BOOL NULL,
faixa_idade INTEGER,
PRIMARY KEY(id_usuario)
);

INSERT INTO usuario (id_usuario, nome, login, senha, sexo, ativo, faixa_idade)
VALUES (1, 'Marcelo Ferreira Dantas', 'marcelo', 'marcelo', 'M', 1, 1);

4 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

INSERT INTO usuario (id_usuario, nome, login, senha, sexo, ativo, faixa_idade)
VALUES (2, 'Gerson Hernandes Vilela', 'gerson', 'gerson', 'M', 1, 2);

INSERT INTO usuario (id_usuario, nome, login, senha, sexo, ativo, faixa_idade)
VALUES (3, 'Manuela Rodrigues', 'Manuela', 'manu', 'F', 1, 2);

Para acessar os dados no MySQL será necessário adicionar o driver JDBC no lib da nossa aplicação. O driver pode ser
baixado em http://www.mysql.com/downloads/api-jdbc-stable.html. Após baixá-lo, descompactar e copiar o
arquivo mysql-connector-java-3.0.8-stable-bin.jar para diretório lib do seu container (no tomcat é
"$CATALINA_HOME/common/lib" já no JBoss vai diretório "$JBOSS_HOME/server/default/deploy").

Definindo as configurações do Connection Pool

A maioria das aplicações para web que usam banco de dados podem ser beneficiadas por um Connection Pool
(coleção de conexões que ficam permanentemente abertas com o SGBD).

Estabelecer uma conexão com o banco de dados a cada solicitação que chega no web server para
consultar/manipular dados é um processo muito dispendioso (conexão e verificação de permissões do usuário do
banco).

Uma aplicação não usa banco de dados o tempo todo, somente em algums momentos para obter os dados, depois
esta conexão não é mais necessária.

Nesse sentido, um Connection Pool pode ajudar bastante, eliminando um overread desnecessário de se reconectar
a cada solicitação que chega, pois ele fará o gerenciamento da coleção de conexões prontas para serem usadas, e
"marcará" as conexões em uso. Se por acaso você pegar uma conexão e ficar um determinado tempo sem fazer
nada (timeout), o Connection Pool regata esta conexão para uso, e invalida o objeto de conexão que você pegou
anteriormente. Tamanha é a importância deste tópico, que acabou entrando na especificação JDBC
(javax.sql.DataSource). Praticamente todos os Containers possuem um mecanismo que fornecem um Connection
Pool.

Embora o nosso projeto seja baseado no MySQL, que provavelmente muitos dirão que não há necessidade de se
fazer isto com o MySQL, pois o processo de conexão é extremamente rápido, não podemos dizer o mesmo de
outros bancos. Mesmo assim, é interessante usar um Connection Pool, pois pode parecer imperceptível para uma
só solicitação chegando, mas em um ambiente de produção, com a sua aplicação sendo macivamente solicitada
para manipular e consultar dados, daí sim haverá uma grande diferença.

A Struts Framework implementa um Connection Pool usando DataSource. Na versão 1.0, a Conexão era obtida do
ActionServlet. No entanto, nas nos betas e release candidate da versão 1.1 o metodo para obtenção de conexões
ficou deprecated, e passaram esta tarefa para o Action. Por fim no release 1.1 de produção, as classes que tratam
do DataSource foram parar no arquivo struts-legacy.jar, indicando claramente a intenção de descontinuar esta
funcionalidade em favor de uma solução padrão (JCA), ou seja, implementada pelo container; apesar de ainda
continuar funcionando. Assim, vamos usar uma implementação fornecida pelo container.

Para definir as configurações de um DataSource no TomCat, siga os passos abaixo:

Altere o arquivo server.xml adicionando o trecho abaixo:

<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true">


...
<DefaultContext>
<Resource name="jdbc/StrutsDemoDS" auth="Container" type="javax.sql.DataSource" scope="Shareable"/>
<ResourceParams name="jdbc/StrutsDemoDS">
<parameter><name>factory</name><value>org.apache.commons.dbcp.BasicDataSourceFactory</value></parameter>
<parameter><name>driverClassName</name><value>com.mysql.jdbc.Driver</value></parameter>
<parameter><name>url</name><value>jdbc:mysql://localhost/strutsdemo</value></parameter>
<parameter><name>username</name><value>root</value></parameter>
<parameter><name>password</name><value>root</value></parameter>
<parameter><name>maxActive</name><value>20</value></parameter>
<parameter><name>maxIdle</name><value>10</value></parameter>
<parameter><name>maxWait</name><value>100</value></parameter>
</ResourceParams>
</DefaultContext>
...
</Host>

No código Java, quando precisar de uma conexão de banco de dados, você irá solicitar uma conexão ao
Connection Pool, o qual irá marcar uma conexão no Pool, como sendo usada, e lhe dará algum tempo para fazer
alguma coisa. Tão logo que você use a conexão para consultar ou manipular dados no banco de dados, será
necessário devolver a conexão para o pool (se não fizer isto o Connection Pool fecha a conexão para você e a
resgata para o pool). Para fazer isto, basta executar o método close(), que na verdade não a fecha, mas devolve
para o Pool (dependendo da implementação do Connection Pool, poderá ter variações da maneira de devolver ao

5 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

pool, algo do tipo invocar um metodo do tipo releaseConnection, do objeto Connection Pool).

Definindo a Camada de Negócio

Para listar os usuários cadastrados precisaremos definir as classes que farão parte da nossa camada de Negócio.
Devemos ter duas classes. Uma classe que representa o registro do usuário "UserData" (value object) , e outra que
irá gerenciar o cadastro dos usuários "AdminUsers". Segue abaixo o código das duas classes:

package strutsdemo.bean;

public class UserData {

private int idUsuario;


private String nome;
private String login;
private String senha;
private String sexo;
private boolean ativo;
private int faixaIdade;

public void setIdUsuario(int idUsuario) {


this.idUsuario = idUsuario;
}
public void setLogin(String login) {
this.login = login;
}
public void setNome(String nome) {
this.nome = nome;
}
public void setSenha(String senha) {
this.senha = senha;
}
public void setSexo(String sexo) {
this.sexo = sexo;
}
public void setAtivo(boolean ativo) {
this.ativo = ativo;
}
public void setFaixaIdade(int faixaIdade) {
this.faixaIdade = faixaIdade;
}

public int getIdUsuario() {


return idUsuario;
}
public String getLogin() {
return login;
}
public String getNome() {
return nome;
}
public String getSenha() {
return senha;
}
public String getSexo() {
return sexo;
}
public boolean getAtivo() {
return ativo;
}
public String getDescricaoStatus() {
return this.ativo ? "Ativo": "Inativo";
}
public int getFaixaIdade() {
return faixaIdade;
}

package strutsdemo.bean;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;

import javax.naming.InitialContext;

6 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

import javax.naming.NamingException;
import javax.sql.DataSource;

public class AdminUsers {

protected static DataSource dataSource;

public AdminUsers() throws Exception {


if (dataSource == null) {
try {
InitialContext ic = new InitialContext();
// se for tomcat
dataSource = (DataSource) ic.lookup("java:comp/env/jdbc/StrutsDemoDS");
// no JBoss faça
// dataSource = (DataSource) ic.lookup("java:jdbc/StrutsDemoDS");

} catch (NamingException ex) {


System.out.println(ex.getMessage());
throw ex;
}
}
}

protected Connection getConnection() throws SQLException {


Connection conn = null;
try {
conn = dataSource.getConnection();
}
catch (SQLException e) {
throw e;
}
return conn;
}

protected void closeConnection(


Connection conn,
PreparedStatement stmt,
ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
}

public LinkedList getUserList() throws SQLException {


Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
LinkedList users = new LinkedList();
try {
conn = getConnection();
stmt = conn.prepareStatement("select * from usuario");
rs = stmt.executeQuery();
while (rs.next()) {
UserData user = new UserData();
user.setIdUsuario(rs.getInt("id_usuario"));
user.setNome(rs.getString("nome"));
user.setLogin(rs.getString("login"));
user.setSenha(rs.getString("senha"));
user.setSexo(rs.getString("sexo"));
user.setAtivo(rs.getBoolean("ativo"));
user.setFaixaIdade(rs.getInt("faixa_idade"));
users.add(user);
}

7 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

}
catch (SQLException e) {
throw e;
}
finally {
closeConnection(conn, stmt, rs);
}
return users;
}

public void insertUser(UserData user) throws SQLException {


Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(
"insert into usuario \n" +
"(id_usuario, nome, login, senha, sexo, ativo, faixa_idade) \n" +
"values (?, ?, ?, ?, ?, ?, ?)");
stmt.setInt(1, user.getIdUsuario());
stmt.setString(2, user.getNome());
stmt.setString(3, user.getLogin());
stmt.setString(4, user.getSenha());
stmt.setString(5, user.getSexo());
stmt.setBoolean(6, user.getAtivo());
stmt.setInt(7, user.getFaixaIdade());
stmt.executeUpdate();
}
catch (SQLException e) {
throw e;
}
finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
}
}

public void updateUser(UserData user) throws SQLException {


Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(
"update usuario set \n" +
"nome = ?, login = ?, senha = ?, sexo = ?, ativo = ?, faixa_idade = ? \n" +
"where id_usuario = ?");
stmt.setString(1, user.getNome());
stmt.setString(2, user.getLogin());
stmt.setString(3, user.getSenha());
stmt.setString(4, user.getSexo());
short ativo = (short) (user.getAtivo()? 1: 0);
stmt.setShort(5, ativo);
stmt.setInt(6, user.getFaixaIdade());
stmt.setInt(7, user.getIdUsuario());
stmt.executeUpdate();
}
catch (SQLException e) {
throw e;
}
finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
}
}

public void deleteUser(int idUsuario) throws SQLException {


Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {

8 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

conn = getConnection();
stmt = conn.prepareStatement(
"delete from usuario where id_usuario = ?");
stmt.setInt(1, idUsuario);
stmt.executeUpdate();
}
catch (SQLException e) {
throw e;
}
finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
}
}
}

Definindo o Controller

O Controller é responsável por receber as requisições do browser e invocar os objetos de negócio do Model para
executar algum processo retornando um resultado que servirá de base para que o Controller possa direcionar para
o JSP que deverá gerar a interface com o usuário.

O ActionServlet lê as configurações do arquivo struts-config.xml. Ao receber as solicitações do usuário, chama o


ActionBean correspondente à requisição, e de acordo com o resultado do ActionBean, executa um JSP.

Para incluir, alterar ou excluir um usuário, precisamos exibir todos os usuários cadastrados para saber o que fazer.
Assim, a nossa primeira implementação será exibir os usuários já cadastrados.

Siga os procedimentos abaixo:

Criar uma package strutsdemo, e uma classe chamada ListUsersAction dentro desta package. A classe deve estender de
org.apache.struts.action.Action (vamos entrar em mais detalhes sobre esta classe na seção "Definindo o Action Bean").
package strutsdemo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class ListUsersAction extends Action {

/* Metódo que invoca a camada de negócios.*/


public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
return null; // somente para compilar, implementação futura ...

}
}

Adicionar configuração no arquivo strutus-config.xml do Action Mapping e seus possíveis forwards de saída.
</struts-config>

<action-mappings>
<action
path="/listUsers"
type="strutsdemo.ListUsersAction">
<forward name="success" path="/listUsers.jsp" />
</action>
</action-mappings>

</struts-config>

Neste caso, quando for solicitado pelo browser /listUsers.do" o Controller chamará o ListUsersAction através do
método process, e se este objeto retornar um ActionForward com o valor "sucess", O controller encaminhará a
solicitação para o arquivo "/pages/listUsers.jsp". Se retornar "failure", será encaminhado para o arquivo
"/pages/error.jsp".

9 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

Definindo o Action Bean

Como foi visto anteriormente, o Controller irá invocar o Action que foi definido no struts-config.xml. Há uma certa
discussão sobre que parte pertence o Action e o ActionForm no padrão MVC (vamos entrar em maiores detalhes
mais adiante em ActionForm, por hora ainda não é necessário). Na realidade tanto o Action, quanto o ActionForm
são Helper Classes. A grosso modo podemos entender como classes auxiliadores para execução no padrão MVC.
Basicamente, os ActionBeans realizam as seguintes ações:

Obtem os valores necessários do ActionForm, JavaBean, request, session ou de outro local;


Chama os objetos de negócio do modelo;
Analisa o resultado da chamada da camada de negócio, e baseado nisso, retorna o ActionForward (indicando qual JSP irá apresentar os
dados) correspondente

O Código da nossa Action ficará assim:

package strutsdemo.action;

import java.sql.SQLException;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import strutsdemo.bean.AdminUsers;

public class ListUsersAction extends Action {

public ActionForward execute(ActionMapping mapping,


ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

LinkedList users = null;


ActionErrors errors = new ActionErrors();
try {
AdminUsers adminUsers = new AdminUsers();
users = adminUsers.getUserList();
HttpSession session = request.getSession();
session.setAttribute("userListBean", users);

}
catch (SQLException e) {
errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.user.list"));
getServlet().log("Erro carregando a lista de usuários", e);
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.findForward("failure"));
}
else {
return (mapping.findForward("success"));
}
}
}

Definindo a camada de apresentação

A Camada de apresentação representa o view (visualização) no padrão MVC. É em sua grande maioria baseada em
JSPs e alguns servlets envolvidos na geração da interface com o usuário ou com outros sistemas. A Struts
framework fornece suporte para construir aplicações multi-idiomas, interação com formulários e outras utilidades
através de tags personalizadas (Custom Tags).

Internacionalização

No nosso projeto (baseado no template struts-blank ), possui um arquivo de chamado application.properties no


diretório cadastro/WEB-INF/classes/java/resources. Este arquivo deve conter as chaves e os valores no formato:
chave.subchave=texto que pertence ao idioma padrão da aplicação. Vamos adicionar algumas chaves que serão

10 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

usadas em nosso projeto. Obs: Este arquivo pode ter qualquer nome, desde que termine com .properties. No
entanto existe um certo padrão, geralmente é chamado de ApplicationResources.properties. A única ressalva, é
que se você mudar o nome deste arquivo, também deverá ser mudado no arquivo struts-config.xml.

welcome.title=Bem Vindo ao Projeto Tutorial Struts


welcome.heading=Bem vindo!
welcome.message=Projeto Tutorial Struts

application.title= Struts Demo - Cadastro


index.header=Bem vindo ao Demo da Strutus Framework - Cadastro
users.title=Cadastro de Usuários
editUser.title=Alteração de Usuário
insertUser.title=Inclusão de Usuário
prompt.idUsuario=Código
prompt.nome=Nome
prompt.login=Login
prompt.senha=Senha
prompt.confirmacaoSenha=Confirmação da Senha
prompt.sexo=Sexo
prompt.ativo=Ativo
prompt.status=Status
prompt.faixaIdade=Faixa Etária
prompt.excluir=Excluir
prompt.incluir=Incluir
prompt.voltar=Voltar
prompt.Masculino=Masculino
prompt.Feminino=Feminino

prompt.ate20=Até 20 anos
prompt.de21a30=De 21 a 30 anos
prompt.de31a40=De 31 a 40 anos
prompt.de41a50=De 41 a 50 anos
prompt.de51a60=De 51 a 60 anos
prompt.acima60=Acima de 60 anos

prompt.senhaAntiga=Senha Antiga
prompt.novaSenha=Nova Senha
prompt.confirmacaoNovaSenha=Confirmação da Senha
button.send=Enviar
button.reset=Cancelar

error.title=Erro Inesperado
error.user.list=Erro obtendo a lista de usuários
error.idUsuario.required=O identificador do usuário é um campo obrigatório
error.login.required=O login do usuário é um campo obrigatório
error.nome.required=O nome do usuário é um campo obrigatório
error.get.user=Erro ao carregar o usuário
error.senhaAntiga.required=A senha antiga é um campo obrigatório
error.novaSenha.required=A nova senha é um campo obrigatório
error.confirmacaoNovaSenha.required=A confirmação da nova senha é um campo obrigatório
error.ConfirmacaoSenha=Erro na confirmação da nova senha
error.user.notFound=Usuário não encontrado
error.senhaAntiga=A senha Antiga não confere
error.update.user=Erro alterando o usuário
error.update.user=Erro incluindo o usuário
error.delete.user=Erro excluindo o usuário
error.idUsuario.duplicateKey=Este código de usuário já existe

Para cada idioma alternativo, devemos adicionar um novo arquivo (no mesmo diretório do arquivo onde temos o
arquivo do idioma padrão) no formato "application_xx.properties", onde xx é o código ISO do idioma. Ex:
(application_en.properties).

No arquivo struts-config.xml devemos definir a localização do arquivo com o idioma padrão.


Coloque neste:

<message-resources parameter="resources.application"/>

Obs: Na Struts 1.0 este arquivo era definido no arquivo web.xml

Nos JSPs que utilizaremos a internacionalização deveremos incluir:


...
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
...

Para declarar que utilizaremos a TagLibrary struts-bean com o prefixo bean e definida no arquivo
/WEB-INF/struts-bean.tld, configurados no arquivo web.xml. E finalmente, utilizaremos a tag (no JSP)
<bean:message key="chave.subchave"/> onde a chave e a subchave correspondem ao texto que será inserido de

11 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

acordo com o idioma do usuário. Exemplo:

...
<h3><bean:message key="application.title"/></h3>
...

Por default, a Struts acessa o idioma principal da aplicação. Devemos utilizar a tag <html:html locale="true">
substituindo a tag <html>, assim, deveremos substituir também a tag </html> por </html:html> Deste modo,
será usado preferencialmente o idioma principal que se encontra no header "Accept-Language" enviado pelo
navegador.

Quando o usuário fizer uma solicitação escolher um novo idioma, baseado em uma lista de idiomas suportados,
informados previamente, adicionaremos o trecho abaixo:

session.setAttribute(Action.LOCALE_KEY,new Java.util.Locale(country, language));

Onde country e language será a string do país e que será feita a tradução. Para mais informações a respeito do
assunto, veja a documentação oficial da Sun disponível em http://java.sun.com/j2se/1.4.2/docs/guide/intl.

Por questões de organização vamos colocar todos os nossos JSPs no diretório cadastro/pages/
. Assim, já podemos escrever o nosso primeiro JSP para listar os usuários:

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>


<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<logic:notPresent name="userListBean" scope="session">


<logic:redirect forward="error"/>
</logic:notPresent>

<html:html locale="true">
<head>
<title><bean:message key="users.title"/></title>
</head>
<body>
<center>
<font face="Comic Sans MS" size="3">
<blockquote>
<center>
<h3><font color="blue"><bean:message key="users.title"/></font></h3>
<table width="80%" border="1">
<tr>
<th width="10%"><bean:message key="prompt.idUsuario"/></th>
<th width="50%"><bean:message key="prompt.nome"/></th>
<th width="20%"><bean:message key="prompt.login"/></th>
<th width="10%"><bean:message key="prompt.ativo"/></th>
<th width="10%"> </th>
</tr>
<%-- loop que percorre a Collection de usuarios --%>
<logic:iterate name="userListBean" id="user" >
<tr>
<td align="center">
<bean:write name="user" property="idUsuario"/>
</td>
<td>
<html:link page="/editUser.do" paramId="idUsuario"
paramName="user" paramProperty="idUsuario">
<bean:write name="user" property="nome"/>
</html:link>
</td>
<td><bean:write name="user" property="login"/></td>
<td><bean:write name="user" property="descricaoStatus"/></td>
<td>
<html:link page="/deleteUser.do" paramId="idUsuario"
paramName="user" paramProperty="idUsuario">
<bean:message key="prompt.excluir"/>
</html:link>
</td>
</tr>
</logic:iterate>
</table>
<br/>
<html:link page="/insertUser.do">incluir</html:link>
<html:link page="/Welcome.do">Página Inicial</html:link>
</center>
</lockquote>

12 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

</body>
</html:html>

Neste ponto, já temos uma aplicação funcionando parcialmente (listando os usuários). No entanto se clicar em
algum link, irá ocorrer um erro, pois ainda não temos os ActionBeanss necessários para invocar a camada de
negócio. Neste momento para facilitar o entendimento, vamos implementar apenas os Beans necessários para
Alterar o usuário. Devemos criar um ActionBean que deverá invocar a camada de negócio e popular um bean com
os dados do usuário a ser alterado, e enviar para a camada de apresentação para que os dados possam ser
alterados. Segue abaixo o código do ActionBean.

package strutsdemo.action;

import java.util.Iterator;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import strutsdemo.bean.AdminUsers;
import strutsdemo.bean.UserData;

public class EditUserAction extends Action {

public ActionForward execute(


ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
ActionErrors errors = new ActionErrors();
try {
HttpSession session = request.getSession();
AdminUsers adminUsers = new AdminUsers();
String idUsuario = request.getParameter("idUsuario");
session.removeAttribute("editUserBean");
LinkedList userList = (LinkedList)session.getAttribute("userListBean");
Iterator iter = userList.iterator();
while (iter.hasNext()) {
UserData user = (UserData)iter.next();
if (user.getIdUsuario() == Integer.parseInt(idUsuario)) {
session.setAttribute("editUserBean", user);
break;
}
}
UserData user = (UserData)session.getAttribute("editUserBean");
if (user == null) {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.user.notFound"));
}
}
catch (Exception e) {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.get.user"));
getServlet().log("Erro carregando o Usuário", e);
}

if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.findForward("failure"));
}
else {
return (mapping.findForward("success"));
}
}
}

Agora devemos alterar o arquivo struts-config.xml, ou seja acrescentar um action-mapping. Para isto basta adicionar

13 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

o trecho de código abaixo na seção <action-mappings>

<action
path="/editUser"
scope="session"
type="strutsdemo.action.EditUserAction"
unknown="false"
validate="false">
<forward
name="success"
path="/pages/editUser.jsp"
redirect="false"
contextRelative="false" />
</action>

Por fim vamos ver como fica o código JSP para alterar o usuário(editUser.jsp). Vale lembrar que o código JSP abaixo
ainda não pode ser executado, pois depende de um Action que ainda não foi implementado.

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>


<%@ taglib uri="/tags/struts-bean" prefix="bean"%>
<%@ taglib uri="/tags/struts-html" prefix="html"%>
<html:html locale="true">
<head>
<title><bean:message key="editUser.title"/></title>
</head>
<body>
<font face="Comic Sans MS" size="3">
<center>
<h3><font color="blue"><bean:message key="editUser.title"/></font></h3>
<html:form action="/saveEditUser.do" method="post" focus="login">
<html:hidden property="idUsuario" name="editUserBean"/>
<table width="80%" border="0">
<tr>
<td width="30%"></td>
<td width="70%">
<%-- exibe os erros de validação --%>
<logic:messagesPresent>
<ul>
<html:messages id="error">
<li><bean:write name="error"/></li>
</html:messages>
</ul>
</logic:messagesPresent>
</td>
<tr>
<tr>
<td align="right"><bean:message key="prompt.idUsuario"/>: </td>
<td align="left"><b><bean:write property="idUsuario" name="editUserBean"/></b></td>
</tr>

<tr>
<td align="right"><bean:message key="prompt.login"/>: </td>
<td align="left"><html:text property="login" name="editUserBean" size="20"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.nome"/></td>
<td align="left"><html:text property="nome" name="editUserBean" size="60"/></td>
</tr>

<tr>
<td align="right"><bean:message key="prompt.senhaAntiga"/>: </td>
<td align="left"><html:password property="senhaAntiga" size="16"
maxlength="20" redisplay="false" value="zzzzz"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.novaSenha"/>: </td>
<td align="left"><html:password property="novaSenha" size="16"
maxlength="20" redisplay="false" value="zzzzz"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.confirmacaoNovaSenha"/>: </td>
<td align="left"><html:password property="confirmacaoNovaSenha" size="16"
maxlength="20" redisplay="false" value="zzzzz"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.faixaIdade"/>: </td>
<td align="left">
<html:select property="faixaIdade" name="editUserBean">
<html:option value="1"><bean:message key="prompt.ate20"/></html:option></html:option>

14 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

<html:option value="2"><bean:message key="prompt.de21a30"/></html:option></html:option>


<html:option value="3"><bean:message key="prompt.de31a40"/></html:option></html:option>
<html:option value="4"><bean:message key="prompt.de41a50"/></html:option></html:option>
<html:option value="5"><bean:message key="prompt.de51a60"/></html:option></html:option>
<html:option value="6"><bean:message key="prompt.acima60"/></html:option></html:option>
</html:select>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.sexo"/>: </td>
<td align="left">
<html:radio property="sexo" value="M" name="editUserBean">
<bean:message key="prompt.Masculino"/>
</html:radio>
<html:radio property="sexo" value="F" name="editUserBean">
<bean:message key="prompt.Feminino"/>
</html:radio>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.ativo"/>: </td>
<td align="left">
<html:checkbox property="ativo" name="editUserBean" titleKey="prompt.ativo"/>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<html:submit><bean:message key="button.send"/></html:submit>
<html:reset><bean:message key="button.reset"/></html:reset>
</td>
</tr>
</table>
</html:form>
<br/>
<html:link page="/listUsers.do">voltar</html:link>
</center>
</font>
</body>
</html:html>

Forms

Uma das tarefas mais trabalhosas no desenvolvimento de uma aplicação é a interação com formulários, para se
alterar e obter nova informação. As validações, o tratamento de erros, a apresentação, e o mesmo a entrada de
dados do form pelo usuário e mensagens de erros, são contempladas pela Struts, o que torna a vida um pouco
mais fácil. Todo trabalho de validação e geração de mensagens de erros serão implementados nos ActionForm e
todo o trabalho de geração de interface no JSP.

Basicamente o ActionForm será um espelho dos inputs que vem do html do browser, ou seja, deverá conter todos
os campos (variaveis privadas com os devidos metodos gets e sets), coincidindo com o nome dos inputs do
formulário html.

Toda validação de dados pela Struts passa por um FormBean. O FormBean é a primeira classe (quando definida no
action-mapping) a ser chamada através do método validate (Detalhes na Figura 2). No entanto, nem toda
solicitação que vem da web necessita de validação de dados. Assim só será necessário usar um FormBean quando
necessitarmos de validação dos dados.

No nosso caso, quando precisarmos salvar os dados que foram alterados pelo usuário. No entanto, antes de
devemos fazer uma validação para que não vá besteira para o banco de dados. Um ActionForm possui dois
métodos importantes. São eles reset e validate.

Veja abaixo a implementação do nosso ActionForm que faz as validações mínimas necessárias.

package strutsdemo.form;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class SaveEditUserForm extends ActionForm {

private String idUsuario;


private String login;

15 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

private String nome;


private String ativo;
private String faixaIdade;
private String sexo;
private String senhaAntiga;
private String novaSenha;
private String confirmacaoNovaSenha;

public void reset(ActionMapping mapping, HttpServletRequest request) {


idUsuario = "-1";
login = "";
nome = "";
ativo = "false";
faixaIdade = "1";
sexo = "M";
senhaAntiga = "";
novaSenha = "";
confirmacaoNovaSenha = "";
}

public ActionErrors validate(


ActionMapping mapping,
HttpServletRequest request) {

ActionErrors errors = new ActionErrors();


if ((idUsuario == null) || (idUsuario.length() < 1)) {
errors.add("idUsuario", new ActionError("error.idUsuario.required"));
}
if ((login == null) || (login.length() < 1)) {
errors.add("login", new ActionError("error.login.required"));
}
if ((nome == null) || (nome.length() < 1)) {
errors.add("nome", new ActionError("error.nome.required"));
}
if ((novaSenha == null) || (novaSenha.length() < 1)) {
errors.add("nome", new ActionError("error.novaSenha.required"));
}
if ((confirmacaoNovaSenha == null) || (confirmacaoNovaSenha.length() < 1)) {
errors.add("confirmacaoNovaSenha", new ActionError("error.confirmacaoNovaSenha.required"));
}
if ((senhaAntiga == null) || (senhaAntiga.length() < 1)) {
errors.add("senhaAntiga", new ActionError("error.senhaAntiga.required"));
}
if (errors.isEmpty()) {
if (!novaSenha.equals(confirmacaoNovaSenha)) {
errors.add("senhaAntiga", new ActionError("error.ConfirmacaoSenha"));
}
}
return errors;
}

public String getNovaSenha() {


return novaSenha;
}
public void setNovaSenha(String novaSenha) {
this.novaSenha = novaSenha;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getAtivo() {
return ativo;
}
public void setAtivo(String ativo) {
this.ativo = ativo;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getSenhaAntiga() {
return senhaAntiga;
}
public void setSenhaAntiga(String senhaAntiga) {
this.senhaAntiga = senhaAntiga;

16 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

}
public String getSexo() {
return sexo;
}
public void setSexo(String sexo) {
this.sexo = sexo;
}
public String getConfirmacaoNovaSenha() {
return confirmacaoNovaSenha;
}
public void setConfirmacaoNovaSenha(String confirmacaoNovaSenha) {
this.confirmacaoNovaSenha = confirmacaoNovaSenha;
}
public String getFaixaIdade() {
return faixaIdade;
}
public void setFaixaIdade(String faixaIdade) {
this.faixaIdade = faixaIdade;
}
public String getIdUsuario() {
return idUsuario;
}
public void setIdUsuario(String idUsuario) {
this.idUsuario = idUsuario;
}
}

Quando terminar a execução do método validate, se ocorrer algum erro, será adicionado em um Collection de
erros. Se este collection tiver algum erro, o FormBean devolve para o jsp que entrou com os dados, com os
campos devidamente preenchidos, e com as mensagens dos erros ocorridos. Caso contrário, ou seja, se não
ocorrer nenhum erro, então o fluxo segue para um ActionBean para invocar a camada de negócio para gravar
estes dados no banco de dados. Logo, precisamos criar este bean. Segue abaixo o código do nosso ActionBean.

package strutsdemo.action;

import java.sql.SQLException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import strutsdemo.bean.AdminUsers;
import strutsdemo.bean.UserData;
import strutsdemo.form.SaveEditUserForm;

public class SaveEditUserAction extends Action {

public ActionForward execute(


ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
ActionErrors errors = new ActionErrors();
try {
HttpSession session = request.getSession();
SaveEditUserForm editUserForm = (SaveEditUserForm)form;
UserData user = (UserData)session.getAttribute("editUserBean");
if (!editUserForm.getSenhaAntiga().equals("zzzzz")) {
if (!user.getSenha().equals(editUserForm.getSenhaAntiga())) {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.senhaAntiga"));
}
else {
user.setSenha(editUserForm.getNovaSenha());
}
}
if (errors.isEmpty()) {

17 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

BeanUtils.copyProperties(user, editUserForm);
DataSource dataSource = getDataSource(request);
AdminUsers adminUsers = new AdminUsers();
adminUsers.updateUser(user);
}
}
catch (SQLException e) {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.update.user"));
getServlet().log("Erro alterando o Usuário", e);
}

if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.findForward("failure"));
}
else {
return (mapping.findForward("success"));
}
}

Agora, que fizemos o FormBean e o ActionBean, devemos fazer o action-mapping associando estes beans. Segue
abaixo as alterações necessárias no struts-config.xml.

<form-beans>
<form-bean dynamic="false" name="saveEditUserForm"
type="strutsdemo.form.SaveEditUserForm">
</form-beans>

...

<action-mappings>
...
<action
attribute="saveEditUserForm"
input="/pages/editUser.jsp"
name="saveEditUserForm"
path="/saveEditUser"
scope="session"
type="strutsdemo.action.SaveEditUserAction"
unknown="false"
validate="true">
<forward
name="success"
path="/pages/listUsers.jsp"
redirect="false"
contextRelative="false" />
</action>
...
</action-mappings>

Obs: Até a versão 1.0 da Struts, a única maneira de validar os dados era herdando do ActionForm, e colocando as
regras de validação no método validate. No entanto com a Struts 1.1 é possível fazer a validação através de um
Form Dinâmico, mais adiante veremos como definir um Form dinâmico.

Outra novidade na Struts 1.1 é o suporte para validação do lado client através de uma biblioteca de javascript
definidos no arquivo validator.xml. Assim, podemos evitar o overread gerado pelas solicitações desnecessárias no
servidor, pois quando a solicitação chega ao web server, estaremos tratando com os dados previamente tratados
pelo javascript do lado client.

Só pra constar como lembrete para os novatos em internet. Pode parecer tentador, mas nunca deixe de validar os
dados no lado servidor se tiver tudo tratado no lado client, pois o usuário pode desabilitar o javascript do browser e
causar erros imprevisíveis na sua aplicação. Conclusão: Use javascript para validar se puder, e sempre use validação
do lado servidor.

Validando dados via javascript

Para fazer a validação com a struts no client via javascript devemos acrescentar os criterios no arquivo no arquivo
validation.xml. Adicionar o trecho de código abaixo na seção <formset>.
<formset>
<form name="saveEditUserForm">

18 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

<field property="idUsuario" depends="required">


<arg0 key="prompt.idUsuario"/>
</field>
<field property="login" depends="required">
<arg0 key="prompt.login"/>
</field>
<field property="nome" depends="required">
<arg0 key="prompt.nome"/>
</field>
<field property="novaSenha" depends="required,mask">
<arg0 key="prompt.novaSenha"/>
<var>
<var-name>mask</var-name>
<var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
<field property="senhaAntiga" depends="required,mask">
<arg0 key="prompt.senhaAntiga"/>
<var>
<var-name>mask</var-name>
<var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
<field property="confirmacaoNovaSenha" depends="required,mask">
<arg0 key="prompt.confirmacaoNovaSenha"/>
<var>
<var-name>mask</var-name>
<var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
</form>
</formset>

Também precisaremos "traduzir" algumas mensagens que já vieram no arquivo application.properties da template
struts-blank.war, pois serão usados no javascript.

# Mensagens de erro padrão para as validações


errors.required={0} é um campo obrigatório.
errors.minlength={0} Não pode ser menor que {1} caracteres.
errors.maxlength={0} Não pode ser maior que {2} caracteres.
errors.invalid={0} está inválido.
errors.byte={0} deve ser um byte.
errors.short={0} deve ser um inteiro curto.
errors.integer={0} deve ser um inteiro.
errors.long={0} deve ser um inteiro longo.
errors.float={0} deve ser um ponto flutuante de precisão simples.
errors.double={0} deve ser um ponto flutuante de precisão dupla.
errors.date={0} não é uma data.
errors.range={0} não está na faixa entre {1} e {2}.
errors.creditcard={0} não é um número de cartão de crédito válido.
errors.email={0} não é um e-mail válido.

Neste ponto temos a aplicação Listando e alterando o cadastro de usuários. No entanto, precisamos ainda fazer a
Inclusão e a Exclusão de Usuários. Então vamos lá ...

Forms Dinâmicos

Uma das grandes melhorias do Struts 1.1 em relação à 1.0 (pode haver controvérsias, mas ...) foi a introdução dos
DynamicForms. Assim você não é mais "obrigado" a criar um Form para validar as entradas de cada formulário HTML.
O Dynamic Form é configurado apenas no struts-config.xml e a Struts já tem uma classe "prontinha para fazer as
validações para você.

Veja o trecho abaixo do codigo para um Form Dinâmico:

<form-bean dynamic="true" name="saveInsertUserForm" type="org.apache.struts.validator.DynaValidatorForm">


<form-property name="idUsuario" type="java.lang.String" />
<form-property name="login" type="java.lang.String" />
<form-property name="nome" type="java.lang.String" />
<form-property name="faixaIdade" type="java.lang.String" />
<form-property name="sexo" type="java.lang.String" />
<form-property name="ativo" type="java.lang.String" />
<form-property name="senha" type="java.lang.String" />
<form-property name="confirmacaoSenha" type="java.lang.String" />
</form-bean>

Terminando o projeto

19 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

Neste ponto o nosso projeto está praticamente finalizado. A partir de agora vamos repetir alguns passos que já
foram feitos antes. ou seja, se você chegou até aqui, só falta um pouco de pratica para você se tornar um expert
nesta framework. Segue abaixo os codigos que ainda faltam:

cadastro/pages/Welcome.jsp
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<html:html locale="true">
<head>
<title><bean:message key="welcome.title"/></title>
<html:base/>
</head>
<body bgcolor="white">
<font face="Comic Sans MS" size="3">
<center>
<h1><font color="blue"><bean:message key="welcome.title"/></font></h1>
<logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application">
<font color="red">
ERROR: Application resources not loaded -- check servlet container
logs for error messages.
</font>
</logic:notPresent>
<h3><bean:message key="welcome.heading"/></h3>
<p><bean:message key="welcome.message"/></p>
<html:link page="/listUsers.do">Cadastro de Usuários</html:link>
</center>
<p><font color="darkblue">
Autor: <html:link href="mailto:wbsouza@yahoo.com.br">Welington B.Souza</html:link>
<br>01/07/2003
</font></p>
</p>
</font>
</body>
</html:html>

cadastro/pages/error.jsp
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<html:html locale="true">
<head>
<title><bean:message key="error.title"/></title>
<html:base/>
</head>
<body bgcolor="white">
<font face="Comic Sans MS" size="3">
<center>
<blockquote>
<h1><font color=red><bean:message key="error.title"/></font></h1>
<logic:messagesPresent>
<ul>
<html:messages id="error">
<li><bean:write name="error"/></li>
</html:messages>
</ul>
</logic:messagesPresent>
</blockquote>
<br/>
<html:link page="/Welcome.do">Página Inicial</html:link>
</center>
</body>
</html:html>

package strutsdemo.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import strutsdemo.bean.UserData;

20 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

public class InsertUserAction extends Action {

public ActionForward execute(


ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
HttpSession session = request.getSession();
UserData user = new UserData();
session.setAttribute("insertUserBean", user);
return (mapping.findForward("success"));
}
}

cadastro/pages/insertUser.jsp
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean"%>
<%@ taglib uri="/tags/struts-html" prefix="html"%>
<html:html locale="true">
<head>
<title><bean:message key="insertUser.title"/></title>
</head>
<body>
<font face="Comic Sans MS" size="3">
<center>
<h3><font color="blue"><bean:message key="insertUser.title"/></font></h3>
<html:form action="/saveInsertUser.do" method="post" onsubmit="return validateSaveInsertUserForm(this);"
focus="idUsuario">
<table width="80%" border="0">
<tr>
<td width="30%"></td>
<td width="70%">
<%-- exibe os erros de validação --%>
<logic:messagesPresent>
<ul>
<html:messages id="error">
<li><bean:write name="error"/></li>
</html:messages>
</ul>
</logic:messagesPresent>
</td>
<tr>
<tr>
<td align="right"><bean:message key="prompt.idUsuario" name="insertUserBean"/>: </td>
<logic:equal name="insertUserBean" property="idUsuario" value="0">
<td align="left"><html:text property="idUsuario" size="5" value=""/></td>
</logic:equal>
<logic:notEqual name="insertUserBean" property="idUsuario" value="0">
<td align="left"><html:text property="idUsuario" size="5" name="insertUserBean"/></td>
</logic:notEqual>
</tr>
<tr>
<td align="right"><bean:message key="prompt.login"/>: </td>
<td align="left"><html:text property="login" size="20" name="insertUserBean"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.nome"/></td>
<td align="left"><html:text property="nome" size="60" name="insertUserBean"/></td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.senha"/>: </td>
<td align="left">
<html:password property="senha" size="16" maxlength="20" redisplay="false"/>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.confirmacaoSenha"/>: </td>
<td align="left">
<html:password property="confirmacaoSenha" size="16" maxlength="20" redisplay="false"/>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.faixaIdade"/>: </td>
<td align="left">
<html:select property="faixaIdade" name="insertUserBean">
<html:option value="1"><bean:message key="prompt.ate20"/></html:option></html:option>
<html:option value="2"><bean:message key="prompt.de21a30"/></html:option></html:option>
<html:option value="3"><bean:message key="prompt.de31a40"/></html:option></html:option>
<html:option value="4"><bean:message key="prompt.de41a50"/></html:option></html:option>

21 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

<html:option value="5"><bean:message key="prompt.de51a60"/></html:option></html:option>


<html:option value="6"><bean:message key="prompt.acima60"/></html:option></html:option>
</html:select>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.sexo"/>: </td>
<td align="left">
<html:radio property="sexo" value="M" name="insertUserBean">
<bean:message key="prompt.Masculino"/>
</html:radio>
<html:radio property="sexo" value="F" name="insertUserBean">
<bean:message key="prompt.Feminino"/>
</html:radio>
</td>
</tr>
<tr>
<td align="right"><bean:message key="prompt.ativo"/>: </td>
<td align="left">
<html:checkbox property="ativo" titleKey="prompt.ativo" name="insertUserBean" />
</td>
</tr>
<tr>
<td colspan="2" align="center">
<html:submit><bean:message key="button.send"/></html:submit>
<html:reset><bean:message key="button.reset"/></html:reset>
</td>
</tr>
</table>
</html:form>
<br/>
<html:link page="/listUsers.do">voltar</html:link>
</center>
</font>
</body>
<html:javascript formName="saveInsertUserForm"/>
</html:html>

package strutsdemo.action;

import java.sql.SQLException;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.validator.DynaValidatorForm;

import strutsdemo.bean.AdminUsers;
import strutsdemo.bean.UserData;

public class SaveInsertUserAction extends Action {

public ActionForward execute(


ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

DynaValidatorForm dynaForm = (DynaValidatorForm) form;


ActionErrors errors = new ActionErrors();
String senha1 = (String)dynaForm.get("senha");
String senha2 = (String)dynaForm.get("confirmacaoSenha");

// como utilizamos um DynamicForm, precisamos terminar a validação aqui.


if (senha1.equals(senha2)) {
try {
HttpSession session = request.getSession();

// popula o bean do usuario com os dados que vieram do Form


UserData user = (UserData) session.getAttribute("insertUserBean");

22 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

user.setIdUsuario(Integer.parseInt((String)dynaForm.get("idUsuario")));
user.setLogin((String)dynaForm.get("login"));
user.setNome((String)dynaForm.get("nome"));
user.setFaixaIdade(Integer.parseInt((String)dynaForm.get("faixaIdade")));
user.setSexo((String)dynaForm.get("sexo"));
user.setNome((String)dynaForm.get("nome"));
user.setSenha(senha1);
boolean ativo = ((String)dynaForm.get("ativo")).equals("on");
user.setAtivo(ativo);

AdminUsers adminUsers = new AdminUsers();


adminUsers.insertUser(user);

LinkedList userList = (LinkedList) session.getAttribute("userListBean");


userList.add(user);
session.removeAttribute("insertUserBean");
}
catch (SQLException e) {
if (e.getErrorCode() == 1062) {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.idUsuario.duplicateKey"));
}
else {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.insert.user"));
}
}
}
else {
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.ConfirmacaoSenha"));
}

if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.findForward("error"));
}
else {
return (mapping.findForward("success"));
}
}
}

package strutsdemo.action;

import java.util.Iterator;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import strutsdemo.bean.AdminUsers;
import strutsdemo.bean.UserData;

public class DeleteUserAction extends Action {

public ActionForward execute(


ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

HttpSession session = request.getSession();


String idUsuario = request.getParameter("idUsuario");

23 of 24
J2EEBrasil - O site da comunidade J2EE no Brasil - www.j2eebrasil.com.br

ActionErrors errors = new ActionErrors();


try {
LinkedList userList = (LinkedList)session.getAttribute("userListBean");
Iterator iter = userList.iterator();
while (iter.hasNext()) {
UserData user = (UserData)iter.next();
if (user.getIdUsuario() == Integer.parseInt(idUsuario)) {
AdminUsers adminUsers = new AdminUsers();
adminUsers.deleteUser(Integer.parseInt(idUsuario));
userList.remove(user);
break;
}
}
}
catch (Exception e) {
errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.delete.user"));
getServlet().log("Erro carregando a lista de usuários", e);
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.findForward("failure"));
}
else {
return (mapping.findForward("success"));
}
}
}

Outras fontes de pesquisa


Struttin' With Struts - Lessons
Stepping through Jakarta Struts
About Struts
i18n with Struts
Introduction to Jakarta Struts Framework
Learn Struts' Form-Related Tags
TheServerSide.com J2EE Community
Project Refinery, Inc
Create Better Web Apps with Struts
Struts Tutorial Overview

Ferramentas para a Struts

Atualmente existem diversas ferramentas para configuração da Struts de forma visual com diversas opções entre
produtos pagos e open source (EasyStruts, Struts Console). No entanto, é recomendável familiarizar-se primeiro
com a configuração manual antes de utilizar estas ferramentas, pois caso ocorra algum problema imprevisto, possa
ser corrigido manualmente. Você pode encontrar mais detalhes em buscas no SourceForge.net, tem um monte de
coisas legais lá.

Integração com outras bibliotecas

Assim como existe a Struts, existem também outras bibliotecas para algumas funcionalidades específicas (custom
tags, templates, etc). Assim é possível fazer uma série de combinações no sentido de conseguir o efeito desejado
na sua aplicação web. Veja algumas bibliotecas que podem ser integradas à Struts.

Struts-Layout
Struts Menu
Velocity
JavaServer Faces
Novamente faça buscas no site SourceForge.net

Boa sorte nos seus projetos com a Struts Framework !

Clique aqui e baixe os fontes deste tutorial.

24 of 24

Você também pode gostar