Iniciando com VRaptor 3

Olá pessoal,

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 três projetos consideravelmente grandes na Giran utilizando-o, e posso dizer que estou super satisfeito e admirado.

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 for Eclipse WTP) e descompactar o arquivo vraptor-blank-project-3.x.x.zip na sua workspace, iremos renomear os arquivos classpath-example e project-example para .classpath e .project respectivamente.

Se o projeto for aberto, será criado ambos os arquivos iniciados com ponto (ocultos), então terá de deletá-los e fazer o procedimento citado. No Unbuntu basta apertar (Ctrl + H) para ver os arquivos ocultos.

Baixei o blank project mais recente e reparei que já estão ambos os arquivos configurados e podem ser utilizados, porém as libs ficarão quebradas, pois estão configuradas com o caminho do ${USER} do lucascs. Então é só clicar com o botão no projeto -> ir em properties -> Java Build Path e na aba Libraries remover as libs com um ‘x’ vermelho indicando referência quebrada.

Agora precisaremos configurar o web.xml para enxergar o caminho do nosso pacote principal. No meu caso com.wordpress.wbotelhos, logo todos as outras pastas: Model, Controller, Dao, Repository etc.. estarão abaixo deste pacote e é ele que devemos mapear:

<context-param>
    <param-name>br.com.caelum.vraptor.packages</param-name>
    <param-value>com.wordpress.wbotelhos</param-value>
</context-param>

Criando o controller

Criaremos o pacote com.wordpress.wbotelhos.controller e dentro um IndexController. (o blank project já contém o mesmo)

@Resource
public class IndexController {

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

}

Todos os nossos controllers terão o nome NomePastaController e serão anotados com @Resource para expor seus recursos e tornar os métodos públicos acessíveis atavés da URI. No exemplo acima o caminho “/” irá executar o método index, tendo como regra o redirecionamento para uma página de mesmo nome do método, contido dentro de uma pasta de 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() {
    // O método não tem que ter o mesmo nome.
}

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 acessár 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.

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 nome quanto os atributos do objeto 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 que formos trabalhar devem estar presentes no construtor 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 ao 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 dos dois tipos leia este artigo do Rafael Ponte. (:

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) {
	result.use(Results.page()).forward("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 iremos colocar a classe como escopo de sessão para que possamos manter o estado dos nossos dados. 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 VRator 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.use(Results.logic()).redirectTo(getClass()).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 meu usuário consultado para a tela, logo passo o mesmo 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 através do request.
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ó da 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

  • leohackin

    dezembro 7th, 2009

    Bem bacana o post! ;)

  • Rafael Ponte

    dezembro 7th, 2009

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

    Parabéns.

  • Gabriel

    dezembro 7th, 2009

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

  • Andre

    dezembro 7th, 2009

    Bom post, parabens!

  • Uchoaaa

    dezembro 10th, 2009

    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

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

  • Bino

    dezembro 17th, 2009

    Excelente Washington. Entendi alguns comandos. ;]

    PS: Te envie um email do gmail.

    Abraços.

  • João Batista Ladchuk

    março 8th, 2010

    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

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

  • Guevara

    março 29th, 2010

    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

    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.

  • Bruce

    maio 21st, 2010

    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

    maio 21st, 2010

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

Leave a Comment

* are Required fields