For those follow closely the Java area is not new the VRaptor is growing up with good references. I work with some projects at Giran using it, and I’m so satisfied.
Knowing the framework
VRaptor is a MVC framework that works with its methods of your controller in a exposed way and RESTFul, then we can access a public method, for example, by URI: /usuario/cadastrar in an easy and intuitive way. Following is a brief list of some features:
- Dependency Injection;
- Automatic Cast;
- Converters;
- Interceptors;
- Integration with Spring, Hibernate and JPA;
- Unit Testing Facility;
- Validations;
- Redirects;
- Parametrized URI;
- And other more…
And the best of all, it’s open source, Brazilian and has a discussion list with people willing to help, beyond keep track the framework development.
Goal
Create a CRUD using the latest version of the VRaptor 3 simulating the persistence in the database.
Configuring the project:
After you download the (blank project) and decompress the file vraptor-blank-project-3.x.x.zip in your workspace, simply import the project in the Eclipse, add to your server and it will be ready to run.
The project comes with some files to run on NetBeans either, if you don’t use it, you need to keep only the folders Webcontent and src, and you can remove the other files.
Creating the controller
We’ll create a package br.com.wbotelhos.controller and in this package the IndexController. (the blank project already contains this as example)
@Resource
public class IndexController {
@Path("/")
public void index() {
}
}
All controllers should be named in the format “FolderNameController” and must be annotated with @Resource to expose the resources and make the public methods accessible by URI. In the above example the path “/” will execute the method index, in which will redirect to a page with the same name of this method, contained within a folder with the same name of the controller like this: WEB-INF/jsp/index/index.jsp. All pages should be located in WEB-INF/jsp/folder where folder’s name is equals the name of the entity.
@Path
VRaptor is a action based framework and its methods are accessed by URI. The Path annotation indicates the URI to be accessed so that we can execute a method. By convention we use it like this:
@Path("entityName/methodName"):
@Path("/user/listAll")
public void list() { }
If we had not noted the list method with @Path by default would be /user/list, but we can annotate and the Path don’t must to be the same of the method name.
Methods
The method access is execute through the most commons HTTP operations as GET, POST, PUT and DELETE which are usually combined with a CRUD.
We can make a kind of rule like:
GET – List data and access links;
POST – Save an entity;
PUT – Update an entity; and
DELETE – Delete an entity.
Even if a method has the same path it can differentiate by the execution type:
@Post
@Path("/user")
public void save() {
}
@Put
@Path("/user")
public void edit() {
}
By default method access consists of controllerName/methodName/ using @Get, and is not necessary in this case noted it, however I recommend always explicit the annotations, avoiding confuse the access method as well the path.
Creating the home page
Now we’ll create a page called index.jsp inside a folder called index containing a navigation menu:
...
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<a href="<c:url value='/usuario/novo'/>">Novo usuário</a>
...
Always use the core tag library to mount correctly URLs.
Ok! Now we can test the application. Add the project to the server and see the name will be vraptor-blank-project. This is the default name and we must use it to access the application: http://localhost:8080/vraptor-blank-project/. But we want to use another name, then I’ll show you how to change, because just rename the project’s folder wont solve the problem.
Changing the name and deploy context-root of the application:
- View the hidden files in your project and you’ll see the folder .settings;
- Open the file org.eclipse.wst.common.component in a text editor;
- Change the property “deploy-name” to change the visually name of the application deploy;
- Change the property “context-root” to change the access context of the application;
- Choose the name that you think better and refresh de project and do the deploy again. I chose the name “vraptor”.
Creating the registration page (novo.jsp)
We’ll create 1 Controller + 1 DAO + 1 folder containing the pages.
...
<form action="<c:url value='/usuario'/>" method="post">
Nome: <input type="text" name="usuario.nome" value="${usuario.nome}"/>
Senha: <input type="text" name="usuario.senha" value="${usuario.senha}"/>
E-mail: <input type="text" name="usuario.email" value="${usuario.email}"/>
<input type="submit" value="Salvar"/>
</form>
...
The form action access is /usuario using POST.
The parameter name set the attributes of the usuario.
The parameter value, retrieves the object usuario and use its to populate the fields.
Creating the model
private Integer id;
private String nome;
private String senha;
private String email;
// Getters e Setters.
Remember that the attribute name="usuario.nome" access the Usuario and both the object and the attributes must exist.
Creating the controller
According the form’s action, we’ll create a method accessed by URI /usuario using POST.
@Post
@Path("/usuario")
public void salvar() { }
There is still the object on which we set the values of the form fields. Unlike useBean of the JSP, the values are set in an object present in the actual controller, making it easer to use. This object just like any other must be present as an argument of the invoked method, more less like this:
@Post
@Path("/usuario")
public void salvar(Usuario usuario) { }
With the object filled from form, we can persist it.
Working with redirection:
If we try to save the user a 404 error will be throwed, because we’ll be redirected to a page with the same name of the method: WEB-INF/jsp/usuario/salvar.jsp, but if we wants to keep the method name, but to redirect to the page listagem.jsp we should do the redirect in the end of the method.
We have two redirects types:
Results.logic()
Redirect to a method of any controller. We must pass the controller class to the method:
result.use(Results.logic()).redirectTo(UsuarioController.class).listagem();
UsuarioController.class can be replaced by getClass() indicating the class itself, or any other.
Results.page()
Redirect to a page. Indicate the path of the page where we want to go. Its can be redirect or foward:
result.use(Results.page()).forward("WEB-INF/jsp/listagem.jsp");
For more information about the difference between the two types of redirect, read the article Repitam Comigo Redirect Não é Forward by Rafael Ponte. (:
In recent versions of VRaptor you can use these methods briefly:
result.redirectTo(this).listagem(); // Its can be a fowardTo
See the method already understood that listagem is a method, then is used the required Results. No longer need to specify if will be logic or page. It was also simplified the use of getClass() to simply this, representing the current controller.
Although there are several other types of Results, this is what we need for while.
Returning to the code, our controller should looks like this:
@Resource
public class UsuarioController {
private Result result;
@Get
@Path("/usuario/novo")
public void novo() {
}
@Post
@Path("/usuario")
public void salvar(Usuario usuario) {
// Method save here.
result.forwardTo("WEB-INF/jsp/usuario/listagem.jsp");
}
}
We must put the path of the pages starting from folder WEB-INF.
Dependency Injection
Certainly if we use the method save would be throwed a NullPointerException, because the object result is not instantiated. A great facility we have is dependency injection in which the object is injected through the class constructor and the framework control what is necessary for it.
public UsuarioController(Result result) {
this.result = result;
}
So now we have our result instantiated. Likewise if we want to work with other controllers, just inject it too.
For best practices recommended to create interfaces with the signatures of the classes and pass it on injection instead of concrete classes, making it easier for unit tests, and having access to an external interface for your application.
Creating the DAO
We need a persistence layer and this layer must be annotated with @Component. Components are instances of classes used by your application to perform tasks or store states in different ways. The DAO is a classic example.
For our example, only to facilitate didactically, we’ll use the class as session scope so that we can maintain the state and simulate the database. The existing scopes are:
RequestScoped: avaliable only during the request;
ApplicationScoped: avaliable only one by application;
SessionScoped: will be the same during a http session; and
PrototypeScoped: instantiated whenever requested.
@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++);
}
}
Listing data: (listagem.jsp)
To be able to list the information saved, we must pass this data through the requestin which the VRaptor encapsulates, as well as response, making this task easier. The Result will be used to include the data as following:
result.include("usuarioList", usuarioList);
To get the list from request we can use EL as so: ${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>
...
In the above case, we’ve included the list in the request, but by default the return is already placed in the request. The return name is formed by the type of the list plus “List”: public List list(); would be captured as ${userList}public User search(); would be captured as ${user}.
Let’s add a method that returns the list in DAO:
public List<Usuario> loadAll() {
return usuarioList;
}
And in the controller too:
@Get
@Path("/usuario")
public List<Usuario> listagem() {
return usuarioDao.loadAll();
}
In the above method I’m using the default and letting it put the usuarioList on resquest as well the redirect to listagem.jsp.
Creating logic to edit
Let’s add a column in the listing table to put a link to edit:
...
<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>
...
As our method is accessed by PUT we’ll have to do the form use it, since basically there’s “only” support as GET and POST in most of times. We should create a hidden field (that does not appear on the screen) overwriting the attribute _method with the value PUT, attribute that indicates which method the form will execute. The id attribute of object Usuario also is hidden to indicate which user we want to edit.
In our controller we have the following method:
@Put
@Path("/usuario")
public void editar(Usuario usuario) {
Usuario entity = usuarioDao.loadById(usuario);
result.redirectTo(this).novo(entity);
}
Note that after we recover the user we want to edit and call the method novo(), because it will redirect to our form, but must take the user queried to the screen, then I pass him to the method that has been refactored to include it in the request:
@Get
@Path("/usuario/novo")
public void novo(Usuario usuario) {
result.include("usuario", usuario);
}
In DAO we’ll also add a method that finds the user by 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) is used here only because we don’t use a real database and the method edit actually remove and then save the user.
Looking for a user with the same ID passed to the method, then immediately remove it from the list:
private void removerItem(Usuario usuarioDelete) {
if (usuarioList.remove(usuarioDelete)) {
id--;
}
}
If we give up to save the user, its will be removed anyway, but the example is symbolic.
Creating the exclusion logic
The delete method follows the same logic of the edit, but don’t need to return the data to the screen.
Let’s add a column in the listing table and put a link to the delete method:
<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>
Let’s create a method annotated with @Delete in the controller:
@Delete
@Path("/usuario")
public void remover(Usuario usuario) {
usuarioDao.remover(usuario);
result.redirectTo(this).listagem();
}
And then we pass to DAO remove the object:
public void remover(Usuario usuario) {
Usuario usuarioDelete = null;
for (Usuario item : usuarioList) {
if (item.getId() == usuario.getId()) {
usuarioDelete = item;
break;
}
}
removerItem(usuarioDelete);
}
The above method search the user by ID, keep it and remove it from the list. In a real situation the object must be removed from the database according to the ID passed.
Conclusion
In a easily way we created a CRUD without worry about requisitions, data conversion and redirects. We can do that and more with VRaptor. And if you worry about the the visuals work just take a look at jQuery UI.
For those who wish to lear more about VRaptor, follow the discussion list, take a look at official documentation available in Portuguese either or continue to follow the articles here at Blog.
See this code running on: http://moviecollection.com.br
Project link:
http://github.com/wbotelhos/iniciando-com-vraptor-3