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