VRaptor

Construindo uma Aplicação Web com Java 8 e VRaptor 4 no Wildfly

por Rubens Saraiva

Há pouco mais de dois meses acompanhamos o lançamento do novo Java 1.8, cheio de novidades e novas APIs. Da mesma forma, e quase na mesma época, chegou a versão 4 do VRaptor, framework brasileiro que tem sido cada vez mais utilizado não só no Brasil, como em outros lugares do mundo.

O VRaptor 4 foi totalmente modificado para usar o CDI em sua engine de Injeção de Dependências. Isso torna o framework totalmente aderente à plataforma Java Enterprise Edition (JavaEE).

Neste post irei mostrar como construir uma aplicação VRaptor 4, usando recursos do novo Java 8 e rodando no WildFly 8, o novo aplication server da JBoss que implementa Java EE 7. Em relação ao banco de dados, a aplicação usará o datasource padrão do Wildfly que usa o HSQLDB Database.

O código da aplicação completa está disponível aqui.

Para começar, crie um projeto Web com Maven utilizando sua IDE preferida. Abra o arquivo de configuração do Maven, pom.xml e inclua as dependências contidas no arquivo abaixo.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.rsaraiva.labs</groupId>
    <artifactId>events</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>events</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>br.com.caelum</groupId>
            <artifactId>vraptor</artifactId>
            <version>4.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>br.com.caelum.vraptor</groupId>
            <artifactId>vraptor-javatime</artifactId>
            <version>4.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.5.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

A primeira dependência é a do novo VRaptor 4, versão final. Na sequência, incluimos o plugin vraptor-javatime que faz o VRaptor reconhecer todos os tipos da nova api de datas. Depois vem a dependência da JSTL para trabalharmos nas JSPs, o hibernate, a lib do CDI e pro fim, a lib do Java EE 7. Na tag plugins, definimos que queremos utilizar Java 8 na construção da aplicação.

Agora vamos criar nosso modelo - uma simples entidade para representar Eventos.

@Entity
public class Event implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private LocalDate date;

    private String description;

    public Event() { }

    public Event(String description, LocalDate date) {
        this.description = description;
        this.date = date;
    }

    // gets and sets
}

Veja que o atributo date é do tipo LocalDate, classe da nova api de Datas do Java 8.

Mas para isso ser possível, é necessário criar um Converter da JPA, já que a JPA 2.1 ainda não suporta os tipos da nova Date and Time API.

@Converter(autoApply = true)
public class LocalDatePersistenceConverter implements AttributeConverter<LocalDate, Date> {

    @Override
    public Date convertToDatabaseColumn(LocalDate entityValue) {
        return (entityValue == null) ? null : Date.valueOf(entityValue);
    }

    @Override
    public LocalDate convertToEntityAttribute(Date databaseValue) {
        return databaseValue.toLocalDate();
    }
}

Este converter deve ser registrado no persistence.xml conforme abaixo.

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="default" transaction-type="JTA">
    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
    <class>com.rsaraiva.labs.vraptor4.jpa.LocalDatePersistenceConverter</class>
    <properties>
      <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>

Agora vamos criar um Service que sabe criar um novo evento e listar todos os eventos. Perceba a integração com JavaEE pelo uso das anotações @Stateless e @PersistenceContext.

@Stateless
public class EventService {
    
    @PersistenceContext
    private EntityManager em;

    public void persist(Event event) {
        em.persist(event);
    }
    
    public List<Event> findAll() {
        return em.createQuery("select e from Event e order by e.description").getResultList();
    }
}

Agora usaremos o CDI para injetar o service no controller do VRaptor.

@Controller
public class EventController implements Serializable {

    @Inject
    private EventService service;

    @Inject
    private Result result;

    @Get("/events")
    public void event() {
        result.include("events", service.findAll());
    }

    @Post
    public void add(Event event) {
        service.persist(event);
        result.redirectTo(this).event();
    }
}

Nosso controller possui dois métodos. O primeiro usa EventService para consultar todos os eventos cadastrados e inclui a lista no contexto da JSP. O segundo recebe o post do formulário da JSP e persiste o evento no banco de dados.

Enfim, devemos criar a jsp com a lista de eventos e o formulário de cadastro.

<\%@page contentType="text/html" pageEncoding="UTF-8"\%>
<\%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" \%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Events</title>
    </head>
    <body>
        <h1>Events</h1>
        <table>
            <c:forEach items="${events}" var="item">
                <tr>
                    <td>${item.id}</td>
                    <td>${item.description}</td>
                    <td>${item.formattedDate}</td>
                </tr>
            </c:forEach>
        </table>
        <br/>
        <form action="${linkTo[EventController].add}" method="post">
            <span>Description: </span>
            <input type="input" name="event.description"/>
            <span>Date </span>
            <input type="input" name="event.date"/>
            <button>Add</button>
        </form>
    </body>
</html>

Cookbook originalmente postado em rsaraiva.com