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 para aumentar nossa produtividade.

O Apache CXF é um framework de integração que dá suporte à criação de serviços no padrão WebService. Através dele podemos criar e consumir serviços, utilizando código Java e configuração do framework Spring. Nesse artigo vamos utilizar um WSDL pronto para demonstrar como trabalhar com o Apache CXF junto com o Spring Framework.

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.

Listagem 1: WSDL do serviço de exemplo


<?xml version="1.0" encoding="UTF-8" ?>

<wsdl:definitions name="HelloWorldService" 
	targetNamespace="http://helloworldservice.devmedia.com.br/wsdl/" 
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
	xmlns:tns="http://helloworldservice.devmedia.com.br/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:do="http://helloworldservice.devmedia.com.br/schema/"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

	<wsdl:types>
	 <xs:schema targetNamespace="http://helloworldservice.devmedia.com.br/schema/"
	 	attributeFormDefault="unqualified" elementFormDefault="unqualified" 
		xmlns:tns="http://helloworldservice.devmedia.com.br/schema/"
		xmlns:xs="http://www.w3.org/2001/XMLSchema">
		<xs:element name="Nome" type="tns:Nome"/>
		<xs:element name="Saudacao" type="tns:Saudacao" />
		<xs:element name="Erro" type="tns:Erro"/>
		<xs:complexType name="Nome">
			<xs:sequence>
				<xs:element name="conteudo" type="xs:string" />
			</xs:sequence>
		</xs:complexType>
		<xs:complexType name="Saudacao">
			<xs:sequence>
				<xs:element name="conteudo" type="xs:string" />
			</xs:sequence>
		</xs:complexType>
		<xs:complexType name="Erro">
			<xs:sequence>
				<xs:element name="conteudo" type="xs:string" />
			</xs:sequence>
		</xs:complexType>
	 </xs:schema>
	</wsdl:types>
	
	<wsdl:message name="DigaOlaRequisicao">
		<wsdl:part name="parametro" type="do:Nome" />
	</wsdl:message>
	
	<wsdl:message name="DigaOlaResposta">
		<wsdl:part name="saudacao" type="do:Saudacao" /> 
	</wsdl:message>
	
	<wsdl:message name="DigaOlaException">
		<wsdl:part name="DigaOlaException" type="do:Erro" />
	</wsdl:message>
	
	<wsdl:portType name="DigaOla_PortType">
		<wsdl:documentation>
			A operacao digaOla recebe um nome por parametro e devolve
			um cumprimento ou erro em situacoes especificas
		</wsdl:documentation>
		<wsdl:operation name="digaOla">
			<wsdl:input name="DigaOlaRequisicao" message="tns:DigaOlaRequisicao" />
			<wsdl:output name="DigaOlaResposta" message="tns:DigaOlaResposta" />
			<wsdl:fault name="DigaOlaException" message="tns:DigaOlaException" />
		</wsdl:operation>
	</wsdl:portType>
	
	<wsdl:binding name="DigaOla_SoapBinding" type="tns:DigaOla_PortType">
		<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="digaOla">
			<soap:operation soapAction="" style="document"/>
			<wsdl:input name="DigaOlaRequisicao">
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output name="DigaOlaResposta">
				<soap:body use="literal" />
			</wsdl:output>
			<wsdl:fault name="DigaOlaException">
				<soap:fault name="DigaOlaException" use="literal" />
			</wsdl:fault>
		</wsdl:operation>
	</wsdl:binding>
	
	<wsdl:service name="HelloWorldService">
		<wsdl:port name="HelloWorldService_Port" binding="tns:DigaOla_SoapBinding">
			<soap:address location="//www.devmedia.com.br/HelloWorldService" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>
Estrutura do WSDL

Figura 1: Estrutura do 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.

Listagem 2: Dependências especiais do nosso projeto


	    <dependency>
	        <groupId>org.springframework</groupId>
	        <artifactId>spring-beans</artifactId>
	        < version>${spring.version}< /version>
	    </dependency>
	    <dependency>
	        <groupId>org.springframework</groupId>
	        <artifactId>spring-core</artifactId>
	        < version>${spring.version}< /version>
	    </dependency>
	    <dependency>
	        <groupId>org.springframework</groupId>
	        <artifactId>spring-test</artifactId>
	        < version>${spring.version}< /version>
	        <scope>test</scope>
	    </dependency>	    
	    <dependency>
	        <groupId>org.springframework</groupId>
	        <artifactId>spring-context</artifactId>
	        < version>${spring.version}< /version>
	    </dependency>
	    <dependency>
	        <groupId>org.apache.cxf</groupId>
	        <artifactId>cxf-api</artifactId>
	        < version>${project.version}< /version>
	    </dependency>
	    <dependency>
	        <groupId>org.apache.cxf</groupId>
	        <artifactId>cxf-rt-core</artifactId>
	        < version>${project.version}< /version>
	    < /dependency>
	    < dependency>
	        <groupId>org.apache.cxf</groupId>
	        <artifactId>cxf-rt-transports-http</artifactId>
	        < version>${project.version}< /version>
	    < /dependency>
	    < dependency>
	        <groupId>org.apache.cxf</groupId>
	        <artifactId>cxf-rt-transports-http-jetty</artifactId>
	        < version>${project.version}< /version>
	    </dependency>
   	    <dependency>
	        <groupId>org.apache.cxf</groupId>
	        <artifactId>cxf-rt-frontend-jaxws</artifactId>
	        < version>${project.version}< /version>
	    < /dependency>

Vemos várias dependências de Spring e CXF, sendo que as do CXF chamam bastante atenção porque têm palavras tais como transports e frontend. Isso não é por acaso. O diagrama de arquitetura do CXF (Figura 1) demonstra a separação das responsabilidades em módulos distintos, permitindo um ajuste fino do framework à real necessidade do projeto.

Um front-end nada mais é do que um modelo de programação. Usamos JAX-WS ou JAX-RS, mas poderíamos trabalhar com “Simple” ou JavaScript ou ainda algo totalmente novo que nós podemos inventar. Assim como o front-end, o Apache CXF suporta transportes diferentes. Nesse caso estamos expondo nosso WebService através de um servidor HTTP, por isso utilizamos os transportes selecionados, mas poderíamos utilizar JMS, SMTP, Local, Servlet e outros tantos mais. Uma observação importante: no WSDL definimos um transporte através do elemento “binding” em seu atributo “transport”. Em nosso exemplo utilizamos “http://schemas.xmlsoap.org/soap/http” que só funcionará se o CXF reconhecer esse namespace através de seus componentes.

Visão de módulos arquiteturais do Apache CXF

Figura 2: Visão de módulos arquiteturais do Apache CXF

Vamos agora dar uma olhada no código fonte do projeto.

Listagem 3: Comando maven para gerar projeto para IDE Eclipse


mvn clean package eclipse:eclipse

O arquivo principal é o que define o contexto do Spring. Nele podemos ver dois exemplos interessantes: jaxws:endpoint e jaxws:client. Eles configuram um servidor para hospedar nosso WebServive e um cliente para invocar operações nesse serviço.

Listagem 4: Implementação de exemplo


<?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="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
		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" >

    
    <jaxws:endpoint
        id="server"
        address="http://localhost:9292/HelloWorldService"
        implementor="br.com.devmedia.helloworldservice.SimpleHelloWorldService"
        wsdlLocation="HelloWorldService.wsdl">
		<jaxws:features>
		    <bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxws:features>
        <jaxws:properties>
            <entry key="schema-validation-enabled" value="true" />
        </jaxws:properties>
    </jaxws:endpoint>
    <jaxws:client address="http://localhost:9292/HelloWorldService"
        id="client"
        xmlns:s="http://helloworldservice.devmedia.com.br/wsdl/"
		serviceName="s:HelloWorldService"
		endpointName="s:HelloWorldService_Port"        	
        wsdlLocation="HelloWorldService.wsdl"
        serviceClass="br.com.devmedia.helloworldservice.wsdl.DigaOlaPortType" >
    </jaxws:client>        
       
</beans>

Na definição de endpoint, passamos parâmetros “address” indicando em que endereço físico nosso serviço estará disponível, implementor contendo a classe que implementa nosso o WebService e onde está localizado o WSDL. Como o arquivo WSDL está na pasta src/main/resources, apenas colocamos o nome do arquivo. Se ele estivesse em src/main/resources/wsdl, a configuração seria “wsdl/HelloWorldService.wsdl” e assim por diante. A “feature” liga permite que sejam mostradas no log as mensagens SOAP de entrada e saída durante a execução do serviço.

Logo abaixo da definição do servidor vemos a definição do cliente. Ela é um pouco mais “complicada”, pois demanda que conheçamos o “serviceName” e o endpointName além do namespace do WebService. Esses valores estão no próprio WSDL como mostrado na Figura 3.

Configuração do cliente

Figura 3: Configuração do cliente

Claramente o namespace é aquele definido no início no WSDL (http://helloworldservice.devmedia.com.br/wsdl/). O serviceName tem seu valor retirado do atributo “name” do elemento wsdl:service enquanto que o endpointName é o atributo “name” do elemento wsdl:port. Fácil não é? Um programador pode gastar horas quebrando a cabeça até entender como configurar o cliente. Finalmente vamos olhar o código-fonte do teste unitário.

Listagem 5: Teste unitário para chamada do serviço


package br.com.devmedia.helloworldservice;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import br.com.devmedia.helloworldservice.schema.Nome;
import br.com.devmedia.helloworldservice.schema.Saudacao;
import br.com.devmedia.helloworldservice.wsdl.DigaOlaException;
import br.com.devmedia.helloworldservice.wsdl.DigaOlaPortType;

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class HelloWorldServiceTest {
	
	@Autowired
	DigaOlaPortType port;

	@Test
	public void requestResponseTest() throws Exception {
        Nome _digaOla_parametro = new Nome();
        try {
        	_digaOla_parametro.setConteudo("ze");
        	Saudacao ret = port.digaOla(_digaOla_parametro);
            Assert.assertEquals("Bom dia", ret.getConteudo());
        } catch (DigaOlaException e) { 
            System.out.println("Expected exception: DigaOlaException has occurred.");
            System.out.println(e.toString());
        }

        try {
        	_digaOla_parametro.setConteudo("maria");
        	Saudacao ret = port.digaOla(_digaOla_parametro);
            Assert.assertEquals("Ola", ret.getConteudo());
        } catch (DigaOlaException e) { 
            System.out.println("Expected exception: DigaOlaException has occurred.");
            System.out.println(e.toString());
        }
        try {
        	_digaOla_parametro.setConteudo("anonimo");
        	Saudacao ret = port.digaOla(_digaOla_parametro);
        	Assert.assertTrue(false);
        } catch (DigaOlaException e) { 
        	Assert.assertEquals("ERR_01",e.getFaultInfo().getConteudo());
            System.out.println("Expected exception: DigaOlaException has occurred.");
            System.out.println(e.toString());
        }
	}
}

Como estamos trabalhando com Spring, nada mais natural que usar o suporte do framework Spring a testes. Para isso, utilizamos duas anotações: ContextConfiguration e RunWith. A primeira indica que um contexto do Spring será criado quando o teste for executado. O arquivo XML deverá por padrão ter o nome < Nome da classe de Teste>-context.xml ou seja HelloWorldServiceTest-context.xml.

RunWith é uma anotação que permite ao framework Junit delegar a execução de Teste a uma classe executora específica que nesse caso é org.springframework.test.context.junit4.SpringJUnit4ClassRunner. O interessante do SpringJunit4ClassRunner é que através dela podemos trabalhar com outras funcionalidades de testes do Spring tais como injeção de dependências (@Autowired).

No teste, a nossa única preocupação foi implementar os cenários de teste para validar o serviço. Toda inicialização e execução do WebService está sob responsabilidade do Spring. Se reparamos no console, veremos também que todas as mensagens SOAP trocadas entre cliente e servidor estão sendo mostradas por que a feature de Logging está configurada.

É isso pessoal. Finalizamos a criação e chamada do nosso serviço utilizando o Apache CXF e Spring. Para ver o projeto completo, basta acessar o repositório do GITHUB https://github.com/leogsilva/ApacheCxfSpringHelloWorld.git. Até a próxima.