WebService WSDL e RESTful com Java


Neste post demonstrarei como criar um WebService WSDL e RESTful na mesma aplicação de modo simplificado, utilizando Spring e Apache CXF, que na nova versão traz suporte total para JSR 311 de forma clara e via annotations. Usaremos também o Maven2 para gerenciar nosso projeto, assumo que você já tem prévio conhecimento, recomendo este tutorial para entender o Maven2 e também como configura-lo. Também presumo que você tenha conhecimento sobre os dois protocolos, porém, caso queira aprender mais sobre leia: WSDL e RESTful.

Bom, cabe a você também decidir qual tipo de serviço sua aplicação deve prover, tanto o WSDL como o RESTful tem suas vantágens e desvantágens e é necessário saber qual delas prover para seu cliente e se preciso prover as duas soluções, com o Apache CXF isso é possível, cheguei a essa conclusão pois a pouco tempo, participei de um projeto onde inicialmente províamos acesso a um WebService WSDL para o cliente, porém foi necessário disponibilizar também o acesso ao RESTful e como utilizávamos o Apache CXF não tivemos que mudar nada no projeto e nenhúm cliente foi afetado, pois a interface do projeto continuava a mesma.

Bom, vamos ao nosso projeto.

Primeiro precisamos mapear nossas dependências no Maven2, segue o pom do nosso projeto:

<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>com.wordpress.dchohfi</groupId>
	<artifactId>ServiceSample</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<developers>
		<developer>
			<id>dchohfi</id>
			<name>Diego Chohfi</name>
			<email>diegochohfi@hotmail.com</email>
			<url>http://dchohfi.wordpress.com</url>
		</developer>
	</developers>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<configuration>
					<scanIntervalSeconds>10</scanIntervalSeconds>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-bundle</artifactId>
			<version>2.2.3</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-beans</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>2.5.6</version>
		</dependency>
	</dependencies>
</project>

O Apache CXF utiliza dentro dele o Spring como dependência, removemos o mesmo para utilizar uma versão atualizada do SpringFramework. Adicionei ao nosso POM também o Plugin do Jetty que iremos utilizar para debugar nossa aplicação dentro do Eclipse. Utilizei também um plugin para compilar nosso projeto o Maven Compiler Plugin dizendo qual versão do Java estamos utilizando e qual o encoding do nosso projeto (não esqueça de mudar o encoding no seu projeto do Eclipse para UTF-8). Agora nossa dependências estão prontas e nosso projeto configurado.

Nosso projeto possui uma interface seguindo o padrão de projeto Façade, essa interface será exposta como nosso WebService, de maneira simplificada, o Façade torna o projeto mais fácil de entender e usar.

Interface:

package com.wordpress.dchohfi.service;

import com.wordpress.dchohfi.exception.ClienteException;
import com.wordpress.dchohfi.model.entity.Cliente;

@Path("/")//caminho onde o serviço REST fica disponibilizado, seguido pelo @Path de cada parametro
@Produces({"application/xml", "application/json"})//tipos de retorno que o nosso REST pode produzir
@WebService//definimos aqui que essa interface é um WebService WSDL
public interface Service {

    @GET//tipo do metodo REST
    @Path("/cliente")//caminho do método
    @WebMethod(operationName="getClientes")//nome do metodo no WSDL
    public Collection<Cliente> getClientes();
    
    @GET
    @Path("/cliente/{id}/")//aqui passamos uma ID para obter um Cliente especifico
    @WebMethod(operationName="getCliente")
    //o ID do cliente é mapeado pelo PathParam para o REST e recebe o nome dado pelo WebParam para o WSDL.
    public Cliente getCliente(@PathParam("id") @WebParam(name="id")int id) throws ClienteException;
}

Verifique como é simples desenvolver uma interface para disponibilizar acesso tanto para RESTful como para WSDL, utilizamos annotations para dizer ao CXF qual o tipo de retorno que o nosso WebService vai prover tanto para o RESTful quanto para o WSDL, no mesmo código! Na implementação da nossa interface não iremos precisar de praticamente nenhuma configuração adicional para o Apache CXF saber o que deve fazer.

Implementação:

package com.wordpress.dchohfi.service;

@WebService(endpointInterface = "com.wordpress.dchohfi.service.Service")
public class ServiceImpl implements Service {

	private ClienteDAO clienteDao;
	
	@Override
	public Cliente getCliente(int id) throws ClienteException {
		return clienteDao.getCliente(id);
	}

	@Override
	public Collection<Cliente> getClientes() {
		return clienteDao.getClientes();
	}

	public void setClienteDAO(ClienteDAO clienteDao) {
		this.clienteDao = clienteDao;
	}

	public ClienteDAO getClienteDao() {
		return clienteDao;
	}
}

Veja que tivemos apenas uma anotação indicando ao Apache CXF qual o Endpoint que ele deve procurar para fazer o mapeamento do XML para o WSDL. Criei também uma interface de ClienteDAO, faremos uma implementação simples do DAO, mas você pode integrá-lo ao Hibernate, por exemplo, ou qualquer outra forma para obter acesso ao seus dados, até mesmo outro WebService. É sempre bom trabalhar com interfaces para garantir o baixo acoplamento das suas classes garantindo uma escalabilidade maior, você não precisa saber o que a classe faz em si, deste que você tenha a assinatura dela.

Implementação Simples do ClienteDAO:

package com.wordpress.dchohfi.model.dao.simple;
//você pode implementar a classe ClienteDAO da forma que desejar
public class ClienteSimpleDAO implements ClienteDAO {

	private Collection<Cliente> clientes;
	
	@Override
	public Cliente getCliente(int id) throws ClienteException {
		for (Cliente cliente : clientes) {
			if(cliente.getId() == id)
				return cliente;
		}
		throw new ClienteException("Cliente com id "+id+" não encontrado!");
	}

	@Override
	public Collection<Cliente> getClientes() {
		return this.clientes;
	}

	public void setClientes(Collection<Cliente> clientes) {
		this.clientes = clientes;
	}
}

Verifique que na implementação lançamos uma Exception do tipo ClienteException caso nenhúm usuário seja encontrado com aquela ID, o Apache CXF ainda não consegue mapear para o REST a exception lançada e responder adequadamente a requisição ao usuário, precisamos então ter um ExceptionMapper para dizer ao Apache CXF o que fazer caso aquela Exception seja lançada.

ClienteExceptionMapper:

package com.wordpress.dchohfi.exception;

public class ClienteExceptionMapper implements ExceptionMapper<ClienteException>{

	@Override
	public Response toResponse(ClienteException arg0) {
		return Response.status(Status.BAD_REQUEST).build();
	}
}

Verifique como é simples, você pode dizer qual o Status do Response gerado pelo método, ainda é possível colocar uma mensagem para mostrar para o usuário o que aconteceu com a requisição que ele fez. Agora precisamos configurar o Spring juntando tudo o que fizemos, considere isso como um pequeno tutorial desse incrivel framework. Leia os comentários dentro do XML e qualquer dúvida poste um comentário e caso seja necessário farei um breve tutorial sobre Spring.

Configuração do Spring:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cxf="http://cxf.apache.org/core" 
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-2.5.xsd


http://cxf.apache.org/core


http://cxf.apache.org/schemas/core.xsd


http://cxf.apache.org/jaxws


http://cxf.apache.org/schemas/jaxws.xsd


http://cxf.apache.org/jaxrs


http://cxf.apache.org/schemas/jaxrs.xsd">

	<!-- Carrega as configurações presentes nos jars do Apache CXF -->
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
	
	<!-- Endpoint WSDL para o Apache CXF -->
	<!-- Dizemos o endereço, o ID do serviço, em qual bean ele depende -->
	<jaxws:endpoint id="service" depends-on="serviceImpl"
		implementor="#serviceImpl" address="/service" />

	<!-- Mapeamento do REST para o Apache CXF -->
	<jaxrs:server id="myService" address="/rest">
		<jaxrs:serviceBeans>
			<ref bean="serviceImpl" />
		</jaxrs:serviceBeans>
		<!-- Aqui declaramos o nosso Mapper para a Exception -->
		<jaxrs:providers>
			<ref bean="ClienteExceptionMapper" />
		</jaxrs:providers>
	</jaxrs:server>
	
	<!-- Referencia ao bean Mapper da nossa Exception -->
    <bean id="ClienteExceptionMapper" class="com.wordpress.dchohfi.exception.ClienteExceptionMapper"/>

	<!-- Bean que implementa o endpoint do nosso webservice -->
	<bean id="serviceImpl" class="com.wordpress.dchohfi.service.ServiceImpl">
		<property name="clienteDao" ref="clienteDAO" />
	</bean>
	
	<!-- Bean criado apenas para demonstrar um simples DAO -->
	<bean id="clienteDAO"
		class="com.wordpress.dchohfi.model.dao.simple.ClienteSimpleDAO">
		<property name="clientes">
			<list>
				<ref bean="cliente1" />
				<ref bean="cliente2" />
				<ref bean="cliente3" />
			</list>
		</property>
	</bean>
	
	<!-- Cria 3 clientes simples com algumas informações para demonstração -->
	<bean id="cliente1" class="com.wordpress.dchohfi.model.entity.Cliente">
		<property name="id" value="1" />
		<property name="nome" value="Diego Chohfi" />
		<property name="endereco" value="Rua Fagundes Varela" />
	</bean>
	<bean id="cliente2" class="com.wordpress.dchohfi.model.entity.Cliente">
		<property name="id" value="2" />
		<property name="nome" value="Cliente 2" />
		<property name="endereco" value="Rua Cliente 2" />
	</bean>
	<bean id="cliente3" class="com.wordpress.dchohfi.model.entity.Cliente">
		<property name="id" value="3" />
		<property name="nome" value="Cliente 3" />
		<property name="endereco" value="Rua Cliente 3" />
	</bean>
</beans>

Considero que a configuração feita em XML é pequena visto o tamanho da aplicação que temos em tão pouco tempo, normalmente a configuração do Spring é bem extensa em projetos de grande porte, então quem já trabalha com Spring não terá problema para entender o contexto. Agora precisamos configurar o servlet dentro do web.xml para nossa aplicação.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

	id="services" version="2.5">

	<context-param>
		<param-name>webAppRootKey</param-name>
		<param-value>cxf.rest.example.root</param-value>
	</context-param>
	
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>
	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

Bom, aqui estamos dizendo ao Spring o que fazer com a nossa aplicação e estamos carregando o Apache CXF para rodar dentro de um servidor de aplicações escolhido por você, aqui você pode ficar tranquilo pois a configuração é padrão.

Neste momento temos nossa aplicação funcionando e pronta para ser colocada para rodar.  Lembra do plugin do Jetty dentro do POM do Maven2? Iremos utilizá-lo agora. Demonstrarei rapidamente como fazer as configurações para debugar seu projeto dentro do Eclipse.

Faça as seguintes configurações, dentro de Run Configurations no Eclipse crie um novo Java Application e siga as instruções como nas imagens:

Acredito que com isso você consiga subir seu projeto dentro do Eclipse e debugar sem problemas sua aplicação, você poderá consumir sua aplicação REST a partir do link http://localhost:8080/ServiceSample/rest/cliente/1/ por exemplo, deixo como dica utilizar um complemento para o FireFox o RESTClient, onde você pode consumir aplicações RESTful sem problema e analizar com clareza os resultados obtidos e para consumir WebServices WSDL recomendo o soapUI que possui diversas funcionalidades para testar e sobrecarregar seu servidor e verificar o número de acessos simultâneos que ele aguenta.

É importante ressaltar que você pode escolher se deseja prover acesso aos dois WebServices dentro da aplicação para o cliente, cabe a você decidir o que é melhor.

Bom, aqui acaba nosso tutorial que acabou ficando maior do que eu esperava, tomara que eu não tenha me perdido em alguma parte e caso algo tenha ficado confuso ou passado desapercebido deixe um comentário que tentarei esclarecer.

Espero que quem leia goste do material e por favor, comente, é importante. Quero continuar este pequeno projeto adicionando funcionalidades como integração do Spring com o Hiberante entre outras coisas, para realmente ter um tutorial completo para uma aplicação robusta.

Qualquer dúvida ou sugestão, novamente, deixe um comentário. Obrigado!

About these ads

4 Comentários on “WebService WSDL e RESTful com Java”

  1. dchohfi disse:

    Link para download do Source do projeto: http://www.4shared.com/file/131567203/33b1617a/ServiceSample.html

    Valeu pessoal! 2:30h da manhã, preciso dormir!

  2. César Caetano Neto disse:

    Parabéns pelo artigo! Gostei muito!

    Só gostaria de fazer uma pequena ressalva. Pelo menos no meu ambiente de desenvolvimento o pom definido por você não foi capaz de baixar as dependências transitivas de org.apache.adbera. Isso porque essa dependência ainda está incubada e não está presente no repositório padrão do maven acredito. Para tanto eu apenas adicionei um repositório ao pom:

    apache.incubating
    Apache Incubating Repository
    http://people.apache.org/repo/m2-incubating-repository

    Espero ter ajudado!
    Abraço!

    • dchohfi disse:

      Obrigado César! É isso mesmo, preferi não adicionar os repositorios ao pom do projeto pois eu deixo todos no arquivo de configuração central do mvn. Porém é necessário mesmo adicionar esse repositório, obrigado novamente.

  3. Wesley disse:

    Olá Diego, estudo um WebService, aqui pra empresa e sua abrodagem foi bastante feliz. Gostei muito, de verdade! obrigado


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

Você está comentando usando sua conta WordPress.com. Sair / Mudar )

Imagem do Twitter

Você está comentando usando sua conta Twitter. Sair / Mudar )

Foto do Facebook

Você está comentando usando sua conta Facebook. Sair / Mudar )

Conectando a %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.