terça-feira, 6 de outubro de 2009

Struts 2 com Annotations

Apesar de ser um assunto não muito atual, com bastante documentação, eu tive muito trabalho para escrever minha primeira aplicação com Struts 2. O site do projeto tem muito conteúdo, mas nada mais prático para começar um projeto do zero. Em minha busca atrás de tutoriais, algo que me ajudasse, achei uma quantidade razoável de artigos/tutoriais, porém, nenhum que funcionasse efetivamente, principalmente utilizando annotations. Fiz o download de vários exemplos, uns funcionavam, outros não, mas não consegui fazer minha aplicação funcionar lendo apenas um tutorial. Dada essa dificuldade, pretendo colocar em prática o que eu aprendi, e, quem sabe, se algum desesperado precisar aprender uma aplicação simples utilizando Struts 2, esse tutorial funcione :-)

Para começar, precisamos montar nosso ambiente, vamos precisar do Eclipse, e do TomCat plugin.

Nosso projeto ficará mais ou menos assim:



Agora, criamos um projeto Java, e inserimos as bibliotecas do Struts 2 (essas são os jars necessários para o nosso exemplo simples, precisando de mais funcionalidades, vamos precisar de mais jars - a versão que estou utilizando é a 2.1.6)
  • commons-fileupload-1.1.1.jar
  • commons-logging-1.0.4.jar
  • freemaker-2.3.13.jar
  • ognl-2.6.11.jar
  • struts2-convention-plugin-2.1.6.jar (Esse jar é necessário somente se for utilizar annotations em sua aplicação, para as configurações em XML, esse jar não é necessário)
  • struts2-core-2.1.6.jar
  • xwork-2.1.2.jar

Criamos uma pasta WEB-INF no nosso projeto, que vai conter nosso querido amigo web.xml (arquivo padrão para configuração do struts). Nele, precisamos obrigatoriamente configurar um FilterDispatcher, o default do struts é o org.apache.struts2.dispatcher.FilterDispatcher, mas nada impede o mapeamento de outros filtros.








struts2
org.apache.struts2.dispatcher.FilterDispatcher



struts2
/*



index.jsp





Por que preciso de um FilterDispatcher?! Bom, ele é o responsável por filtrar requisições vindas do browser! Junto com as Actions e os Interceptors, é a camada de Controller do MVC. MV O QUE?!

MVC - Model-View-Controller, é o padrão implementado pelo Struts.

Actions - Unidade mais básica do struts. É basicamente uma ação a ser executada pelo usuário.

Interceptors - Uma das grandes novidades do Struts 2 com relação ao Struts 1, é o controle sobre o fluxo de execução de uma requisição, por meio de Interceptors. Com eles podemos executar métodos antes ou depois de uma determinada Action ser executada.

Chega de teoria! Tendo o web.xml configurado, é hora de cuidar da View! O Model não é nosso foco agora, porque ele seria o Domínio da nossa aplicação. Mas ... não temos domínio, é só um exemplo simples! Então vamos criar os jsp's necessários para o nosso exemplo.

index.jsp (repare que esse é o jsp configurado no welcome-file do web.xml)


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>







resposta.jsp


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>




Ok, temos a View pronta. E agora? Agora é hora de criar as Actions! No Struts 2, não é necessário estender nenhuma classe, a Action pode ser um POJO simples, desde que devidamente anotada. Repare que o valor(value) da @Action definida no método a executar, é o mesmo que colocamos na action do formulário no nosso jsp. Quando subirmos nossa aplicação, o Struts vai recuperar as classes anotadas, verificar quais são as actions (essa verificação é feita com o Struts procurando classes anotadas dentro do pacote action), e é assim que o jsp vai saber que ação executar.

Exemplo de Action (repare que é um POJO comum, anotado):

package br.com.maps.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;

/**
* Action de exemplo para o tutorial de Struts 2 com annotation.
*
* @author finx
* @created Oct 19, 2009
*/
public class HelloWorldAction {

private String requisicao;

private String resposta;

/**
* Executa a Action.
*
* @return resultado da execução da Action.
*/
@Override
@Action(value = "/helloworld", results = { @Result(name = "success", location = "/resposta.jsp"), @Result(name = "input", location = "/index.jsp") })
public String execute() {
this.resposta = "Olá! Eu estou executando uma ação do usuário, com a seguinte requisição: ";
return "success";
}

/**
* Retorna a mensagem da requisição.
*
* @return a mensagem da requisição.
*/
public String getRequisicao() {
return requisicao;
}

/**
* Define a mensagem da requisição passada como parâmetro.
*
* @param requisicao a mensagem da requisição a ser definida.
*/
public void setRequisicao(String requisicao) {
this.requisicao = requisicao;
}

/**
* Retorna a resposta da requisição, chamada pelo componente s:property do jsp.
*
* @return a resposta da requisição, chamada pelo componente s:property do jsp.
*/
public String getResposta() {
return this.resposta + " [" + this.getRequisicao() + "].";
}

}


Subindo nossa aplicação, ela deve ficar com essa cara:



Hmmm bem legal ... mas quando eu aperto o botão e não escrevo nada, a minha resposta é vazia! Há, aí é que entram os interceptadores. Estendendo nossa action de ActionSupport, ganhamos um fluxo de execução com interceptadores configurado no arquivo struts-default.xml, e aí é só configurar!

Exemplo de requisição vazia:



Um exemplo de como a Action ficaria com validação:

package br.com.maps.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.Validations;

/**
* Action de exemplo para o tutorial de Struts 2 com annotation.
*
* @author finx
* @created Oct 19, 2009
*/
public class HelloWorldAction extends ActionSupport {

private String requisicao;

private String resposta;

/**
* Executa a Action.
*
* @return resultado da execução da Action.
*/
@Override
@Action(value = "/helloworld", results = { @Result(name = "success", location = "/resposta.jsp"), @Result(name = "input", location = "/index.jsp") })
@Validations(requiredStrings = @RequiredStringValidator(fieldName = "requisicao", message = "Campo obrigatório"))
public String execute() {
this.resposta = "Olá! Eu estou executando uma ação do usuário, com a seguinte requisição: ";
return "success";
}

/**
* Retorna a mensagem da requisição.
*
* @return a mensagem da requisição.
*/
public String getRequisicao() {
return requisicao;
}

/**
* Define a mensagem da requisição passada como parâmetro.
*
* @param requisicao a mensagem da requisição a ser definida.
*/
public void setRequisicao(String requisicao) {
this.requisicao = requisicao;
}

/**
* Retorna a resposta da requisição, chamada pelo componente s:property do jsp.
*
* @return a resposta da requisição, chamada pelo componente s:property do jsp.
*/
public String getResposta() {
return this.resposta + " [" + this.getRequisicao() + "].";
}

}


E um exemplo de como ficaria a nossa requisição vazia, com validação:



Finalmente, com o fluxo completo:



E, disponibilizando o exemplo utilizado nesse tutorial na página de projetos do blog.