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.

2 comentários:

Rodrigo Lazoti disse...

Bem legal o post, parabens.
Estou usando Vraptor em todos os projetos novos onde trabalho e so tenho elogios.

Uma dica, voce pode usar o proprio vraptor para fazer DI, no exemplo do seu service basta receber o DAO no construtor. ;)

[]s
Rodrigo

Bregaida disse...

Hehehe valeu Rodrigo, bem pensado, eu fiz o construtor apenas para instanciar, poderia mesmo ter colocado o DAO como parametro dele.

Obrigado.

[]sss