29 de dezembro de 2011

Tutorial VRaptor 3

Bom, meu último post desse ano, vou falar do incrível framework brasileiro o VRaptor.
É outro framework MVC que vale a pena aprender e escrever sobre é o VRaptor, é um framework MVC muito poderoso, teve seu início na época do Struts 1.x, como solução a burôcracia que o Struts trazia, é mantido pela Caelum, grátis e open source e se diferencia pela facilidade na curva de aprendizado e por trabalhar com convenções a configurações, ou seja, adeus um monte de XML, para quem viu o último post de Spring, verá que VRaptor é tão simples de utilizá-lo quanto o próprio Spring MVC.
Outra vantagem é documentação nas opções Português e Inglês, a Caelum também fornece uma apostila completa de forma grátis para download e caso a empresa queira investir há o curso de VRaptor FJ 28 e o curso VRaptor online.
Fiz o curso online e recomendo.

Vamos ver um exemplo que montei em VRaptor, o código está disponível para download.

Aconselho antes a ler sobre Patterns como DAO, Repository, MVC, TO, PO, VO, DTO.

Criei um pequeno CRUD sem acesso ao banco apenas para mostrar a simplicidade do framework, basta pegar o código e rodar no seu Eclipse ou IDE favorita.

Tentei manter no estilo do post sobre Spring para quem estiver acompanhando validar o quão semelhante e mais simples o VRaptor é.

Passo 1:

Crie um projeto Web (ou importe o código).

Passo 2:

Criei a estrutura de pacotes:


Passo 3:

Vamos criar os Beans que ficaram no pacote domain:

public class Livro {

    private Long id;
    private String titulo;
    private String loja;
    private BigDecimal preco;
    private String autor;
   
    public Livro(Long id, String titulo, String loja, BigDecimal preco, String autor){
        this.id = id;
        this.titulo = titulo;
        this.loja = loja;
        this.preco = preco;
        this.autor = autor;
    }
   
    public Livro() {
    }

   //getters and setters
  
}

Passo 4:

Assim como no Spring é utilizado no VRaptor algumas anotações para o framework saber o que vamos querer e criar seus mapeamentos assim não necessitamos de XMLs.
No DAO utilizaremos as anotações:
@Component - VRaptor mapeará as classes como componentes (instâncias das classes que executarão tarefas)
@RequestScoped - Os componentes ficam em escopos específicos, podemos mapeá-los como @RequestScoped (o componente é o mesmo durante uma instância), @SessionScoped (Componente é o mesmo durante uma http session), @ApplicationScoped (Singleton, apenas 1 por aplicação), @PrototypeScoped (Componente sempre instanciado quando requisitado), maiores informações aqui.

Comparativo:No Spring utilizávamos a anotação @Repository.

LivroDao

@Component
@RequestScoped
public class LivroDao implements LivroDaoI {

private final static List livros = new ArrayList();
   
    static {
        populaProdutosIniciais();
    }
   
    public void salva(Livro livro) {
        livro.setId(livros.size() +1l);
        livros.add(livro);
    }

    public List listaTodos() {
        return Collections.unmodifiableList(livros);
    }

    public void remove(Livro livro) {
        Iterator it = livros.iterator();
        while(it.hasNext()) {
            Livro existente = it.next();
            if(existente.getId().equals(livro.getId())) {
                it.remove();
                break;
            }
        }
    }

    private static void populaProdutosIniciais() {
        livros.add(new Livro(1l, "Introdução À Arquitetura e Design de Software - Uma Visão Sobre a Plataforma Java", "Saraiva", new BigDecimal(73.00), "Sergio Lopes, Paulo Silveira, Guilherme Silveira"));
        livros.add(new Livro(2l, "iWOZ - a Verdadeira História da Apple Segundo Seu Cofundador", "Saraiva", new BigDecimal(49.90), "Steve Wozniak, Gina Smith"));
        livros.add(new Livro(3l, "Steve Jobs - A Biografia", "Livraria Cultura", new BigDecimal(49.90), "Walter Isaacson"));
        livros.add(new Livro(4l, "Use a cabeça! Padrões de Projetos", "Livraria Cultura", new BigDecimal(142.89), "Eric Freeman, Elisabeth Freeman"));
    }

    public Livro buscaPorId(Long id) {
        for(Livro livro : livros) {
            if(livro.getId().equals(id)){
                return livro;
            }
        }
        return null;
    }

}

É boa prática sempre criar interfaces então criei a LivroDaoI

public interface LivroDaoI {

    public void salva(Livro livro);

    public List listaTodos();

    public void remove(Livro livro);

    public Livro buscaPorId(Long id);
   
}

Lembrete: Esses livros do exemplo existem e recomendo.

Passo 5:

Criei uma camada de business ondem a lógica de negócio será mantida separada da DAO e da camada de View, também está mapeada com @Component, como o foco não é o Spring não utilizei o @Autowired para injetar dependências, mas você pode utilizá-lo.

Comparativo:
No Spring utilizávamos a anotação @Service.

LivroService
@Component
public class LivroService implements LivroServiceI{

    private LivroDaoI    livroDao;
   
    public LivroService(){
        if(livroDao==null){
            livroDao = new LivroDao();
        }
    }
   
    public void salva(Livro livro) {
        livroDao.salva(livro);
    }

    public List listaTodos() {
        return livroDao.listaTodos();
    }

    public void remove(Livro livro) {
        livroDao.remove(livro);
    }

    public Livro buscaPorId(Long id) {
        return livroDao.buscaPorId(id);
    }

}

LivroServiceI
public interface LivroServiceI {
   
    public void salva(Livro livro);

    public List listaTodos();

    public void remove(Livro livro);

    public Livro buscaPorId(Long id);


}
Passo 6:

Para evitar acessar diretamente o Bean e se por ventura necessitar colocar algum atributo de tela criei um Form onde conterá o Bean e os demais atributos de tela caso necessário.

LivroForm

public class LivroForm {

    private Livro livro = new Livro();

    //getters and setters
   
}

Passo 7:

Criei uma classe Controller assim como no Spring o padrão adotado é NomeClasseController, utilizaremos aqui a anotação @Resource TODOS seus controllers deverão ter essa anotação.
@Resource - O VRaptor saberá através dessa anotação qual a convençao para criar a URI exemplo: /nomeController/nomeMetodo.
 Temos também a anotação @Post e @Path("/livro/{id}").
@Post -  Aqui poderíamos criar comportamentos REST, iremos receber nosso form completamente populado.
@Path("/livro/{id}") - Aqui receberemos no nosso bean livro o atributo id preenchido, essa anotação muda a URI que acessará o método.
Foi utilizado também a Classe Result que tem como objetivo retornar um ou mais objetos, assim como mensagens, dentro outas coisas vale a pena ler aqui.
Validação:
validator.checking(new Validations(){{
                that(livroForm.getLivro().getPreco().doubleValue() > 0,"erro", "livro.preco.invalido");
}});
            validator.onErrorUsePageOf(this).formulario();  
Para validar os dados de retorno no método basta chamar o validator.checking, instanciar Validations, dentro do that você colocará a validação, a categoria e a mensagem.
O validator.onErrorUsePageOf(this).formulario(); é utilizado para fazer o direcionamento, dentro passo um this informando que é essa classe (LivroController) que ele vai utilizar no método formulario, ou seja ele irá redirecionar para o formulário com as mensagens de erro.
No JSP para exibir a mensagem basta colocar:
   
       [c:forEach var="error" items="${errors}"]
            ${error.category} - ${error.message}[br /]
        [/c:forEach]

Comparação:
No Spring utilizamos as anotações: @Controller, @RequestMapping, @RequestParam(value = "produto.id") que fariam as mesmas coisas acima.
No caso do Result seria a utilização do ModelAndView quem traz a mesma idéia de enviar um ou mais objetos.
Validação:
No Spring utilizamos no parametro do método do Controller um @Valid e quem trata o erro e adiciona as mensagens após a verificação por um if ou pelo HibernateValidator é o BindingResult.
No JSP colocaríamos por exemplo:
[form:errors path="nome"  /]
LivroController
@Resource
public class LivroController {

    private final Result result;

    private final LivroServiceI livroService;

    private final Validator validator;
   
    public LivroController(LivroServiceI livroService, Result result, Validator validator) {
        this.livroService = livroService;
        this.result = result;
        this.validator = validator;
    }
   
    public void formulario(){
       
    }
   
    public void consulta(){
       
       }
   
    @Post
    public void adiciona(final LivroForm livroForm){
         validator.checking(new Validations(){{
                that(livroForm.getLivro().getPreco().doubleValue() > 0,"erro", "livro.preco.invalido");
                that(!livroForm.getLivro().getTitulo().isEmpty(), "erro", "livro.titulo.nao.informado");
                that(!livroForm.getLivro().getLoja().isEmpty(), "erro", "livro.loja.nao.informado");
            }});
            validator.onErrorUsePageOf(this).formulario(); 
   
            livroService.salva(livroForm.getLivro());
            result.redirectTo(this).lista();
    }
   
    public void remove (Livro livro){
        livroService.remove(livro);
        result.nothing();
    }
   
    public List lista() {
        return livroService.listaTodos();
    }
   
    @Post
    public void pesquisa(LivroForm livroForm) {
        result.redirectTo(this).exibe(livroForm.getLivro().getId());
    }
   
    @Path("/livro/{id}")
    public LivroForm exibe(Long id){
        LivroForm livroForm = new LivroForm();
        livroForm.setLivro(livroService.buscaPorId(id));
        return livroForm;
    }
   
}

Assim como no Spring, no VRaptor um método terá um JSP que o representa, assim o VRaptor por convenção o redirecionará.

Passo 8:

Os JSPs devem estar dentro da pasta WEB-INF -> jsp -> nomeDoQueEleRepresenta -> nomeMetodoQueEleRepresenta.

Exemplo:


Simples se o controller vai criar a URI, nossa estrutura do JSP deverá estar representando esse acesso, nosso controller chama-se LivroController e dentro há um método exibe sua url ficará /livro/exibe, dentro da pasta jsp (padrão) deverá conter a pasta livro (LivroController, remove-se do nome Controller), exibe (Método que há dentro de LivroController), por convenção ele saberá se achar e fazer a escolha do jsp para abrir no browser.
No exemplo para download tem todos JSPs, acesso via EL aos valores ea única taglib que utilizei foi a de formatação já citada no exemplo Spring MVC.

Comparação:
No caso do Spring é necessário configurar pelo menos 1 XML que representará onde ele encontrará os JSPs e também terá a configuração para ele achar os pacotes que contenham os mapeamentos, isso NÃO é feito no VRaptor, não existe essa configuração, o VRaptor por suas convenções é mais inteligente.

Passo 9:

web.xml
[?xml version="1.0" encoding="UTF-8"?]
[web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"]
  [display-name]projetoVRaptor[/display-name]
  [filter]
    [filter-name]vraptor[/filter-name]
    [filter-class]br.com.caelum.vraptor.VRaptor[/filter-class]
  [/filter]
  [filter-mapping]
    [filter-name]vraptor[/filter-name]
    [url-pattern]/*[/url-pattern]
    [dispatcher]FORWARD[/dispatcher]
    [dispatcher]REQUEST[/dispatcher]
  [/filter-mapping]
[/web-app]
 Notem que a configuração do web.xml é muito mais simples do que a configuração que fazemos no Spring.

Passo 10:

Para internacionalização das mensagens de erro criei um message.properties

livro.preco.invalido=preço deve ser maior que 0.
livro.titulo.nao.informado=O título é obrigatória.
livro.loja.nao.informado=A Loja de venda é obrigatória.
Espero que tenham gostado, o VRaptor é um poderoso framework MVC, para quem já utiliza Spring MVC é muito fácil entender como o VRaptor funciona, também torna bem mais simples a vida do desenvolvedor pois o único XML configurado é o web.xml, isso é uma enorme vantagem, as convenções também tornam o trabalho muito rápido e produtivo facilitando muito nossa vida.

Mais Informações:

Blog da Caelum - Links de Artigos e Cursos
Macelo Madeira - Vantagens do VRaptor
Lucas Toniazzo - Iniciando com VRaptor
Washington Botelho - Posts sobre Vraptor

Revistas:
Mundo Java (Mundo J) - Ediçoes: 61, 93, 94, 96, 98

Java Magazine - Ediçoes: 17, 38

Um Feliz Ano Novo para todos.

24 de dezembro de 2011

Tutorial Spring MVC

Faz muito tempo que não posto um tutorial completo com um projeto (código exemplo) sobre um framework, atualmente estou desenvolvendo aplicação com Spring MVC e isso acabou me motivando a escrever um tutorial sobre ele.

Aconselho antes a ler sobre Patterns como DAO, Repository, MVC, TO, PO, VO, DTO.

Passo 1:

Executem o Eclipse -> File -> New -> Project -> Dynamic Web Project

Passo 2:

Crie essa estrutura básica de pacotes.



Passo 3:

Criei dentro do pacote domain um bean
public class Produto {
     public Produto(Long id, String nome, String descricao, double preco, String cor, Long quantidade) {
     this.id = id;
     this.nome = nome;
     this.descricao = descricao;
     this.preco = preco;
     this.cor = cor;
     this.quantidade = quantidade;
}
private Long id;
private String nome;
private String descricao;
private double preco;
private String cor;
private Long quantidade;

//getters and setters

}



Passo 4:

Dentro do pacote dao criei a ProdutoDao e a ProdutoDaoI:

Interface:

public interface ProdutoDaoI {

    public void salva(Produto produto);
   
    public List pegaTodos();
   
    public void remove(Produto produto);
   
    public Produto pegaPorId(Long id);
}

Implementação:


Dentro da implementação do meu DAO notem a anotação @Repository essa anotação diz que ali será um repositório de dados ou seja seu DAO (camada de persistência),  com essa anotação automaticamente as exceptions são traduzidas e você não terá preocupações com lock otimista do JPA por exemplo.

@Repository
public class ProdutoDao implements ProdutoDaoI {

    private final static List produtos = new ArrayList();
   
    static {
        populaProdutos();
    }
   
    public void salva(Produto produto) {
        produto.setId(produtos.size() +1l);
        produtos.add(produto);
    }

    public List pegaTodos() {
        return Collections.unmodifiableList(produtos);
    }

    public void remove(Produto produto) {
        Iterator it = produtos.iterator();
        while(it.hasNext()) {
            Produto existente = it.next();
            if(existente.getId().equals(produto.getId())) {
                it.remove();
                break;
            }
        }
    }

    private static void populaProdutos() {
        produtos.add(new Produto(1l, "iPhone", "Celular da apple", 299.90, "prata", 10L));
        produtos.add(new Produto(2l, "DVD Yu Yu Hakusho", "Anime sobre Yusuke Urameshi um detetive sobrenatural.", 1999.99, "prata", 20L));
        produtos.add(new Produto(3l, "Caelum OnLine", "Cursos online da Caelum", 249.00, "verde", 60L));
        produtos.add(new Produto(4l, "Fred Rovella Show", "cd de músicas italianas", 29.90, "azul", 100L));
    }

    public Produto pegaPorId(Long id) {
        for(Produto produto : produtos) {
            if(produto.getId().equals(id)) return produto;
        }
        return null;
    }

}

Passo 5:
 
Nesse Passo serão utilizadas 2 anotações do Spring a @Service e a @Autowired (também citei a @Qualifier para quem vá utilizá-la futuramente).@Autowired: Serve para injeção de beans, como meus beans são simples posso usar tranquilamente essa anotação, mas se meu projeto começar a crescer muito será necessário usar a anotação @Qualifier para indicar qual bean quero injetar e evitar erros de injeção.
@Service: Serve para anotar a camada de serviço.

Dentro do pacote service criei as classe ProdutoService e a interface ProdutoServiceI, é ali que ficarão suas regras de negócio, ela receberá os parametros da camada view, chamará a dao e retornará a resposta para a view.

Interface:
public interface ProdutoServiceI {

    public void salva(Produto produto);

    public List pegaTodos();

    public void remove(Produto produto);

    public Produto pegaPorId(Long id);
}

Implementação:
@Service
public class ProdutoService implements ProdutoServiceI{

    @Autowired
    private ProdutoDao    produtoDao;

    public void salva(Produto produto) {
        produtoDao.salva(produto);
    }

    public List pegaTodos() {
        return produtoDao.pegaTodos();
    }

    public void remove(Produto produto) {
        produtoDao.remove(produto);
    }

    public Produto pegaPorId(Long id) {
        return produtoDao.pegaPorId(id);
    }
}


Passo 6:


As classes da camada de pacotes view, criei mais 2 pacotes form e controller, no pacote Form eu criei uma classe que terá os dados das páginas JSP e no pacote Controller as classes que controlam as chamadas do JSP para a service (Leia Controller como Managed Bean do JSF ou as Actions do Struts).


Form:

Aqui podemos ver as anotações do Hibernate Validator.
@NotEmpty: Anotação para validar se o valor é vazio.

@NotNull: Anotação para validar se o valor é nulo.
Message: Você pode customizar as mensagens que por padrão são em inglês.

public class ProdutoForm {

    private Long id;
    @NotEmpty(message = "Valor não pode ser vazio")
    private String nome;
    @NotEmpty(message = "Valor não pode ser vazio")
    private String descricao;
    private double preco;
    @NotEmpty(message = "Valor não pode ser vazio")
    private String cor;
    @NotNull(message = "Valor não pode ser nulo")
    private Long quantidade;

    //getters and setters

}


Controller:
@Controller
@RequestMapping("/produto/**")
public class ProdutoController {

    @Autowired
    private ProdutoService    produtoService;

    private ProdutoForm        produtoForm;

    private Produto         produto;
   
    @RequestMapping("/produto/formulario")
    public ModelAndView formulario() {
        return new ModelAndView("formulario").addObject("produtoForm", new ProdutoForm());
    }
   
    @RequestMapping("/produto/adiciona")
    public ModelAndView adiciona(@Valid ProdutoForm produtoForm, BindingResult result) {
        if (result.hasErrors()) {
            return new ModelAndView("formulario").addAllObjects(result.getModel());
        }
        populaBean(produtoForm);
        produtoService.salva(produto);
        return new ModelAndView("lista").addObject("produtos", produtoService.pegaTodos());
    }

    @RequestMapping("/produto/lista")
    public ModelAndView lista() {
        return new ModelAndView("lista").addObject("produtos", produtoService.pegaTodos());
    }
   
    @RequestMapping(value = "/produto/remove", method = RequestMethod.GET)
    public ModelAndView remove(@Valid @RequestParam(value = "produto.id") long id) {
        produto = new Produto();
        produto.setId(id);
        produtoService.remove(produto);
        return new ModelAndView("lista").addObject("produtos", produtoService.pegaTodos());
    }
   
    @RequestMapping("/produto/consulta")
    public ModelAndView consulta() {
        return new ModelAndView("consulta").addObject("produtoForm", new ProdutoForm());
    }
   
    @RequestMapping("/produto/pesquisa")
    public ModelAndView pesquisa(@Valid ProdutoForm produtoForm, BindingResult result) {
        produto = produtoService.pegaPorId(produtoForm.getId());
        produtoForm = populaForm(produto, produtoForm);
        return new ModelAndView("exibeProduto").addObject("produtoForm", produtoForm);
    }
   
    private void populaBean(ProdutoForm produtoForm) {
        //implementação do método de/para
    }
   
    private ProdutoForm populaForm(Produto produto, ProdutoForm produtoForm) {
       //implementação do método de/para
    }

   //getters and setters
   
}

É aqui que acontece a atuação do Spring MVC, temos a anotação @Controller que diz que essa classe será a página que receberá os valores do formulário e depois irá passar um objeto ou redirecionar para outras páginas.


@Controller: Anotação que diz que aquela classe terá como função  transformar o dados do formulário em dados do modelo, ou seja, gerenciamento entre as camadas View e Model.

@RequestMapping: Essa anotação é onde definimos o caminho do HTTP que irá ser utilizado na nossa aplicação, sendo mapeado na classe, todas as chamadas que contém "/produto/*" serão analisadas pelo Controller.

@RequestParam(value = "produto.id"): Podemos receber tanto o Form completo como apenas o parametro que desejamos com a anotação @RequestParam como parametro do método no Controller.

Podemos também enviar e receber mais de um objeto do(para o) formulário através do ModelAndView, nos frameworks mais antigos enviávamos apenas um objeto no retorno, com o ModelAndView podemos enviar mais.
Exemplo:
return new ModelAndView("exibeProduto").addObject("produtoForm", produtoForm);
Na linha acima estamos dizendo, ModelAndView coloque na página exibeProduto o objeto produtoForm.
ou ModelAndView eu quero que na página lista você coloque a lista de produtos.

return new ModelAndView("lista").addObject("produtos", produtoService.pegaTodos());

OBS: repare que o que coloco dentro do ModelAndView("lista") é o JSP lista.jsp.
OBS 2: Spring 3 trabalha por convenção, então baseado em suas anotações, JSPs e métodos , o que está dentro do @RequestMapping é o que criará na uri o caminho que chamará sua página.
Exemplo:
@RequestMapping("/produto/consulta") chamará o JSP consulta e na url ficará: /produto/consulta

Para validar o formulário utilizamos o BindingResult, ele verifica as validações e podemos retornar a página em seu estado no momento que pegamos o erro (ao invés de salvar todos objetos em param ou hiddens, mantendo seu estado.

Para validar nos parametros do método do controller que queremos fazer a validação colocamos no objeto a anotação @Valid, por exemplo: @Valid @RequestParam(value = "produto.id") long id, verifica se o id é válido ou @Valid ProdutoForm produtoForm no caso de um formulário.
Dentro do método adicionamos:


        if (result.hasErrors()) {
            return new ModelAndView("formulario").addAllObjects(result.getModel());
        }

No retorno estamos dizendo: Hei, se houver erro ModelAndView, volta pra página e devolve os objetos que estavam populados com as respectivas mensagens de erro.

Passo 7:
Para configurar o Spring precisamos colocar alguns XMLs (acalme-se não será um caminhão de XML como era antigamente)

Dentro de WebContent -> WEB-INF -> spring econtraremos o servlet-spring.xml e o app-spring.xml

servlet-spring.xml
[?xml version="1.0" encoding="UTF-8"?]
[beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd"]

     
    [bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"]
      [property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/]
      [property name="prefix" value="/WEB-INF/jsp/"/]
      [property name="suffix" value=".jsp"/]
    [/bean]

[/beans]
ViewResolver: aqui ele irá caçar na pasta "WEB-INF/jsp/" os nossos arquivos com sulfixo ".jsp"

app-spring.xml
[?xml version="1.0" encoding="UTF-8"?]
[beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd]
       
    [mvc:annotation-driven /]
    [context:annotation-config /]
   
   [ mvc:resources location="/css/" mapping="/resources/"/]
   [ mvc:resources location="/images/" mapping="/resources/"/]
   [ mvc:resources location="/js/" mapping="/resources/"/]
       
    [context:component-scan base-package="br.com.possege.loja" /]
  
   
    [import resource="spring-servlet.xml"/]

    [bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /]
   
    [mvc:resources mapping="/resources/**" location="/resources/" /]

    [bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource"]
        [property name="basename" value="classpath:application-message" /]
        [property name="defaultEncoding" value="UTF-8" /]
        [property name="fallbackToSystemLocale" value="false" /]
    [/bean]
    
    [bean id="localeChangeInterceptor"
        class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"]
        property name="paramName" value="lang" /]
    [/bean]
    [bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.CookieLocaleResolver"]
        [property name="defaultLocale" value="pt_BR" /]
    [/bean]
 [/beans]

mvc:annotation-driven: Permite enviar as requisições das classes anotadas com @Controller.
context:annotation-config: Procura todas as classes anotadas com @PersistenceContext, @Autowired, entre outros, fazendo automaticamente a injeção de dependência.
context:component-scan: Procura todas as classes anotadas no pacote definido, assim não precisamos mapeá-las em XML, quando o scan é feito as classes são passadas por um filtro e é criada a definição em um bean para cada uma delas, quem determina a definição do bean é a anotação.
mvc:resources: Serve para o acesso GET dos arquivos estáticos, como CSS, JS, etc.
import resource: Importa o outro XML de configuração do Spring.

Passo 8:

Vamos configurar o web.xml
[?xml version="1.0" encoding="UTF-8"?]
[web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"]
[display-name]projetoSpring[/display-name]

    [servlet]
        [servlet-name]spring[/servlet-name]
        [servlet-class]org.springframework.web.servlet.DispatcherServlet[/servlet-class]
        [init-param]
            [param-name]contextConfigLocation[/param-name]
            [param-value]/WEB-INF/spring/app-config.xml[/param-value]
        [/init-param]
        [load-on-startup]1[/load-on-startup]
    [/servlet]

    [!-- Spring MVC Dispatcher Servlet --]
    [servlet-mapping]
        [servlet-name]spring[/servlet-name]
        [url-pattern]/[/url-pattern]
    [/servlet-mapping]
   
    [!-- Permitir comandos HTTP RESTfull (GET, POST, PUT, DELETE) --]
    [filter]
        [filter-name]hiddenHttpMethodFilter[/filter-name]
        [filter-class]org.springframework.web.filter.HiddenHttpMethodFilter[/filter-class]
    [/filter]

    [filter-mapping]
        [filter-name]hiddenHttpMethodFilter[/filter-name]
        [servlet-name]spring[/servlet-name]
    [/filter-mapping]

    [session-config]
        [session-timeout]10[/session-timeout]
    [/session-config]

    [!--SiteMesh --]
    [filter]
        [filter-name]sitemesh[/filter-name]
        [filter-class]com.opensymphony.sitemesh.webapp.SiteMeshFilter[/filter-class]
    [/filter]

    [filter-mapping]
        [filter-name]sitemesh[/filter-name]
        [url-pattern]/*[/url-pattern]
    [/filter-mapping]
   
    [!-- Encoding --]
    [filter]
        [filter-name]encodingFilter[/filter-name]
        [filter-class]org.springframework.web.filter.CharacterEncodingFilter[/filter-class]
        [init-param]
            [param-name]encoding[/param-name]
            [param-value]UTF-8[/param-value]
        [/init-param]
        [init-param]
            [param-name]forceEncoding[/param-name]
            [param-value]true[/param-value]
        [/init-param]
    [/filter]
    [filter-mapping]
        [filter-name]encodingFilter[/filter-name]
        [url-pattern]*[/url-pattern]
    [/filter-mapping]
   
    [error-page]
        [error-code]405[/error-code]
        [location]/erro[/location]
    [/error-page]
    [error-page]
        [error-code]500[/error-code]
        [location]/erro[/location]
    [/error-page]
    [error-page]
        [error-code]404[/error-code]
        [location]/erro[/location]
    [/error-page]
    [error-page]
        [error-code]400[/error-code]
        [location]/erro[/location]
    [/error-page]
[/web-app]

 Aqui configuramos o Spring, uma pog para permitir comandos HTTP Restfull, os filtros, a configuração do SiteMesh, Encoding e páginas de erro.

O redirecionamento de um 404, 500, etc, particularmente achei bizarro a solução que encontrei foi criar um ErroController e apontar para um JSP chamado erro.
Até cheguei abrir uma discussão no GUJ mas não obtive resposta.

ErroController:
@Controller
@RequestMapping("/")
public class ErroController {

    @RequestMapping("/**")
    public ModelAndView erro() {
        return new ModelAndView("erro");
    }
}

JSP:
[%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%]
[html]
    [head]
        [title][spring:message code="produto.erro.titulo" /][/title]
    [/head]
    [body]
        [p]
            [spring:message code="produto.erro.msg_default_erroGenerico" /]
        [/p]
    [/body]
 [/html]

Passo 9:

Configurando o SiteMesh, já adianto que não fiz CSS para esse exemplo, mas o SiteMesh é um ótimo template e já o deixei configurado.

Para configurar o SiteMesh eu criei na pasta WEB-INF o decorators.xml e uma sub pasta chamada decorators com o principal.jsp dentro.

decorators.xml
[xml]
[?xml version="1.0" encoding="UTF-8"?]
[decorators defaultdir="/decorators/principal.jsp"]

    [decorator name="principal"]
        [pattern]/*[/pattern]
    [/decorator]
   
[/decorators]
[/xml]

principal.jsp
[!DOCTYPE HTML]
[%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator"%]
[%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%]
[%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%]
[html]
    [head]
        [title][decorator:title default="Possege"/][/title]
        [meta charset="utf-8" /]
        [meta http-equiv="pragma" content="no-cache" /]
        [meta http-equiv="expires" content="-1" /]
        [meta http-equiv="cache-control" content="no-cache" /]
       
        [meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;" /]       
        [meta name="format-detection" content="telephone=no" /]

        [meta name="apple-mobile-web-app-capable" content="yes" /]
        [meta name="apple-mobile-web-app-status-bar-style" content="black" /]       
       
       
        [decorator:head /]
    [/head]
    [body]
        [div]
            [div]
                [decorator:body/]
            [/div]
        [/div]
        [script src="http://www.google.com/jsapi"][/script]
       [script type="text/javascript" src="[c:url value="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" /]"][/script]
  [/body]
[/html]

Passo 10:

Criei uma pasta na raiz do projeto chamada resources que leva meu application-message_pt_BR.properties para internacionalização.
produto.lista.titulo=Lista de Produtos

produto.novo.titulo=Novo Produto
produto.novo.nome=Nome
produto.novo.descricao=Descri\u00E7\u00E3o
produto.novo.preco=Pre\u00E7o
produto.novo.cor=Cor
produto.novo.quantidade=Quantidade

produto.consulta.titulo=Consulta Produto
produto.consulta.codigo=C\u00F3digo
produto.consulta.lista=Listar Todos Produtos
produto.consulta.consulta=Consultar

produto.exibe.titulo=Produto Encontrado
produto.exibe.codigo=C\u00F3digo:
produto.exibe.nome=Nome:
produto.exibe.descricao=Descri\u00E7\u00E3o:
produto.exibe.preco=Pre\u00E7o:
produto.exibe.cor=Cor:
produto.exibe.quantidade=Quantidade:

produto.erro.titulo=Erro
produto.erro.msg_default_erroGenerico=Ocorreu um erro.

Passo 11:

Por fim criei todos os JSPs, não vou colocá-los aqui no blog mas vou colocar algumas tags que são essênciais e úteis.

As tag libs que mais utilizei:
[%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%]
[%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%]
[%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%]
[%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%]

Tags úteis:
[spring:message code="produto.exibe.preco" /]
[c:url var="pesquisa" value="/produto/pesquisa" /]
[form:form action="${pesquisa}" id="produtoForm" modelAttribute="produtoForm" method="post"]
[form:input path="nome" /] 
[form:errors path="nome"  /]
[fmt:formatNumber value="${produtoForm.preco }" type="currency"/]
[fmt:formatDate pattern="dd/MM/yyyy" value="${produtoForm.algumaData}"/]

spring:message: Coloca as mensagens, quando code ele pega do properties, quando text você digita o texto.
c:url: Submeter a página por um link, no caso estou passando o valor para o form:form
form:form: Nosso formulário, passo a ação, id e o modelAttribute que o Controller irá mandar, posso escolher enviar por get ou post.
for:input: Criar os campos texto para digitação no formulário.
form:errors: Pegar as mensagens de erro enviadas pelo BindingResult.
fmt:formatNumber: Formatar o valor no caso em moeda, há porcentagem e outros valores.
fm:formatDate: Formatar as datas no padrão que defini.

Fiz também um remove ajax utilizando JQuery dentro da aplicação:


[a href="javascript:void(0);" onclick="remove(${produto.id}); return false;"]Remove[/a]

[script type="text/javascript" src="[c:url value="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" /]"][/script]
[script type="text/javascript"]
    function remove(id){
        $.get('remove?produto.id='+id,function(){
            $('#produtoRemove-' + id).hide();
            alert('produto removido com sucesso');
            $('#produtoRemove-' + id).remove();
        });
    }
[/script]

Bom todo o fonte disponibilizei o download no GitHub.

Espero que este tutorial o ajude nos pontos que tive problemas, a ideia era mostrar uma solução com o ótimo framework MVC que os Spring nos dá.

Aconselho também estudar VRaptor é muito parecida a forma de programar e é um excelente framework MVC.

Agradecimenos especiais para Caelum e ao Christian Reichel por ter me ajudado com as dúvidas que surgiram quando comecei a programar com Spring MVC.

Comentários são bem vindos e as referências para deixar o seu template mais bonito e mais detalhes sobre Spring MVC e o VRaptor estão abaixo:


Washington Botelho - SiteMesh
Edson Gonçalves - Spring MVC
Rodolfo Chaves - Anotações do Spring
Valdemar Junior - Spring MVC com Tiles
Spring Source - Documentação Oficial
Jérôme Jaglale - Spring MVC Fast Tutorial
NetBeans - Tutorial Spring
Mkyoung - Hello World Spring
Caelum - FJ 27 Spring Framework
Caelum - FJ 28 VRaptor
Caelum - VRaptor online


Um Feliz Natal para todos. =D

4 de dezembro de 2011

Café com Java dia 10/12


O que é Café com Java?

Organizado há algum tempo por eu e o Marky Vasconcelos com extrema ajuda do Paulo Silveira, era simplesmente um encontro para o pessoal fazer um networking. Recentemente tomou um outro formato para atender mais pessoas, com mais temas. 


Porém como mês de Dezembro tem Natal, preparativos para o Ano Novo e etc, faremos o Café com Java Especial de Natal, no Blue Pub a partir das 16:00 hrs.


A entrada será de R$ 20,00 consumiveis e o que passar disso.


Vamos falar de Java, Tecnologia, Metodologias e nos divertir nesse fim de ano.


Horário: 16:00 em diante no Pub
Site do Pub: http://www.thebluepub.com.br/
R. do Pub: Alameda Campinas, 105, travessa da paulista.
Fotos dos outros Cafés com Java: Flickr
GUJ: Link do tópico
Twitter Tag: #CafeComJava