WebService WSDL e RESTful com Java
Publicado; 10/09/2009 Arquivado em: Apache CXF, Façade, Interface, Java, RESTful, Spring, WSDL 4 Comments »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:
- Modelo do projeto dentro do Eclipse
- Configurações main do projeto
- Argumentos para o projeto
- Classpath do projeto
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!





Link para download do Source do projeto: http://www.4shared.com/file/131567203/33b1617a/ServiceSample.html
Valeu pessoal! 2:30h da manhã, preciso dormir!
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!
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.
Olá Diego, estudo um WebService, aqui pra empresa e sua abrodagem foi bastante feliz. Gostei muito, de verdade! obrigado