Programador ajudante e aprendiz da comunidade open source.

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 NomePasta*Controller* 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
...

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 artigos 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.

Link do projeto:

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

  1. Cedulio Cezar 15 Abr 2014 08:30

    Olá Washington.

    Parabéns pelo post foi de grande ajuda pois estou iniciando com web e vraptor.

    Comentários são um dos motores para que as pessoas continuem escrevendo, sei disso pois tenho um blog sobre desenvolvimento Android.

    Muito obrigado.

  2. Reuben Felipe 12 Mai 2012 07:50

    Botelho, estou precisando de um crud completo que tenha um objeto e dentro deste objeto tenha uma lista de objetos. Não estou conseguindo fazer isto funcionar quando vou alterar, sempre trabalhei com o mentaway e estou com dificuldades para mostrar isto na jsp. Fv me responder por email se possível. Agradeço a gentileza.

  3. Roberto Fonseca 23 Mai 2010 16:09

    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?

  4. Bruce 21 Mai 2010 13:05

    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?

    1. Washington Botelho autor 21 Mai 2010 18:56

      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. (:

  5. Guevara 29 Mar 2010 15:19

    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!

    1. Washington Botelho autor 30 Mar 2010 09:48

      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.

  6. João Batista Ladchuk 7 Mar 2010 22:21

    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?

    1. Washington Botelho autor 7 Mar 2010 23:06

      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. (:

  7. Bino 17 Dez 2009 11:18

    Excelente Washington. Entendi alguns comandos. ;]

    PS: Te envie um email do gmail.

    Abraços.

  8. Uchoaaa 9 Dez 2009 22:30

    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

    1. Washington Botelho autor 10 Dez 2009 08:21

      Fala Uchoaaa,

      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. (:

  9. Andre 7 Dez 2009 12:18

    Bom post, parabens!

  10. Gabriel 7 Dez 2009 11:31

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

  11. Rafael Ponte 7 Dez 2009 10:48

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

    Parabéns.

  12. leohackin 7 Dez 2009 09:29

    Bem bacana o post! ;)

Em resposta:
(cancelar)
Formate seu código utilizando Markdown.