Nesse artigo vamos visitar rapidamente a especificação WS-Addressing, abordando de forma prática alguns de seus conceitos mais básicos e como é o seu funcionamento dentro do framework Apache CXF com Web Services.

O WS-Addressing é uma especificação WS-*. No site do W3C você pode encontrar uma vastíssima documentação sobre esse padrão dividido em três documentos: Web Services Addressing 1.0 – Metadata, Web Services Addressing 1.0 – SOAP Binding e Web Services Addressing 1.0 – Core (http://www.w3.org/).

Se não nos amedrontarmos diante da enormidade de informações, percebemos que no Abstract (parte inicial) de cada documento está descrito o motivo de existir WSA (sigla do WS-Addressing): “Fornecer mecanismos independentes de transporte para endereçamento de Web Services e mensagens”. Vamos por partes. Quando mandamos mensagens para Web Services e ele “conversa” através de SOAP então nossas mensagens deverão obedecer ao padrão determinado pelo SOAP.

Se utilizarmos o protocolo HTTP para “transmitir” a nossa mensagem a um Web Service, então o protocolo de transporte é o HTTP. WSA ser independente do transporte significa dizer que cada mensagem carrega em si toda informação relevante para o seu processamento e roteamento e isso funcionará da mesma forma seja qual for o protocolo de transporte (HTTP, SMTP, JMS etc).

Roteamento depende de endereços (origem e principalmente destino). Com WSA toda mensagem tem um identificador único, pode ter um originador, destinatário, endereço de falha, endereço de retorno, propriedades específicas, parametrizações pra alguma instância do serviço e assim por diante. E essa mensagem pode ser repassada de um serviço para outro, sendo por eles manipulada e incrementada. Todas essas informações são adicionadas ao “header” da mensagem SOAP.

Podemos comparar a utilidade do WSA ao envio uma carta pelos correios. Numa correspondência adicionamos um destinatário (nome do destinatário, endereço), instruções específicas de rota (casa de cima, vire a direita depois do beco) e endereço de retorno (utilizado em caso de falha na entrega). Os correios ainda podem incrementar isso com parâmetros adicionais tais como número de tentativas, motivo da falha, mais instruções de retorno (buscar na agência) e assim por diante. Ora, se desejamos enviar uma carta utilizando DHL ou Fedex (transportes diferentes), todas as outras informações de origem e destino continuam sendo válidas! WSA oferece isso ao mundo de Web Services.

WSA em ação

Figura 1: WSA em ação

Vamos agora explorar um pouco do WSA através do nosso exemplo em Apache CXF.

Primeiramente precisamos definir nossos serviços. O primeiro será o HelloService, no qual pretendemos invocar a operação digaOla passando um nome de usuário. O retorno dessa operação é simplesmente uma string. Vamos ver sua definição na Listagem 1.

Listagem 1: Definição de HelloService


@WebService(targetNamespace = HelloService.NS)
@Addressing(enabled = true, required = true)
@org.apache.cxf.interceptor.InInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingInInterceptor" })
@org.apache.cxf.interceptor.OutInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingOutInterceptor" })
public interface HelloService {
	String NS = "http://devmedia.sayhello";

	@WebResult(name = "response")
	public abstract String digaOla(String user) ;
}

No serviço HelloService vemos uma anotação interessante: “Addressing”. Através dessa anotação habilitamos o padrão WSA para esse serviço. Vemos também que o retorno da operação “digaOla” será encapsulado no elemento “response”. Vamos ver a definição do serviço Callback na listagem 2.

Listagem 2: Definição do serviço Callback


@WebService()
@org.apache.cxf.interceptor.InInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingInInterceptor" })
@org.apache.cxf.interceptor.OutInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingOutInterceptor" })
public interface Callback {
	String NS = "http://devmedia.sayhello";

	@Oneway
	@RequestWrapper(localName = "digaOlaResponse", targetNamespace = NS)
	public void callBack(@WebParam(name = "response") String callbackMessage);

}

O objetivo de Callback é receber o resultado da chamada da operação digaOla do HelloService. Na operação callBack, o parâmetro de entrada é a string encapsulada no elemento “response”. Reparem também que na anotação RequestWrapper utilizamos o localName = “digaOlaResponse”. Esse é o nome da ação (Action) do WSA que representa o retorno da operação digaOla. Uma Action é uma intenção ou objetivo da mensagem.

Para inicializarmos os nossos serviços e dar a eles um endereço físico, podemos utilizar a configuração do Spring como vemos na listagem 3.

Listagem 3: Exemplo de configuração do Spring


    <jaxws:endpoint
        id="server"
        address="http://localhost:9292/hello"
        implementor="br.com.devmedia.helloworldservice.SimpleHelloService"
        bus="serverBus"
        >
        <jaxws:properties>
            <entry key="schema-validation-enabled" value="true" />
        </jaxws:properties>
    </jaxws:endpoint>

    <jaxws:endpoint
        id="reply"
        address="http://localhost:9293/reply"
        implementor="br.com.devmedia.helloworldservice.CallbackImpl"
        bus="replyBus"
        >
        
		<jaxws:features>
		    <bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxws:features>
        <jaxws:properties>
            <entry key="schema-validation-enabled" value="true" />
        </jaxws:properties>
    </jaxws:endpoint>
  

Na configuração do jaxws:endpoint percebemos o atributo bus referenciando beans do tipo Bus. O Bus é o objeto principal do Apache CXF e é através dele que configuramos o comportamento do framework como, por exemplo, uso de segurança, políticas diversas, addressing, logging, suporte a transportes e assim por diante.

Já que temos os serviços, vamos ao código de exemplo do cliente na listagem 4.

Listagem 4: Exemplo de Cliente para HelloService


        HelloService hello = getHelloService();

        AddressingBuilder builder = AddressingBuilder.getAddressingBuilder();
        AddressingProperties maps = builder.newAddressingProperties();
        EndpointReferenceType replyType = WSA_OBJECT_FACTORY.createEndpointReferenceType();
        AttributedURIType uriType = WSA_OBJECT_FACTORY.createAttributedURIType();
        uriType.setValue("http://localhost:9293/reply");
        replyType.setAddress(uriType);
        maps.setReplyTo(replyType);
        
        Map<String, Object> requestContext =
                ((BindingProvider)hello).getRequestContext();
        requestContext.put(CLIENT_ADDRESSING_PROPERTIES, maps);

        String result2 = hello.digaOla("Tester2");
        Assert.assertNull(result2);

Várias coisas estão acontecendo na listagem 4. Primeiro obtemos um cliente para HelloService. Em seguida utilizamos a API do apache CXF para criar uma instância do tipo EndpointReferenceType. Esse objeto representa na verdade uma entidade importante no WSA que é o EPR (endpoint reference). Um EPR em termos simples é uma estrutura que encapsula informações de endereçamento. No nosso caso, contém uma URI que é o endereço do Callback. Utilizamos essa referência de EPR como replyTo e passamos essa configuração para o contexto de execução dessa chamada (requestContext).

Essa é uma especificação poderosa, pois de forma razoavelmente simples permite que o cliente mude a forma como os serviços se comportam. Outras funcionalidades como controle de erros e atributos extras são suportados.

Vamos agora examinar como ficam as mensagens SOAP trafegadas entre o cliente e o serviço na listagem 5 e listagem 6.

Listagem 5: Requisição SOAP ao HelloService


<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
    <soap:Header>
        <Action xmlns="http://www.w3.org/2005/08/addressing" >
			http://devmedia.sayhello/HelloService/digaOlaRequest
        </Action>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing" >
			urn:uuid:647a8c13-01a7-4838-aa38-9e46f0e256b7
        </MessageID>
        <To xmlns="http://www.w3.org/2005/08/addressing" >
			http://localhost:9292/hello
        </To>
        <ReplyTo xmlns="http://www.w3.org/2005/08/addressing" >
            <Address>
				http://localhost:9293/reply
            </Address>
        </ReplyTo>
    </soap:Header>
    <soap:Body>
        <ns2:digaOla xmlns:ns2="http://devmedia.sayhello" >
            <arg0>
				Tester2
            </arg0>
        </ns2:digaOla>
    </soap:Body>
</soap:Envelope>

Na listagem 5 vemos informações interessantes adicionadas ao Header da mensagem. O Action representa uma requisição à digaOla. MessageID é o identificador único que cada mensagem terá. “To” é o endereço do destinatário e “ReplyTo”, o recipiente do resultado do processamento que é o serviço Callback.

Listagem 6: Requisição SOAP ao Callback


<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
    <soap:Header>
        <Action xmlns="http://www.w3.org/2005/08/addressing" >
			http://devmedia.sayhello/HelloService/digaOlaResponse
        </Action>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing" >
			urn:uuid:ed7b9091-a225-4cff-94be-d7bcccd7cff2
        </MessageID>
        <To xmlns="http://www.w3.org/2005/08/addressing" >
			http://localhost:9293/reply
        </To>
        <RelatesTo xmlns="http://www.w3.org/2005/08/addressing" >
			urn:uuid:647a8c13-01a7-4838-aa38-9e46f0e256b7
        </RelatesTo>
    </soap:Header>
    <soap:Body>
        <ns2:digaOlaResponse xmlns:ns2="http://devmedia.sayhello" >
            <response>
				Hello Tester2
            </response>
        </ns2:digaOlaResponse>
    </soap:Body>
</soap:Envelope>

Na listagem 6 vemos um elemento RelatesTo que contém um ID. Esse ID é o mesmo da mensagem enviada ao HelloService configurando assim um relacionamento entre elas.

Pessoal, com esse artigo conseguimos arranhar superficialmente o que o WSA pode oferecer aos arquitetos e programados na criação e desenvolvimento de Web Services. O WSA é uma especificação base para outras tantas e portanto vale a pena ser entendido com bastante calma. Não se esqueçam de dar uma olhada no projeto completo em https://github.com/leogsilva/WsAddressingCxf.git. Até a próxima.