Struts 2 + AJAX + JPA + Spring – Parte I

 

O Struts é um dos frameworks para desenvolvimento web mais utilizados, aqui neste artigo apresentaremos sua nova versão o Struts 2, utilizando a Java Persistence API para realizarmos a persistência de nossos objetos e ainda integrando essas duas tecnologias com o Spring 2.

 

Para este artigo estaremos utilizando a nova versão do Web Tools Project do eclipse, o Eclipse Europa, o site para download esta no final do artigo assim como os outros links para as frameworks que iremos abordar neste artigo.

 

Esta nova versão do WTP vem mais recheada que o normal, ela já vem com algumas perspectivas a mais, que é o caso da JPA Development e a Database Development entre outras.

 

Também nesta versão temos suporte para a versão 6 do Tomcat entre outras diferenças, no final do artigo tem o link para quem quer saber mais a respeito do Eclipse Europa.

 

O processo de instalação dele é igual ao do antigo, basta descompactar o arquivo baixado do site www.eclipse.org para uma pasta eclipse.

 

Estamos utilizando aqui o MySQL 5 como banco de dados para a aplicação exemplo de nosso artigo.

 

Para nosso artigo criamos um projeto Dynamic Web Project que será chamado de Cadastro, nele teremos os pacotes br.jm.actions para nossas actions, br.jm.entidades para as entidades que serão mapeadas utilizando a JPA, br.jm.servico que terão nossas classes e interfaces que proveram o acesso do banco de dados para nossas actions e por fim o pacote br.jm.persistencia onde ficará nossos dao.

 

Estamos utilizando a versão 2.0.6 do Struts 2, já esta disponível a versão 2.0.8, mas aconselho que se baixe essa versão para evitar problemas na execução da aplicação exemplo.

 

E para a JPA estamos usando a implementação do Hibernate 3

 

A lista de dependencias do projeto esta na figura 5, são as libs necessárias para o funcionamento das frameworks que utilizaremos, foi necessário colocar o jar do dwr, pois o Struts 2 utiliza o dwr para validações.

 

Quando fizer o download do spring, faça o download do spring com dependências, assim vai ter disponível as libs para integrar o Spring com outras tecnologias.

 

Por se tratar de uma aplicação web, é interessante que estas dependências sejam copiadas para o diretório nomeProjeto/WebContent/WEB-INF/lib, assim você não terá problemas quando fazer o deploy da aplicação.

 

Nossa aplicação de exemplo terá uma tela de login que validará um usuário e uma senha, e ira direcionar caso o usuário seja válido para uma tela onde o usuário possa cadastrar um novo contato e listar os contatos já cadastrados, também poderá remover qualquer contato ou toda sua lista, apenas para demonstrarmos a utilização do Struts 2, JPA e Spring.

 

Para que o login funcione, é necessário fazer um insert na tabela CT_USUARIOWEB com um login e uma senha para efetuarmos o login.

 

O Struts 2

Houve muitas mudanças nesta versão do Struts, quem já era familiarizado com a versão 1.x dessa framework com certeza pode se sentir confuso no começo, ao contrário de quem já utilizou o WebWork, pois, muito desta framework foi agregado ao Struts 1x para a criação do Struts2.

 

No Struts 1.3.8, a versão antiga do Struts, é necessário utilizar funções javascript para que sua aplicação web tenha funcionalidades AJAX.  Embora frameworks como o DWR auxiliem na implementação do AJAX, ainda sim temos um contato com o javascript que pode ser cansativo, mas agora no Struts 2, podemos criar páginas com conteúdo dinâmico sem esforço, e sem javascript como será visto neste artigo.

 

O Struts 2 continua uma framework MVC, mas diferente de seu antecessor, nos traz facilidades na integração de AJAX entre outras como para geração de relatórios com o Jasper Report, por exemplo.

 

E agora temos interceptors, que é algo novo no Struts, mas já usado no WebWork, interceptors são objetos que são executados antes e/ou depois da execução das actions. Este login por exemplo, poderia ser um interceptor que executaria antes da action de listar contatos e validaria se existe um usuário válido na session e se o mesmo tivesse permissão de acesso nessa lista e o direcionaria para um login caso não tenha ou seguiria o fluxo normal.

        

O assunto quanto a interceptors é longo e não abordaremos neste artigo apenas usaremos o interceptor padrão do Struts para que qualquer exception  que possa ocorrer em nossa aplicação seja redirecionada para a página erroServicoContato.jsp no caso da action de login. Veja mais sobre intercetors no fim do artigo links a respeito.

 

Actions e o struts.xml

Nossa action que realiza o login do usuário, chamamos de LoginAction, como na listagem 1.

 

package br.jm.actions;

 

import br.jm.persistencia.LoginDAO;

import br.jm.servico.LoginImpl;

 

import com.opensymphony.xwork2.ActionSupport;

 

public class LoginAction extends ActionSupport{

 

      private LoginImpl servico;

      private String login;

      private String senha;

     

      public LoginAction() {

           

      }

     

      public LoginAction(LoginImpl servico) {

            this.servico = servico;

      }

     

      public String validaLogin() {

            if(servico.valida(login, senha)) {

                  return "valido"; 

            }

            return "invalido";

      }

     

      //métodos getters e setters 

}

Listagem 1 – LoginAction.java

        

Você pode reparar que nossa action estende a classe ActionSupport, estamos herdando desta classe para que possamos utilizar as funções de validações do struts, pois temos as propriedades login e senha que irão ter sua entradas validadas.

 

No Struts 2 você pode desenvolver actions que sejam simples POJOS(Plain Old Java Objects) sem herdar de nenhuma classe, veremos um exemplo mais adiante.

        

O atributo servico fornecerá nosso acesso ao banco de dados sempre que necessário e este será injetado pelo Spring no momento que nossa action for instanciada.

 

Vale a pena lembrar que para que não haja problemas na instanciação desta action pelo Spring deve-se declarar um construtor default, ou seja, sem parâmetros, para que as propriedades login e senha de nossa action sejam acessadas na página index.jsp é necessário criar os métodos getters e setters dessas propriedades, isso pode causar erros se não for feito.

 

Esta action possui apenas o método.

 

public String validaLogin() {

      if(servico.valida(login, senha)) {

            return "valido"; 

      }

      return "invalido";

}

 

E é ele que iremos mapear no struts.xml, para que um método possa ser mapeado é necessário que o mesmo tenha como tipo de retorno uma string, não passamos nada como parâmetro para nosso método, pois, estamos utilizando os atributos de nossa action para receber os dados de input do usuário em uma página jsp, por isso que se faz necessário ter os métodos getters e setters de um atributo, pois eles são utilizados para atribuir ou obter um valor de uma propriedade quando acessado pelo jsp.

 

Para utilizarmos nosso método validaLogin() estaremos utilizando a página index.jsp para criar um form que será explicado mais adiante para fazermos o input dos dados. Abaixo como ficará a declaração de nossa action no arquivo struts.xml.

        

<action name="login!*" method="{1}"

      class="br.jm.actions.LoginAction">

      <result name="input">/index.jsp</result>

      <result name="valido">/contato.jsp</result>

      <result name="invalido">/erroLogin.jsp</result>

</action>

     

Aqui estamos criando uma action chamada login e estamos configurando o primeiro método desta action para ser acessado utilizando o parâmetro method apontando que deve-se utilizar o primeiro método desta action, pode-se observar no nome de nossa action que colocamos um ponto de exclamação e * (login!*), o struts irá fazer um split na string e como tem um asterisco ele ira ler todos os métodos de nossa action, mais adiante temos um exemplo especificando o nome do método a ser mapeado.

 

Pode parecer estranho, sendo que na versão antiga do Struts tinhamos apenas o método execute() que tinha dependências com HttpServletRequest e HttpServletResponse, aqui no Struts 2, não temos essa dependência diretamente, e podemos mapear um ou mais métodos de nossas actions, ou utilizar o método execute() da classe ActionSupport que não possui dependências como antigamente e retorna uma String.

        

No nosso caso, o retorno valido ira direcionar o usuário para a página contato.jsp após a execução do método validaLogin() e o retorno invalido direciona para a página erroLogin.jsp, finalmente o retorno input é utilizado para possíveis erros que possam acontecer na validação dos campos.

                  

Nossa próxima action é a InsereContatoAction, conforme a listagem 2. Esta action é responsável por receber os dados do usuário e executar o método que ira persistir essas informações no nosso banco de dados, para isso temos a propriedade contato que será populada por um form na página contato.jsp.

 

package br.jm.actions;

 

import br.jm.entidade.Contato;

import br.jm.servico.ServicoContatoImpl;

 

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ActionSupport;

 

public class InsereContatoAction extends ActionSupport{

 

      private ServicoContatoImpl servico;

      private Contato contato;

           

      public InsereContatoAction() {

 

      }

     

      public InsereContatoAction(ServicoContatoImpl servico) {

            this.servico = servico;

      }

     

      public String execute() throws Exception {

            if(hasActionErrors() || hasFieldErrors()) {

                  return "input";

            }

            servico.salvarContato(contato);

           

            return Action.SUCCESS;

      }

      //métodos getters e setters

Listagem 2 – InsereContatoAction.java

 

Repare no retorno de nosso método execute(). O struts disponibiliza alguns retornos default como, SUCCESS, ERROR, etc pela interface com.opensymphony.xwork2.Action.

 

Nesta action sobrescrevemos o método execute() da classe ActionSupport, como pode ser observado no mapeamento da mesma no struts.xml

 

<action name="insereContato"

      class="br.jm.actions.InsereContatoAction">

     

      <result name="input">/contato.jsp</result>

      <result>/contato.jsp</result>

</action>

 

Não especificamos qual o método que será mapeado nesta action, isto porque quando não é informado qual método será acessado, o default para o struts é o método execute(), então quando esta action for executada o método execute() sera executado por default.

        

Temos então apenas o nome insereContato que será utilizado para referenciar esta action na página jsp, o parametro class informamos onde esta a action com o caminho completo.

        

Esta action também tem uma propriedade servico que disponibilizara meios para acessar nosso banco para inserir este novo registro, também usaremos o Spring para disponibilizar este serviço para nossa action, mas adiante veremos como funciona este ServicoContatoImpl.

        

Quanto ao mapeamento desta action, pode ser visto abaixo que é semelhante ao de nossa action de login, só que este direcionara o usuário para a página contato.jsp após ter executado a action.

        

No tipo de retorno desta action, esta especificado o tipo input que como vimos é para possíveis erros de validação, e um outro result sem identificação que direciona para a página contato.jsp , como estamos utilizando o tipo de retorno fornecido pelo Struts 2, o Action.SUCCESS, e este retorna a sting “success” que é definida como padrão pelo mesmo, então ao criarmos um result sem nome este será de acordo com a string “success”.

 

Para listarmos todos os contatos do usuário utilizaremos a action ListaContatosAction como na listagem 3, para obtermos a lista do banco de dados. também será disponibilizado acesso ao banco pelo ContatoImpl.

 

package br.jm.actions;

 

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

 

import br.jm.entidade.Contato;

import br.jm.servico.ServicoContatoImpl;

 

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ActionSupport;

 

public class ListaContatosAction extends ActionSupport{

 

      private ServicoContatoImpl servico;

      private List<Contato> contatos;

     

      public ListaContatosAction() {

 

      }

     

      public ListaContatosAction(ServicoContatoImpl servico) {  

            servico = servico;

      }

     

      public String execute() throws Exception {

            contatos = servico.listarContatos();

           

            return Action.SUCCESS;

      }

Listagem 3 – ListaContatosAction.java

 

Temos uma propriedade contatos que é a nossa lista, essa é preenchida quando chamado o método execute de nossa action. Para utilizarmos nossa action devemos configurar no struts.xml conforme abaixo.

 

<action name="listaContatos"

      class="br.jm.actions.ListaContatosAction">

      <interceptor-ref name="exception" />

      <interceptor-ref name="basicStack" />

      <exception-mapping result="erroServicoContato"

            exception="java.lang.Exception" />

            <result name="erroServicoContato">

            /erroServicoContato.jsp

      </result>

      <result>/listarContatos.jsp</result>

</action>

 

Nossa action se chama listaContatos, e possui um intercetor para qualquer excetion que possa ocorrer e ira direcionar para a página erroServicoContato.jsp, e seu result default onde também não especificamos um nome para ele é a página listarContato.jsp.

 

Para utilizarmos este interceptor para exceptions apenas configuramos o inteceptor e mapeamos para um result apontando o tipo de exception que este devera filtrar, que neste caso é java.lang.Exception que consequentemente ira ser qualquer exception que ocorra já que todas herdam dela, e configuramos o result como a página erroLogin.jsp.

 

<interceptor-ref name="exception" />

<interceptor-ref name="basicStack" />

<exception-mapping result="erroServicoContato"

      exception="java.lang.Exception" />

      <result name="erroServicoContato">

      /erroServicoContato.jsp

</result>

 

Aqui a única coisa que alteramos foi o parâmetro result onde criamos o nome erroExcetion e apontamos este nome para a página erroServicoContato.jsp.

 

Criamos uma página jsp conforme a listagem 4 sem nenhum cabeçalho, apenas escrevemos a navegação da propriedade contatos.

 

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

 

<table border="1" bordercolor="blue" cellpadding="2" cellspacing="2">

     

      <tr>

            <td><b>Contatos Registrados</b></td>

      </tr>

      <tr>

            <td style="width: 160px; text-align: center;">Nome</td>

            <td style="width: 160px; text-align: center;">Sobrenome</td>

            <td style="width: 160px; text-align: center;">Data De Nascimento</td>

            <td style="width: 160px; text-align: center;">DDD</td>

            <td style="width: 160px; text-align: center;">Telefone</td>

            <td style="width: 160px; text-align: center;">Remover</td>

      </tr>

     

           

     

      <s:iterator value="contatos">

            <tr>

                  <td><s:property value="pessoa.nome"/></td>

                  <td><s:property value="pessoa.sobreNome"/></td>

                  <td><s:date name="pessoa.dataDeNascimento" format="dd/MM/yyyy"/></td>

                  <td><s:property value="telefone.ddd"/></td>

                  <td><s:property value="telefone.numero"/></td>

                  <td>

                 

                  <s:url id="link1" action="/removerContato!removeUmContato.action">

                        <s:param name="id"><s:property value="pessoa.id"/> </s:param>

                  </s:url>

           

                  <s:a id="linkRemove"

                              href="%{link1}"

                              theme="ajax"

                              notifyTopics="listaContatoTopic"

                              loadingText="Removendo contato.."

                              errorText="Ocorreu um erro durante o processamento.."

                              showLoadingText="true"

                              showErrorTransportText="true">Excluir</s:a></td>

            </tr>

      </s:iterator>

     

</table>

Listagem 4 – listaContatos.jsp

        

Iremos ver no próximo tópico as páginas jsp que criamos para nossa aplicação exemplo.

 

Agora vamos ver a action que ira remover os registros do usuário, na action RemoverContatoAction temos dois métodos, um para remover apenas um registro e o outro para remover todos os registros do usuário, foi dividido em duas declarações no struts.xml para demonstrar outras formas de trabalhar com o Struts 2.

 

<action name="removerContato!*" method="removeLista"

      class="br.jm.actions.RemoverContatoAction">

      <interceptor-ref name="exception" />

      <interceptor-ref name="basicStack" />

      <exception-mapping result="erroServicoContato"

            exception="java.lang.Exception" />

      <result name="erroServicoContato">

            /erroServicoContato.jsp

      </result>

      <result>/contato.jsp</result>

</action>

 

Repare que temos o mesmo nome para nossa action, vamos apenas especificar qual método será acessado, no caso acima informamos que o método removeLista  pelo parâmetro method terá um interceptor para qualquer exception e seu resultado deve ser direcionado para a página contato.jsp.

 

Para nossa outro mapeamento para a action RemoverContatoAction informamos que o método removeUmContato() irá ser acessado e tem os mesmos retornos e tipo de interceptor do método removeLista().

 

<action name="removerContato!*" method="removeUmContato"

      class="br.jm.actions.RemoverContatoAction">

      <interceptor-ref name="exception" />

      <interceptor-ref name="basicStack" />

      <exception-mapping result="erroServicoContato"

            exception="java.lang.Exception" />

      <result name="erroServicoContato">

            /erroServicoContato.jsp

      </result>

      <result>/contato.jsp</result>

</action>

 

Nossa action para remover como pode ser observado na listagem 5 é um pojo, não herda de nenhuma classe e segue o padrão JavaBean, mas mesmo assim o utilizamos como uma action do Struts 2, assim fica mais prático de se testar as actions, visto que não há a necessidade de um container para executá-las.

 

package br.jm.actions;

 

import com.opensymphony.xwork2.Action;

import br.jm.servico.ServicoContatoImpl;

 

public class RemoverContatoAction {

 

      private Long id;

      private ServicoContatoImpl servico;

     

      public RemoverContatoAction() {

     

      }

     

      public RemoverContatoAction(ServicoContatoImpl servico) {

            this.servico = servico;

      }

     

      public String removeUmContato() {

            servico.removerContato(id);

           

            return Action.SUCCESS;

      }

     

      public String removeLista() {

            servico.removerContatos();

           

            return Action.SUCCESS;

      }

Listagem 5 – RemoverContatoAction.java

 

Vocês podem estar pensando que seria melhor ter desenvolvido uma action ContatoAction e nela ter os métodos para inserir, remover, listar, etc. Mas o objetivo em separar em várias classes nossas actions foi de ilustrar as diferentes maneiras que podem ser utilizadas para configurar sua action no Struts 2, veja os links no final do artigo para mais informações.

Leia todos os artigos da série