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.