Desenvolvendo um web service com Apache CXF, Apache Camel e Spring

Veja neste artigo como desenvolver e publicar um web service utilizndo o framework Apache CXF em conjunto com o Spring e o Apache Camel.

Nesse artigo vamos explorar o framework Apache CXF e como criar e publicar um WebService simples de forma prática e rápida utilizando o Spring framework e Apache Camel para aumentar nossa produtividade.

O Apache CXF é um framework de integração que dá suporte a criação de serviços no padrão WebService. O Apache Camel é um framework que implementa patterns de integração. Juntos, eles são uma combinação poderosa para publicação e consumo de serviços. É o que veremos nesse artigo, além de usar a simplicidade do framework Spring para configuração e execução dos testes.

O WSDL que utilizaremos é bem simples e oferece apenas uma operação que retorna obrigatoriamente uma resposta de sucesso ou um erro. Operações que trabalham requisição / resposta de forma síncrona são ditas que seguem o MEP (message exchange pattern) Request / Reply. Erros são comumente chamados de Fault. A definição completa do arquivo WSDL pode ser encontrada no em HelloWorldService.wsdl.

Agora, vamos ao trecho do nosso projeto maven. O projeto completo e outros arquivos você poderá consultar no repositório github para esse artigo.

<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf</artifactId> <version>${camel.version}</version> </dependency>
Listagem 1. Dependências especiais do nosso projeto

Esse artefato é o único necessário para acrescentarmos o suporte ao Apache Camel e sua integração com o CXF. O maven se encarregará de adicionar ao nosso projeto as demais dependências do Camel e CXF compatíveis.

Como estamos utilizando o framework Spring para configurar a criação dos componentes utilizados no nosso projeto, teremos um arquivo XML que configura o contexto descrito na Listagem 2.

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xsi:schemaLocation=" http://camel.apache.org/schema/spring http://camel.apache.org/schema/ spring/camel-spring.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/ camel-cxf.xsd http://www.springframework.org/schema/beans http://www.springframework.org/ schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd" > <bean id="routeBuilder" class="br.com.devmedia.helloworldservice.HelloWorld ContextBuilder"/> <bean id="pojoHelloWorldService" class="br.com.devmedia.helloworldservice. PojoHelloWorldService" /> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <camel:routeBuilder ref="routeBuilder"/> </camelContext> <cxf:cxfEndpoint id="soapMessageEndpoint" serviceClass="br.com.devmedia.helloworldservice.wsdl.DigaOlaPortType" xmlns:s="http://helloworldservice.devmedia.com.br/wsdl/" address="http://localhost:9292/HelloWorldService" wsdlURL="HelloWorldService.wsdl" endpointName="s:HelloWorldService_Port" serviceName="s:HelloWorldService" > </cxf:cxfEndpoint> </beans>
Listagem 2. Configuração do framework Spring

A primeira configuração que nos chama a atenção é justamente o componente “camelContext”. O contexto do Camel é o ponto principal de toda aplicação. Esse componente é o responsável por gerenciar o ciclo de vida das nossas integrações. Essas integrações podem ser de qualquer tipo: jms, ftp, webservice, rest, local, beans do próprio spring, banco de dados entre muitas outras. Integrações são suportadas por diversos componentes do próprio Camel. Para criarmos rotas, dependemos principalmente de um configurador. Isso no Camel é possível através de um RouteBuilder cuja implementação é referenciada pelo nosso camelContext.

O Apache Camel tem nele um conceito muito forte chamado Rota. Rota é a capacidade de consumir um conteúdo de um ponto de entrada e produzi-lo em um ponto de saída. Se queremos por exemplo, ler arquivos de um diretório e enviá-los a uma fila JMS criaremos uma rota que “consuma” arquivos e “produza” o seu conteúdo para uma fila JMS. Tanto o arquivo quando a fila serão pontos de entrada e saída (endpoints) respectivamente. Veremos mais adiante como isso fica em Java.

Outra Bean do XML do Spring que chama a atenção é o cxfEndpoint. Através dele podemos configurar muito facilmente um ponto de entrada ou saída para nossas rotas do Camel. Isso é interessante: ele serve como servidor e cliente ao mesmo tempo, mas é interpretado diferentemente de acordo com o seu uso. Se usamos ele como ponto de entrada, então estaremos publicando um serviço WebService no endereço físico http://localhost:9292/HelloWorldService. Se utilizamos ele como ponto de saída, então temos um cliente para envio de mensagens para esse endereço. Vamos olhar as rotas que demonstram o que acabei de explicar.

package br.com.devmedia.helloworldservice; import org.apache.camel.builder.RouteBuilder; public class HelloWorldContextBuilder extends RouteBuilder{ @Override public void configure() throws Exception { from("direct:digaOla").to("cxf:bean:soapMessageEndpoint?dataFormat=POJO"); from("cxf:bean:soapMessageEndpoint?dataFormat=POJO").to("bean:pojoHelloWorldService"); } }
Listagem 3. Configuração das Rotas do Camel

Vemos apenas 2 rotas. Uma consome de “direct:digaOla” e produz para “cxf:bean:soapMessageEndpoint”. A segunda rota consome de cxf:bean:soapMessageEndpoint e produz para “bean:pojoHelloWorldService”. No nosso exemplo, “soapMessageEndpoint” é o nome do bean configurado com a tag “cxf:cxfEndpoint” no arquivo XML do Spring enquanto que “pojoHelloWorldService” é o nome do bean que implementa a lógica de negócio do WebService.

Essas 2 rotas são respectivamente cliente e servidor para o WebService. Se você gostou, então veja como ficou a implementação do serviço na Listagem 4.

package br.com.devmedia.helloworldservice; import br.com.devmedia.helloworldservice.schema.Erro; import br.com.devmedia.helloworldservice.schema.Nome; import br.com.devmedia.helloworldservice.schema.Saudacao; import br.com.devmedia.helloworldservice.wsdl.DigaOlaException; public class PojoHelloWorldService { public Saudacao digaOla(Nome parametro) throws DigaOlaException { Saudacao s = new Saudacao(); if ("ze".equals(parametro.getConteudo())) { s.setConteudo("Bom dia"); } else if ("maria".equals(parametro.getConteudo())){ s.setConteudo("Ola"); } else { Erro erro = new Erro(); erro.setConteudo("ERR_01"); throw new DigaOlaException("Nao quero conversar",erro); } return s; } }
Listagem 4. Implementação POJO do WebService

Simples e prático. Melhor ainda: na nossa implementação de serviço não fazemos referência a qualquer anotações seja do Camel, CXF, JAX-WS etc. Também não precisamos implementar qualquer interface de qualquer framework. Apenas com um teste unitário conseguimos verificar a implementação do negócio e nem precisamos de servidor de aplicação, servidor HTTP, nada. Isso só é possível por alguns detalhes: o primeiro é que o Camel já faz todo o trabalho de conversão entre mensagens SOAP e os objetos mapeados pelo JAX-WS. Ele também consegue “adivinhar” que se o nosso método tem um parâmetro do tipo “Nome”, ele deve injetar objetos desse tipo para nós. Mesmo se lançarmos uma exceção, ele conseguirá transformá-la em um SOAP Fault. Perceba o parâmetro dataFormat=POJO. Isso informa ao Camel que usaremos classes mapeadas do JAX-WS e é o que ele também usará para fazer as conversões necessárias. O Camel suporta outros tipos de formato de dados que você pode conhecer em http://camel.apache.org/cxf.html.

Podemos utilizar a própria API do Camel para enviar mensagens para o WebService. Para isso, instanciamos um utilitário (ProducerTemplate) capaz de interagir com o contexto do Camel. Depois criamos objetos e o passamos ao endpoint “direct:digaOla”. Toda a execução fica à cargo do Apache Camel. Veja o exemplo na Listagem 5.

@ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class HelloWorldServiceTest { @Autowired CamelContext context; @DirtiesContext @Test public void requestResponseTest() throws Exception { ProducerTemplate template = context.createProducerTemplate(); Nome _digaOla_parametro = new Nome(); MessageContentsList ret = null; _digaOla_parametro.setConteudo("ze"); ret = (MessageContentsList) template.sendBody("direct:digaOla", ExchangePattern.InOut, _digaOla_parametro); Saudacao saudacao = (Saudacao) ret.get(0); Assert.assertEquals("Bom dia", saudacao.getConteudo()); } } }
Listagem 5. Exemplo de chamada ao WebService

É isso, pessoal. Finalizamos a criação e chamada do nosso serviço utilizando o Apache CXF, Apache Camel e Spring. Para ver o projeto completo, basta acessar o repositório do GITHUB.

Artigos relacionados