Iniciando com VRaptor 3

Atualizado em 20 de Novembro de 2010.

Pra quem esta acompanhando a área de Java não é novidade alguma que o VRaptor esta explodindo de sucesso e boas referências. Hoje trabalho em vários projetos na Giran utilizando-o, e posso dizer que estou super satisfeito.

Conhecendo a framework

O VRaptor é um framework MVC que trabalha com os métodos de seus controllers de forma exposta e de maneira RESTFul, ou seja, conseguimos acessar um método público, por exemplo, através da URI: /usuario/cadastrar de forma fácil e intuitiva. A seguir há uma lista resumida de algumas características:

- Injeção de Dependência;
- Cast automático;
- Conversores;
- Interceptadores;
- Integração com Spring, Hibernate e JPA;
- Facilidade de Testes de Unidade;
- Validações;
- Redirecionamentos;
- URI parametrizada;
- Entre outras mais…

E o melhor de tudo, é open source, brasileira e tem uma lista de discussão com pessoas dispostas a ajudar, além de poder acompanhar o desenvolvimento da framework.

Objetivo

Criar um CRUD de Usuário utilizando a última versão do VRaptor 3 simulando a persistência no banco.

Configurando o projeto:

Depois de fazer o download do (blank project) e descompactar o arquivo vraptor-blank-project-3.x.x.zip na sua workspace, basta importar o projeto no Eclipse, adicioná-lo ao seu servidor e ele estará pronto para rodar.

O projeto vem com alguns arquivos para ser executado no NetBeans também, caso você não o utilize, precisará manter apenas as pastas Webcontent e src, podendo remover o restante.

Criando o controller

Criaremos o pacote br.com.wbotelhos.controller e dentro deste pacote um IndexController. (o blank project já contém um como exemplo)

@Resource
public class IndexController {

    @Path("/")
    public void index() {
    }

}

Todos os nossos controllers terão o nome no formato NomePastaController e serão anotados com @Resource para expor seus recursos e tornar os métodos públicos acessíveis atavés da URL. No exemplo acima o caminho “/” irá executar o método index, tendo como regra o redirecionamento para uma página com o mesmo do método, contida dentro de uma pasta com o mesmo nome do controller WEB-INF/jsp/index/index.jsp. Todas as nossas páginas ficarão dentro de WEB-INF/jsp/pasta, onde pasta é o nome da entidade na qual queremos trabalhar.

@Path

O VRaptor é uma framework Action Based, logo os métodos são acessados via ações da URI, e estas devem ser únicas. A anotação Path indica a URI a ser acessada para que possamos executar um método. Por convenção o utilizado é o seguinte: @Path("nomeEntidade/nomeMetodo"):

@Path("/usuario/listarTodos")
public void listar() { }

Se não tivéssemos anotado o método listar com o @Path, por padrão seria /usuario/listar, mas podemos anotar e o Path não precisa ter relação com o nome do método.

Methods

O acesso a um método é feito através das operações HTTP mais comuns como o GET, POST, PUT e DELETE que normalmente são combinadas a um CRUD.

Podemos fazer um esqueminha do tipo:
GET – Listar dados e acessar links;
POST – Salvar uma entidade;
PUT – Atualizar uma entidade; e
DELETE – Excluir uma entidade.

Mesmo que um método tenha o mesmo Path ele pode se diferenciar através do método executado:

@Post
@Path("/usuario")
public void salvar() {
}

@Put
@Path("/usuario")
public void editar() {
}

Por padrão o acesso de um método é composto por nomeController/nomeMetodo via @Get, não sendo necessário neste caso anotar, porém recomendo sempre explicitar, evitando confudir o método de acesso assim como o caminho.

Criando a página inicial

Agora criaremos uma página chamada index.jsp dentro de uma pasta chamada index contendo um menu para a navegação:

...
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<a href="<c:url value='/usuario/novo'/>">Novo usuário</a>
...

Utilize sempre a tag URL da biblioteca core para que as URLs sejam montadas corretamente.

Pronto! Podemos testar nossa aplicação. Adicione o projeto ao servidor e verá que o nome estará vraptor-blank-project. Esse é o nome default e é através dele que iremos acessar a aplicação: http://localhost:8080/vraptor-blank-project/. Mas queremos usar outro nome, então irei mostrar como trocar, pois renomear a pasta do projeto apenas não irá resolver.

Trocando o nome de deploy e o context-root da aplicação:

- Visualize os arquivos ocultos e dentro da pasta do projeto conterá uma outra chamada .settings;
- Abra o arquivo org.eclipse.wst.common.component em um editor de texto;
- Altere a tag “deploy-name” para alterar o nome visualmente ao escolher a aplicação para deploy;
- Altere a propriedade “context-root” para alterar o contexto de acesso da aplicação;
- Faça o deploy e verá as alterações, podendo escolher o nome que achar melhor, no caso escolhi “project”.

Criando a página de cadastro do usuario (novo.jsp)

Criaremos 1 Controller + 1 DAO + 1 Pasta contendo as páginas.

...
<form action="<c:url value='/usuario'/>" method="post">
    Nome: <input type="text" name="usuario.nome" value="${usuario.nome}"/><br/>
    Senha: <input type="text" name="usuario.senha" value="${usuario.senha}"/><br/>
    E-mail: <input type="text" name="usuario.email" value="${usuario.email}"/><br/>
    <input type="submit" value="Salvar"/>
</form>
...

A action do formulário acessa /usuario via POST.
Através do parâmetro name, setamos os valores nos atributos do objeto usuario.
Através do parâmetro value, recuperamos o objeto usuario e utilizamos seus atributos para popular os campos.

Criando o modelo do usuário

private Integer id;
private String nome;
private String senha;
private String email;

// Getters e Setters.

Lembre-se que atributo name="usuario.nome" acessa de fato o objeto Usuario e tanto o objeto quanto seus atributos devem existir.

Criando o controller do usuário

De acordo com a ação do nosso formulário, criaremos um método acessado pela URI /usuario via POST.

@Post
@Path("/usuario")
public void salvar() { }

Ainda falta o objeto no qual seto os valores dos campos do formulário. Diferente do useBean do JSP os valores são setados em um objeto presente no próprio controller, falicitando assim o uso do mesmo. Este objeto assim como qualquer outro que formos trabalhar devem estar presentes como argumento do método invocado, podendo ser mais do que um:

@Post
@Path("/usuario")
public void salvar(Usuario usuario) { }

Com o objeto alimentado a partir do formulário podemos persistí-lo.

Trabalhando com redirecionamento:

Ao tentarmos salvar um usuário teremos um erro 404, pois seremos redirecionados para uma página com o mesmo nome do método: WEB-INF/jsp/usuario/salvar.jsp, mas como queremos manter o nome do método, porém sermos redirecionados para a página listagem.jsp teremos de fazer o redirecionamento no final do método.

Temos dois tipos de redirecionamentos:

Results.logic()

Redirecionamento para um método qualquer dos controllers. Devemos passar a classe do controller que iremos utilizar:

result.use(Results.logic()).redirectTo(UsuarioController.class).listagem();

UsuarioController.class pode ser substituido por getClass() indicado a própria classe, ou qualquer outro Controller.class.

Results.page()

Redirecionamento para uma página. Indicamos o caminho da página na qual queremos ser redirecionados (redirect) ou encaminhados (forward):

result.use(Results.page()).forward("WEB-INF/jsp/listagem.jsp");

Para maiores informações sobre a diferença entre os dois tipos leia este artigo do Rafael Ponte. (:

Nas últimas versões do VRaptor é possível usar estes métodos de forma resumida:

result.redirectTo(this).listagem(); // Poderia ser um fowardTo

Veja que já é entendido que listagem é um método, logo por debaixo dos panos é usado o Results necessário. Não é mais preciso especificar se será usado logic ou page. Também foi simplificado o uso do getClass() para simplesmente this, representando o próprio controller.

Ainda existem vários outros tipos de Results, mas por hora é isso que precisamos saber.

Voltando ao código, então teríamos nosso controller assim:

@Resource
public class UsuarioController {

    private Result result;

    @Get
    @Path("/usuario/novo")
        public void novo() {
    }

    @Post
    @Path("/usuario")
    public void salvar(Usuario usuario) {
        // Método que salva.
        result.forwardTo("WEB-INF/jsp/usuario/listagem.jsp");
    }
}

Colocamos o caminho das páginas a partir da pasta WEB-INF.

Injeção de dependência

Certamente se formos utilizar o método salvar seria lançado um NullPointerException, pois o objeto result não esta instanciado. Uma grande facilidade que temos é a injeção de dependência na qual injetamos o objeto através do contrutor da classe e a framework trata de controlar o que é necessário para intanciá-lo.

public UsuarioController(Result result) {
    this.result = result;
}

Então agora já temos nosso result instanciado. Da mesma forma se quiséssemos trabalhar com outros controllers, por exemplo, apenas injetaríamos os mesmos.

Por boas práticas recomenda-se criar interfaces com as assinaturas de suas classes e na injeção passá-las em vez das classes concretas, falicitando deste modo os testes de unidades, além de ter uma interface externa de acesso para sua aplicação.

Criando o Dao

Precisamos da camada de persistência e esta camada deve ser anotada como @Component. Componentes são instâncias de classes utilizadas pela sua aplicação para executar tarefas ou armazenar estados em diferentes formas. O Dao é um exemplo clássico.

Para o nosso exemplo, apenas para facilitar didáticamente, iremos colocar a classe como escopo de sessão para que possamos manter o estado dos nossos dados e simular o banco. Os escopos existentes são:

RequestScoped: estará disponível apenas durante a requisição;
ApplicationScoped: será apenas um por aplicação;
SessionScoped: será o mesmo durante uma http session; e
PrototypeScoped: instanciado sempre que requisitado.

@SessionScoped
@Component
public class UsuarioDao {

    private List<Usuario> usuarioList = new ArrayList<Usuario>();
    private Integer id = 1;

    public void salvar(Usuario usuario) {
        usuarioList.add(usuario);
        usuario.setId(id++);
    }

}

Listando os dados: (listagem.jsp)

Para podermos listar nossas informações salvas, devemos passar estes dados através do request que o VRaptor encapsula, assim como o response, tornando esta tarefa mais fácil. O Result será utilizado para incluir os dados da seguinte forma:

    result.include("usuarioList", usuarioList);

Para recuperarmos a lista do request podemos utilizar EL da seguinte forma: ${usuarioList}.

...
<table>
    <thead>
        <tr>
	    <th>Nome</th>
            <th>Senha</th>
	    <th>E-mail</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach items="${usuarioList}" var="item">
            <tr>
                <td>${item.nome}</td>
                <td>${item.senha}</td>
                <td>${item.email}</td>
            </tr>
        </c:forEach>
    </tbody>
</table>
...

No caso acima nós que incluímos a lista no request, porém por padrão o retorno já é colocado no request. O nome do retorno é formado pelo tipo da lista caso tenha mais o nome “List”: public List listar(); seria capturado como ${usuarioList} e se for apenas um objeto basta capturar o nome começando em minísculo: public Usuario consultar(); seria capturado como ${usuario}.

Vamos adicionar o método que retorna a lista no Dao:

public List<Usuario> loadAll() {
    return usuarioList;
}

E também no Controller:

@Get
@Path("/usuario")
public List<Usuario> listagem() {
    return usuarioDao.loadAll();
}

No método acima estou usando o default e deixando ele colocar o usuarioList no resquest assim como fazer o redirecionamento para listagem.jsp.

Criando a lógica para editar

Vamos adicionar uma coluna na tabela da listagem de dados para conter um link para a edição:

...
<td>
    <form action="<c:url value='/usuario'/>" method="post">
        <input type='hidden' name='_method' value='put'/>
        <input type='hidden' name='usuario.id' value='${item.id}'/>
        <input type="submit" value="Editar"/>
    </form>
</td>
...

Como nosso método editar é acessado via PUT teremos de fazer o formulário utilizá-lo, já que só há o suporte como GET e POST hoje em dia. Para isso criamos um campo hidden (que não aparece na tela) sobrescrevendo o atributo _method com o valor PUT, atributo no qual indica qual método que o formulário executará. O atributo id do objeto Usuario também esta escondido servindo apenas para indicar qual usuário queremos editar.

No nosso controller teremos o seguinte método:

@Put
@Path("/usuario")
public void editar(Usuario usuario) {
    Usuario entity = usuarioDao.loadById(usuario);
    result.redirectTo(this).novo(entity);
}

Repare que após recuperarmos o usuário que queremos editar fazemos uma chamada ao método novo(), pois é ele que redireciona para nosso formulário, porém preciso levar o usuário consultado para a tela, logo passo ele para o método que foi refatorado para incluir o usuário no request:

@Get
@Path("/usuario/novo")
public void novo(Usuario usuario) {
    result.include("usuario", usuario);
}

No nosso Dao também iremos adicionar o método que consulta o usuário pelo ID:

public Usuario loadById(Usuario usuario) {
    Usuario usuarioDelete = null;

    for (Usuario item : usuarioList) {
        if (item.getId() == usuario.getId()) {
            usuarioDelete = item;
            break;
        }
    }

    removerItem(usuarioDelete); // *
    return usuarioDelete;
}

* removerItem(usuarioDelete) foi utilizado aqui apenas por não usármos um banco de dado real e o editar ser na verdade um remover e logo em seguida um salvar.

Procuro o usuário com o ID passado e o guardo, logo em seguida removo ele da lista:

private void removerItem(Usuario usuarioDelete) {
    if (usuarioList.remove(usuarioDelete)) {
        id--;
    }
}

Se desistirmos de salvar o usuário lá no formulário o mesmo já terá sido removido, mas o exemplo é simbólico.

Criando a lógica de exclusão

O método para deletar segui o mesmo raciocínio do editar, só não tendo que retornar os dados para a tela.
Vamos adicionar uma coluna na tabela da listagem de dados para conter um link para a remoção:

<td>
    <form action='<c:url value="/usuario"/>' method="post">
        <input type='hidden' name='_method' value='delete/>
        <input type='hidden' name='usuario.id' value='${item.id}'/>
        <input type="submit" value="Excluir"/>
    </form>
</td>

Vamos criar um método anotado com @Delete no controller:

@Delete
@Path("/usuario")
public void remover(Usuario usuario) {
    usuarioDao.remover(usuario);
    result.use(Results.logic()).redirectTo(getClass()).listagem();
}

E então repassaremos para o nosso Dao remover o objeto:

public void remover(Usuario usuario) {
    Usuario usuarioDelete = null;

    for (Usuario item : usuarioList) {
        if (item.getId() == usuario.getId()) {
            usuarioDelete = item;
            break;
        }
    }

    removerItem(usuarioDelete);
}

No método acima busco pelo ID, ao achar guardo o objeto e o removo da lista. Em uma situação real removeríamos o objeto do banco de dados de acordo com o ID passado.

Conclusão

De forma fácil conseguimos criar um CRUD sem nos preocupar com requisições, conversões de dados e redirecionamentos. Podemos fazer isso e muito mais com o VRaptor. E se esta preocupado com a parte visual é só dar uma olhada no jQuery UI.

Para quem quiser se aprofundar mais no VRaptor, acompanhe a lista de discussão, de uma olhada na documentação oficial disponível em português ou continue acompanhando os artigos aqui no Blog.

Veja este código funcionando em: http://moviecollection.com.br

Link do projeto:

http://github.com/wbotelhos/iniciando-com-vraptor-3

Share
  • Roberto Fonseca

    maio 23rd, 2011

    Responder

    Bom, eu sou novo no VRator e estou gostando muito, mas tenho algumas dúvidas tão bobas e não acho referência alguma para elas.

    Estou usando Vraptor e Hibernate, criei Usuario e Endereco, e fiz ligação entre eles OneToOne, mas no formulário eu utilizei

    usuario.nome e usuario.endereo.rua por exemplo, mas não consegui ainda uma maneira de fazer a inserção dos endereços… mas não teve errro.. como fazer, devo passar para o controller Usuario e Endereço?

    • wbotelhos

      maio 23rd, 2011

      Responder

      Oi Roberto,

      Que bom que esta usando e gostando o VRaptor. (:

      Para salvar o Endereco quando você salvar o Usuario, você precisa anotar o Endereco como CascadeType.MERGE, assim o Hibernate salva a classe Todo e também suas partes.

      @OneToOne(cascade = CascadeType.MERGE)
      private Endereco endereco;
      
  • wbotelhos

    maio 21st, 2010

    Responder

    Fala @Bruce,

    Já tentou dar uma olhada no artigo JPA e VRaptor 3?
    Acho que é a melhor forma de trabalhar com o banco de dados, a não ser que esteja conectando na unha via JDBC.

    Se aquele artigo não te ajudar, só falar qual é sua dúvida que te ajudo sim. (:

  • Bruce

    maio 21st, 2010

    Responder

    Olá amigo primeiramente parabéns pela dica!
    Me ajudou bastante com framework
    E estou precisando de uma ajuda para me conectar com banco da dados local, pode me ajudar?

  • Guevara

    março 29th, 2010

    Responder

    Olá amigo!
    Parabéns pelo post!
    Estou com um problema aqui, sabe como anoto as interfaces? Pois eu fiz uma classe de interface Funcionario com getNome, getIdFuncionario e talz, depois fiz uma classe Gerente com os atributos, getters e setters, coloquei lá:

    public interface Funcionario {
    public Long getIdFuncionario();
    	public String getNome();
    	public String getTelefone();
    	public String getEmail();
    }
    

    E classe Gerente:

    public class Gerente implements Funcionario {
    private Long idFuncionario;
    	private String nome;
    	private String telefone;
    	private String email;
           // getters e setters
    }
    
    public interface UserLogin {
           public String getLogin();
           public String getNome();
           public String getSenha();
    }
    

    Como eu faço essa anotação? Criei uma classe para autenticar Login chamado UserLogin que quero usar para que seja implementada tanto pelos funcionarios como pelos clientes, ou seja, funcionarios logarão e realizarão tarefas diferentes dos clientes.
    Quando vou anotar a classe Gerente ao invés de aparecer a anotação @Entity me aparece @EJB. =/

    Poderia me ajudar nessa?

    Abraço!

    • wbotelhos

      março 30th, 2010

      Responder

      Fala Guevara,

      Uma interface não precisa ser anotada pelo VRaptor. Ela é utilizada normalmente.
      Se você já tem sua interface UserLogin que creio eu ser um Controller, DAO, Repository ou BO você só precisa fazer suas classes implementá-las.

      Só tome cuidade para não usar interface para fazer um tipo de herança quando deveria utilizar classe abstratas, pois você esta citando classes modelos (entidade), pois só elas que utilizam @Entity.

      Não vejo motivo de você ser forçado a utilizar a anotação @EJB, basta removê-la e colocar @Entity.
      Se alerte também pelo fato de existir o @Entity do pacote persistence e do pacote hibernate.

      Outra coisa que percebi no seu código é as assinaturas de métodos retornam apenas valores dos seus atributos. Isso não me parece ser legal, pois isso deve estar na sua entidade e a partir de uma instância da mesma você terá isso, não sendo parte da sua interface.

  • João Batista Ladchuk

    março 8th, 2010

    Responder

    Olá amigo primeiramente parabéns pela dica!
    Me ajudou bastante com framework
    E estou precisando de uma ajuda para me conectar com banco da dados local, pode me ajudar?

    • wbotelhos

      março 8th, 2010

      Responder

      Fala João,

      Você pode estar lendo o artigo JPA e VRaptor 3, lá tem exatamente um exemplo de conexão com o banco de dados utilizando o Hibernate. Se ainda assim tiver dúvidas, ficarei grato em ajudar.

      Abraço e obrigado pelos parabéns. (:

  • Bino

    dezembro 17th, 2009

    Responder

    Excelente Washington. Entendi alguns comandos. ;]

    PS: Te envie um email do gmail.

    Abraços.

  • Uchoaaa

    dezembro 10th, 2009

    Responder

    Legal o post, parece bem completo. Já usei algumas vezes o VRaptor 2 e gostei muito. Depois fui pro Rails e ficou difícil voltar pro Java ;)
    Mas agora tou em um projeto que devo usar o VRaptor. Vou olhar com calma teu post para ver se vale a pena migrar do VRaptor 2 pro 3.

    []s

    • wbotelhos

      dezembro 10th, 2009

      Responder

      Fala Rafael,

      Não sei se compensa migrar toda uma aplicação pronta de VRaptor 2 para a versão 3, já que os conceitos mudaram bastante, porém se você for iniciar um projeto, não tenha dúvida, pois a versão 3 trás muitas funcionalidades novas além de correções e facilidades.

      Só acompanhar o blog que irei focar bastante no VRaptor.

      Abraço. (:

  • Andre

    dezembro 7th, 2009

    Responder

    Bom post, parabens!

  • Gabriel

    dezembro 7th, 2009

    Responder

    Parabens pelo post, Washington. Bem completo e didático. Show de bola.

  • Rafael Ponte

    dezembro 7th, 2009

    Responder

    Muito bom o post. Alias, bem completo. Para quem está começando com VRaptor3 este post é uma mão-na-roda.

    Parabéns.

  • leohackin

    dezembro 7th, 2009

    Responder

    Bem bacana o post! ;)

Leave a Comment

* are Required fields