O Apache CXF é um framework de integração que dá suporte à criação de serviços no padrão Web Service. Através dele podemos criar e consumir serviços, utilizando código Java e configuração do framework Spring. Não é objetivo desse artigo explicar extensamente o que é um Web Service, ou como é organizado um WSDL e nem mesmo o que é SOA. Abordaremos apenas como criar um projeto simples com o apache Maven, como utilizar a geração de código Java a partir de um arquivo WSDL pré-existente e ainda como publicar uma implementação do serviço.

Quando trabalhamos com Apache CXF, temos claramente duas linhas principais de desenvolvimento. A primeira é gerar o código Java anotado e compatível com o JAX-WS a partir de um descritor WSDL (que veremos a seguir). A segunda forma é criar o código Java manualmente e em seguida utilizá-lo para gerar o arquivo WSDL. O resultado final é o mesmo: um serviço no padrão Web Service.

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.


<?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>
Listagem 1. WSDL do serviço de exemplo
Estrutura do WSDL
Figura 1. Estrutura do WSDL

Na Figura 1 conseguimos ver a estrutura do nosso WSDL. Todo descritor WSDL é composto de duas partes principais: uma abstrata que relaciona os tipos suportados (types), as mensagens (message) e as operações (portType e operation) e a outra concreta, que amarra as definições abstratas em detalhes de protocolo, transporte, endereços físicos e codificação (binding, service). Para mais detalhes sobre a estrutura de um WSDL, visite o site: Web Services Description Language.

Uma vez que já temos o nosso descritor de serviço, podemos criar nosso projeto do maven (pom.xml) da seguinte forma:


<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>org.apache.cxf.samples</groupId>
    <artifactId>wsdl_first_cxf</artifactId>
    <packaging>jar</packaging>
    <name>WSDL first usando apache cxf</name>
    <description>WSDL first usando apache cxf</description>
    <version>2.7.0</version>
    <build>
        <plugins>
           <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${project.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>src/main/resources/
                                    HelloWorldService.wsdl</wsdl>
                                    <extraargs>
                                        <extraarg>-all</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
	<dependencies>
	    <dependency>
	        <groupId>junit</groupId>
	        <artifactId>junit</artifactId>
	        <version>4.10</version>
	    </dependency>
	</dependencies>
</project>
Listagem 2. Projeto do maven

Nesse pom.xml a parte mais interessante está na seção de plugins. Vemos uma ferramenta bem útil do CXF chamada wsdl2java. Esse plugin transformará o WSDL em código Java anotado utilizando o padrão JAX-WS. O JAX-WS é um padrão de mercado suportado por vários fornecedores e traz facilidade na criação de serviços, apenas adicionado anotações do Java a classes Pojo. Um fato importante é que se utilizamos JAX-WS, não estamos necessariamente amarrados ao CXF. O JAX-WS é nativamente suportado pelo Java 6 em diante.


mvn clean package eclipse:eclipse
Listagem 3. Comando maven

Esse comando faz com que o código Java seja gerado e ao mesmo tempo cria o projeto compatível com o IDE Eclipse. Podemos agora abrir o projeto e finalmente fazer o nosso serviço funcionar. Lembre-se: se você não conhece o maven, visite Apache.org.

A opção “-all” criará para nós exemplos de servidor e cliente. O servidor é simplesmente a classe Java que dá vida ao Web Service. Essa classe implementará uma interface Java que define o comportamento descrito pelo WSDL e será publicada em um endereço físico acessível pelo cliente. O cliente é qualquer sistema que queira invocar operações do serviço inclusive outros serviços. A seguir, uma implementação simples da operação digaOla.


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;
import br.com.devmedia.helloworldservice.wsdl.DigaOlaPortType;

@javax.jws.WebService(
        serviceName = "HelloWorldService",
        portName = "HelloWorldService_Port",
        targetNamespace = "http://helloworldservice.devmedia.com.br/wsdl/",
        endpointInterface = "br.com.devmedia.helloworldservice.wsdl.DigaOlaPortType")
public class SimpleHelloWorldService implements DigaOlaPortType {

	@Override
	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 de exemplo
Estrutura do projeto
Figura 2. Estrutura do projeto

Alguns pontos importantes dessa implementação devemos ressaltar:

  • A classe deve ser anotada para ser interpretada pelo framework CXF. O CXF usará as informações da anotação WebService para conseguir interpretar as chamadas ao serviço e invocar esse código.
  • A classe implementa a interface DigaOlaPortType. Essa interface foi gerada pela ferramenta wsd2java no diretório “target/generated-sources/cxf”.
  • Os tipos utilizados nessa operação (Nome, Saudacao, Erro) são aqueles que estão descritos no WSDL.
  • Quando queremos lançar um erro, podemos utilizar a exception DigaOlaException (também gerada). Isso se traduzirá naturalmente em um Message Fault.

    Finalmente, vamos criar um teste unitário com um cliente simples para o nosso serviço.

    
    package br.com.devmedia.helloworldservice;
    
    import java.net.URL;
    import javax.xml.ws.Endpoint;
    import junit.framework.Assert;
    import org.junit.Test;
    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;
    import br.com.devmedia.helloworldservice.wsdl.HelloWorldService;
    
    public class HelloWorldServiceTest {
    
    	@Test
    	public void requestResponseTest() throws Exception {
            Object implementor = new SimpleHelloWorldService();
            String address = "http://localhost:9292/HelloWorldService";
            Endpoint.publish(address, implementor);	
            HelloWorldService ss = new HelloWorldService(new 
            URL("http://localhost:9292/HelloWorldService"));
            DigaOlaPortType port = ss.getHelloWorldServicePort();  
            System.out.println("Invoking digaOla...");
            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());
            }
    	}
    }
    Listagem 5. Teste unitário para chamada do serviço

    Reparem que para publicar o serviço, utilizamos apenas a classe utilitária javax.xml.ws.Endpoint passando um endereço físico e uma instância da classe SimpleHelloWorldService. Além disso, o cliente (também gerado) é a classe br.com.devmedia.helloworldservice.wsdl.HelloWorldService e precisamos apenas passar o endereço do endpoint (servidor) para invocarmos a operação digaOla.

    É isso pessoal. Finalizamos a criação e chamada do nosso serviço utilizando o Apache CXF. Existem outras funcionalidades fantásticas que exploraremos no futuro mas já deu pra ter uma noção da facilidade e produtividade oferecida por esse framework. Até a próxima.