BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles VRaptor MVC Framework; Powerful Simplicity

VRaptor MVC Framework; Powerful Simplicity

When working with Java for the web, there are many possible MVC action based frameworks. VRaptor is one of those. Its new 4th version is CDI 1.1 based. This article will take you on a tour through the framework's principles and version novelties.

All you need to do to start your first controller on VRaptor is to annotate your class with @Controller, and the framework will count on its URL and JSP conventions so you can use as little configurations as possible. This is an example:

@Controller
public class UserController {
public void list() { //... }
}

Introduction to the conventions

VRaptor's URL convention is controllerName/methodName, therefore the list method will be reachable through user/list. Note the Controller suffix is not considered on the route.

The JSP dispatches follow another useful convention. It is quite similar to the URL convention: after the controller method execution, VRaptor will look for the JSP on the

WEB-INF/jsp/controllerName/methodName.jsp.

In our example, it will be

WEB-INF/jsp/user/list.jsp.

All your controller's public methods will be mapped following these conventions, and will answer to requests regardless of the used HTTP verb.

You can also limit access to a controller method according to the verb you choose, using the @Get, @Post, @Put or @Delete annotations.

Should you choose not to use the URLs convention, you can also add a String parameter to these annotations changing the URL route that will reach them, and if you would like to customize de URL for an HTTP verb unrestricted method, you can use the @Path annotation.

@Get("any/other/url")
public void list() { //... }

Getting parameters

You can receive parameters on your controller methods, and VRaptor will attempt to populate those values. That will work with simple values, such as a long on this search method:

@Get
public void search(long id) { //... }

If the request has any parameter called id, i.e., with the same name as the method parameter, VRaptor will try converting the parameter to the expected type. This method could be accessed through the user/search?id=1 URL, or you could even use a parameterized URL:

@Get("user/search/{id}")
public void search(long id) { //... }

In this case, the example URL would be user/search/1.

That's a start but we can also get more complex objects from the view, as seen on this add method that gets an User:

@Post
public void add(User user) { //... }

The form.jsp that will call that method should look like this:

<form action="user/add" method="post">
      <input type="text" name="user.name"/>
      <input type="text" name="user.email"/>
      <input type="submit"  value="Create user">
</form>

A request parameter example for this form could be:

user.name = Rodrigo Turini
user.email = rodrigo.turini@caelum.com.br
...

Customizing the result

In order to make objects accessible on the JSP, we can simply return them on the controller method:

@Get
public List<User> list(){
       return userDao.list();
}

Since we are returning a List<User>, the variable name on the view will be ${userList}. If our return type was a single User object, the view variable would be ${user}. There is yet another way objects can be sent to the view, by using the specialist interface: Result. We can have this bean injected to our controller and use its include method:

@Controller
public class UserController {

       @Inject private Result result;
       @Inject private UserDao userDao;

       @Get
       public void list(){
              List<User> list = userDao.list();
              result.include(list);
              result.include("users", list);
       }
}

The code above includes the same list twice for the sake of explaining both options and their differences. The first include will make a variable called ${userList} available on the JSP, just like the method return. The second include names the object, thus it will be accessible through ${users}.

There are many other Result methods that can help us deal with views. Check how simple it is to return this object list serialized to JSON:

@Get
public void jsonList(){
       List<User> users = userDao.list();
       result.use(json()).from(users).serialize();
}

The json method comes from the Results class, which provides us with methods for the most common result types, such as xml, html, jsonp. There is also a representation method, that will behaves accordingly to the request's accept format.

Easily configurable

Everything we have seen so far is also easily configurable. Because your classes are CDI managed beans, we can specialize any VRaptor component -- and those are neatly encapsulated. For example, in order to change the default rendered view, or to change the location VRaptor will look for it, all you have to do is to specialize the DefaultPathResolver:

@Specializes
public class CustomPathResolver extends DefaultPathResolver {

   @Override
   protected String getPrefix() {
       return "/root/folder/";
   }
}

We could change the URL conventions likewise, overriding the PathAnnotationRoutesParser.

VRaptor integration with the JPA

Another interesting aspect of the CDI integration is how simple it gets to manage external classes to your project. Integrating VRaptor with the JPA, for instance, is a quite trivial process. You can write an EntityManager producer that looks like this:

public class EntityManagerCreator {

       @Inject private EntityManagerFactory factory;

       @Produces @RequestScoped
       public EntityManager getEntityManager() {
              return factory.createEntityManager();
       }

       public void destroy(@Disposes EntityManager em) {
               if (em.isOpen()) {
                       em.close();
               }
       }
}

That's it. Now any bean in your application can be injected with an instance of this object:

public class UserDao {
        @Inject private EntityManager em;

        public void add(User user) {
               em.getTransaction().begin();
               em.persist(user);
               em.getTransaction().commit()
        }

       // ...
}

We can also create a simple interceptor to have an open transaction in view. An example of it would be:

@Intercepts
public class JPATransactionInterceptor {

        @Inject private EntityManager manager;

        @AroundCall
        public void intercept(SimpleInterceptorStack stack) {
               EntityTransaction transaction = manager.getTransaction();
               transaction.begin();
               stack.next();
               transaction.commit();

        }
}

Notice that the intercept method, annotated with @AroundCall, receives a SimpleInterceptorStack as a parameter. Calling the next() method will dispatch to the controller, therefore we need to open the transaction before it and commit the transaction after it.

And an approach that is as simple as the former is to support the @Transactional annotation, and to apply that interception only on methods annotated with it. All you need to do is to add an accepts method to the interceptor:

@Accepts
public boolean accepts() {
       return method.containsAnnotation(Transactional.class);
}

That's is! The add method on the UserController is an example:

@Controller
public class UserController {

       @Inject private Result result;
       @Inject private UserDao userDao;

       @Get
       public void list() {
              List<User> list = userDao.list();
              result.include(list);
              result.include("users", list);
       }

       @Post @Transactional
       public void add(User user) {
              userDao.add(user);
       }
}

And now UserDao methods can simply delegate to EntityManager.persist:

public void add(User user) {
       em.persist(user);
}

Adding plugins

Those and many other features are already implemented and distributed as VRaptor4 plugins. The interceptors and producers mentioned above were placed on vraptor-jpa and vraptor-hibernate plugins, and all we need to do to use them is to add their jar (or even configure your favorite dependency manager) in our project. No extra configuration is needed!

As long as the plugin has a beans.xml file, CDI will manage its classes and make them available for being injected on VRaptor. Because creating extensions is so easy, community participation has been great and many plugins have been created. Some of them can be found on the framework documentation in this page.

Digging deeper into VRaptor usage with JavaEE

If you are in an application server, the work with JPA and transaction control will become even simpler. You can inject your EntityManager using @PersistenceContext instead of CDI’s @Inject. So you can use javax.transaction.Transactional annotation to control your methods’  transactions, i.e:

@Controller
public class UserController {

       @Inject private Result result;
       @Inject private UserDao userDao;

       @Get
       public void list(){
              List<User> list = userDao.list();
              result.include("users", list);
       }

       @Post @Transactional
       public void add(User user){
              userDao.add(user);
       }

       @Post @Transactional
       public void remove(User user){
              userDao.delete(user);
       }
}

Or if you want, instead of annotate your methods, you can simply annotate your VRaptor’s Controller with @Transactional, just like this:

@Controller @Transactional
public class UserController {

       @Inject private Result result;
       @Inject private UserDao userDao;

       // remaining code
}

This is possible because your VRaptor’s Controller, as well as all other classes you want to, are CDI managed-beans.

Hands-on learning VRaptor4

You can learn more about the framework in a very hands-on fashion, checking out open source projects built on VRaptor4. Mamute, a Q&A engine, is one good example that takes benefit from 4th version features, as you can see in the project's repository. There is also vraptor-music-jungle, the framework's test application and the provided blank-project, a pre-configured base project to give you a quick start.

More about VRaptor4

You can find more information about the framework in its documentation: start with the 1 minute and the 10 minute guides, check the migration from previous versions tutorial, and browse through the community written cookbooks. Should you want to study further, also read more about the CDI 1.1 specification and features, that seamless integrate with VRaptor's core.

About the Author

Rodrigo Turini studied Computer Science and works as a developer and instructor at Caelum in Brazil. He is one of the leaders behind VRaptor 4th version and contributes to several other open-source projects.

Rate this Article

Adoption
Style

BT