Criando um projeto com RestEasy + CDI + Maven

Sempre me interessei em estudar REST e arquitetura de sistemas Action Based. Já desenvolvi vários projetinhos utilizando Vraptor, por ser desenvolvido pela Caelum e bastante utilizado pela comunidade brasileira.

Dessa vez, devido a uma nova necessidade de projeto, resolvi me aventurar nos estudos sobre o RestEasy e posso afirmar que é framework incrível.

Mas vamos ao que interessa…

Dando continuidade também nos estudos sobre CDI, resolvi desenvolver um projeto de exemplo utilizando RestEasy, CDI e Maven seguindo o mesmo cenário do caixa eletrônico.

Primeiro vamos dar uma olhada na estrutura do projeto que eu criei.

Estrutura do Projeto

estrutura-projeto

Para que o nosso projeto funcione é necessário definir as dependências do RestEasy e CDI, como podemos ver no arquivo pom.xml a seguir.

pom.xml

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>br.com.mews</groupId>
	<artifactId>resteasy-cdi</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>resteasy-cdi Maven Webapp</name>
	<url>www.mews.com.br</url>

	<dependencies>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<!-- CDI -->
		<dependency>
			<groupId>org.jboss.weld.servlet</groupId>
			<artifactId>weld-servlet</artifactId>
			<version>1.1.10.Final</version>
		</dependency>

		<!-- JAX-RS -->
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxrs</artifactId>
			<version>2.3.5.Final</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxb-provider</artifactId>
			<version>2.3.5.Final</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jettison-provider</artifactId>
			<version>2.3.5.Final</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-cdi</artifactId>
			<version>2.3.5.Final</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>resteasy-cdi</finalName>
	</build>
</project>

Devido a utilização de um interceptor que foi criado para exemplificar o uso, precisamos configurar o arquivo beans.xml.

beans.xml

<beans 
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

	<interceptors>
		<class>br.com.mews.interceptor.SecurityOperation</class>
	</interceptors>

</beans>

Precisamos fazer também algumas configurações obrigatórias no arquivo web.xml.

web.xml

<web-app id="WebApp_ID" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>resteasy-cdi</display-name>

	<!-- Resteasy -->
	<servlet>
		<servlet-name>RestServlet</servlet-name>
		<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<context-param>
		<param-name>resteasy.servlet.mapping.prefix</param-name>
		<param-value>/rest</param-value>
	</context-param>
	<context-param>
		<param-name>resteasy.scan</param-name>
		<param-value>true</param-value>
	</context-param>
	<servlet-mapping>
		<servlet-name>RestServlet</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>

	<!-- Weld -->
	<listener>
		<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
	</listener>

	<resource-env-ref>
		<resource-env-ref-name>BeanManager</resource-env-ref-name>
		<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
	</resource-env-ref>

	<context-param>
		<param-name>resteasy.injector.factory</param-name>
		<param-value>org.jboss.resteasy.cdi.CdiInjectorFactory</param-value>
	</context-param>

</web-app>

Aqui vem a parte divertida da implementação do JAX-RS que permite a abstração do uso de servlets criando simples classes com a anotação @Path que é responsável por definir um serviço e qual o caminho que estará disponível. Esse caminho é referente a URL.

No nosso exemplo o ATMService estará disponível a partir do caminho /atm. Além disso temos os métodos referentes as possíveis ações que podem ser executadas por um cliente (browser, mobile, etc..), ou seja, através de uma chamada HTTP (GET, POST, DELETE, PUT…).

Então vamos analistar o método deposit().

O método deposit() tem duas anotações interessantes: @GET que é um método HTTP e também o @Path, que define o caminho utilizado executar esse método através de um chamada HTTP. No @Path podemos ver que existem um parâmetro a ser recebido. Sim, através da anotação @PathParam podemos ter facilmente o valor que foi enviado por parâmetro.

ATMService.java

package br.com.mews.service;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import org.slf4j.Logger;

import br.com.mews.ATM;
import br.com.mews.BankAccount;

@Path("/atm")
public class ATMService {

	@Inject
	private ATM atm;
	@Inject
	private BankAccount bankAccount;
	@Inject
	private Logger logger;

	@GET
	@Path("deposit/{value}")
	public String deposit(@PathParam("value") int value) {
		atm.deposit(value);
		return String.valueOf(bankAccount.getBalance());
	}

	@GET
	@Path("take/{value}")
	public String take(@PathParam("value") int value) {
		atm.take(value);
		return String.valueOf(bankAccount.getBalance());
	}

	@GET
	@Path("balance")
	public String balance() {
		return String.valueOf(bankAccount.getBalance());
	}
}

Para testar o no serviço de caixa eletrônico, criei um simples arquivo index.jsp com dois botões para realizar Saque e Depósito.

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<script type="text/javascript" src="<c:url value="/js/jquery-1.8.0.min.js"/>"></script>
</head>
<body>
	<div id="wrapper">
		<strong><span>SALDO ATUAL:</span></strong>
		<span id="saldo"></span><br><br>
		<span >Valor da operação:</span>
		<input type="text" id="valorOperacao">
		<input type="button" value="Depositar" onclick="operation('deposit')">
		<input type="button" value="Sacar" onclick="operation('take')"><br><br>
		<strong><span>LOG:</span></strong><br>
		<span id="log"></span>
	</div>

	<script type="text/javascript">

	function operation(type){
		var value = $("#valorOperacao").val();
		var url = "rest/atm/";
		url = url + type + "/" + value;
		log("Chamando URL: " + url);
		$.ajax({
		  url: url,
		}).done(function ( result ) {
			log("Resultado da URL: " + result);
			$("#saldo").html(result);
		});
	}

	function log(value){
		var log = $("#log").html();
		log = log + "<br>" + value;
		$("#log").html(log);
	}

	$(document).ready(function() {
		var url = "rest/atm/balance";
		log("Chamando URL: " + url);
		$.ajax({
		  url: url,
		}).done(function ( result ) {
			log("Saldo inicial: " + result);
			$("#saldo").html(result);
		});
	});

	</script>

</body>
</html>

Meu projeto está sendo utilizado no Tomcat7 e após subir o ambiente podemos ter o seguinte resultado:

utilizando-sistema

Bom… existem muita coisa para se falar sobre REST e também mais especificamente sobre o RestEasy.  Em breve colocarei mais informações sobre isso.

O projeto pode ser baixado no GitHub.

Referências

http://kristallstadt.de/index.php?id=601

http://docs.jboss.org/resteasy/docs/3.0.2.Final/userguide/html_single/index.html

5 responses to “Criando um projeto com RestEasy + CDI + Maven

  1. William Neves, antes de mais nada, parabéns pelo artigo. Existe alguma mudança para que seu projeto rode no JBoss AS 7.1.1 ? Pois tentei e deu o seguinte erro:

    08:25:20,945 ERROR [org.apache.catalina.core.StandardContext] (MSC service thread 1-3) Context [/resteasy-cdi-master] startup failed due to previous errors: java.lang.IllegalArgumentException: Duplicate context initialization parameter resteasy.injector.factory
    at org.apache.catalina.core.StandardContext.addParameter(StandardContext.java:2186) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.web.deployment.JBossContextConfig.processWebMetaData(JBossContextConfig.java:261) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.jboss.as.web.deployment.JBossContextConfig.applicationWebConfig(JBossContextConfig.java:169) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.startup.ContextConfig.start(ContextConfig.java:417) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:182) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.web.deployment.JBossContextConfig.lifecycleEvent(JBossContextConfig.java:162) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:115) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:3790) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.web.deployment.WebDeploymentService.start(WebDeploymentService.java:90) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_25]
    at java.lang.Thread.run(Thread.java:724) [rt.jar:1.7.0_25]

    08:25:20,950 ERROR [org.apache.catalina.core.StandardContext] (MSC service thread 1-3) Context [/resteasy-cdi-master] startup failed due to previous errors
    08:25:20,962 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-3) MSC00001: Failed to start service jboss.web.deployment.default-host./resteasy-cdi-master: org.jboss.msc.service.StartException in service jboss.web.deployment.default-host./resteasy-cdi-master: JBAS018040: Failed to start context
    at org.jboss.as.web.deployment.WebDeploymentService.start(WebDeploymentService.java:95)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_25]
    at java.lang.Thread.run(Thread.java:724) [rt.jar:1.7.0_25]

    08:25:21,168 INFO [org.jboss.as.server] (DeploymentScanner-threads – 2) JBAS015870: Deploy of deployment “resteasy-cdi-master.war” was rolled back with failure message {“JBAS014671: Failed services” => {“jboss.web.deployment.default-host./resteasy-cdi-master” => “org.jboss.msc.service.StartException in service jboss.web.deployment.default-host./resteasy-cdi-master: JBAS018040: Failed to start context”}}
    08:25:21,188 INFO [org.jboss.weld.deployer] (MSC service thread 1-3) JBAS016009: Stopping weld service for deployment resteasy-cdi-master.war
    08:25:21,241 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015877: Stopped deployment resteasy-cdi-master.war in 73ms
    08:25:21,242 INFO [org.jboss.as.controller] (DeploymentScanner-threads – 2) JBAS014774: Service status report
    JBAS014777: Services which failed to start: service jboss.web.deployment.default-host./resteasy-cdi-master: org.jboss.msc.service.StartException in service jboss.web.deployment.default-host./resteasy-cdi-master: JBAS018040: Failed to start context

    08:25:21,249 ERROR [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads – 1) {“JBAS014653: Composite operation failed and was rolled back. Steps that failed:” => {“Operation step-2” => {“JBAS014671: Failed services” => {“jboss.web.deployment.default-host./resteasy-cdi-master” => “org.jboss.msc.service.StartException in service jboss.web.deployment.default-host./resteasy-cdi-master: JBAS018040: Failed to start context”}}}}

    Obrigado pela atenção.

  2. @carlos… esse projeto desenvolvido com tomcat 7, não cheguei a testar com JBoss. Sei que existem bastante diferenças entre esses dois webcontainers. Nesse caso não posso lhe ajudar.

Leave a Reply

Your email address will not be published. Required fields are marked *