Apache CXF e MTOM

Veja neste artigo um mecanismo eficiente de envio de dados binários a Webservices conhecido por MTOM (Message Transmission Optimization Mechanism) e XOP (XML-Binary Optimization Packaging) e como é o suporte a eles dentro do Apache CXF.

Nesse artigo vamos examinar um mecanismo eficiente de envio de dados binários a Webservices conhecido por MTOM (Message Transmission Optimization Mechanism) e XOP (XML-Binary Optimization Packaging) e como é o suporte a essa funcionalidade dentro do framework Apache CXF.

Figura 1. Processamento utilizando MTOM

Se você olhar as informações sobre MTOM no site do W3C pode ficar realmente confuso com a quantidade de especificações e documentações. Mas como sempre, basta dar uma olhada com calma para perceber que é mais simples do que parece. Resumidamente, vamos identificar as 3 especificações que definem o que é MTOM e como ele deve funcionar:

Agora ficou fácil demais! Fica claro que os profissionais do W3C se preocuparam em criar definições cujas implementações pudessem ser evoluídas com o tempo, em novos protocolos ou formatos mais otimizados daí essa separação em várias camadas.

Agora vamos ver o que o framework Apache CXF tem para nós com relação ao MTOM. Para maiores detalhes, vale a pena dar uma olhada nessa página.

Antes de mais nada, vamos definir o nosso WebService utilizando JAX-WS.

package br.com.devmedia.helloworldservice; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.soap.MTOM; @WebService(targetNamespace = HelloService.NS) @MTOM(enabled=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 PersonInfo digaOla(PersonInfo info) ; }
Listagem 1. Serviço utilizando suporte MTOM

A primeira coisa que nos chama a atenção é a anotação MTOM. Ao adiciona-la, informamos ao Apache CXF que esse serviço suporta o uso do padrão. Na nossa operação digaOla, recebemos um objeto complexo por parâmetro do tipo PersonInfo. É esse objeto que encapsula as informações binárias. Vamos ver como isso é implementado utilizando o JAXB (listagem 2).

import javax.activation.DataHandler; import javax.xml.bind.annotation.XmlMimeType; import javax.xml.bind.annotation.XmlType; @XmlType public class PersonInfo { private DataHandler binaryData; @XmlMimeType("application/octet-stream") public DataHandler getBinaryData() { return binaryData; } public void setBinaryData(DataHandler binaryData) { this.binaryData = binaryData; } }
Listagem 2. Objeto que carrega informações binárias

A classe PersonInfo é também muito simples e carrega apenas um atributo do tipo DataHandler. A classe DataHandler do pacote javax.activation é simplesmente uma abstração que representa uma transferência de dados entre várias origens e em qualquer formato. Dessa forma, programadores não precisam se preocupar como será utilizado o XOP ou qualquer outra codificação, pois isso fica por conta do Apache CXF.

O nosso serviço já consegue receber e transmitir grandes volumes de informação binária. Vamos examinar na listagem 3 para ver como é a implementação de um cliente simples para o nosso serviço.

HelloService hello = service.getPort(portName, HelloService.class); SOAPBinding binding = (SOAPBinding) ((BindingProvider)hello).getBinding(); binding.setMTOMEnabled(true); PersonInfo info = new PersonInfo(); File f; try { f = new File(SimpleNormalHelloService.class.getResource("/java.png").toURI()); DataSource ds = new FileDataSource(f); DataHandler dh = new DataHandler(ds); info.setBinaryData(dh); PersonInfo result = hello.digaOla(info); result.getBinaryData(); Assert.assertNotNull(result); } catch (URISyntaxException e) { e.printStackTrace(); }
Listagem 3. Cliente para serviço HelloService

No nosso cliente, vemos dois aspectos interessantes: primeiro o MTOM habilitado no Binding. Isso fará com que o cliente mande requisições com o formato otimizado. Em segundo, de forma bem simples criamos um DataHandler que encapsula as informações binárias que desejamos enviar para o serviço. Para finalizar, vamos dar uma olhada como ficou a requisição SOAP na listagem 4.

Address: http://localhost:9292/hello Encoding: UTF-8 Http-Method: POST Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:0b2e9c8c-3b23-49bc-a925-b2b0435616af"; start="<root.message@cxf.apache.org>"; start-info="text/xml" Headers: {Accept=[*/*], SOAPAction=[""]} Payload: --uuid:0b2e9c8c-3b23-49bc-a925-b2b0435616af Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"; Content-Transfer-Encoding: binary Content-ID: <root.message@cxf.apache.org> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:digaOla xmlns:ns2="http://devmedia.sayhello"> <arg0> <binaryData> <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:784823ae-1a42-4ed1-a07e-153fb671c7ef-1@cxf.apache.org"/> </binaryData> </arg0> </ns2:digaOla></soap:Body> </soap:Envelope> --uuid:0b2e9c8c-3b23-49bc-a925-b2b0435616af Content-Type: application/octet-stream Content-Transfer-Encoding: binary Content-ID: <784823ae-1a42-4ed1-a07e-153fb671c7ef-1@cxf.apache.org> Content-Disposition: attachment;name="java.png" ?PNG (binary data)
Listagem 4. Requisição SOAP otimizada com MTOM

Vemos claramente na requisição que o conteúdo representado pelo cabeçalho Content-Type é o xop+xml. No payload SOAP, vemos na seção body a tag binaryData com um ID: 784823ae-1a42-4ed1-a07e-153fb671c7ef. Esse ID é uma referência ao conteúdo binário que está um pouco mais abaixo do cabeçalho “Content-ID”.

Se é tão fácil e tão otimizado, por que não mandar tudo utilizando isso? O overhead do uso do MTOM é consideravelmente alto já que o conteúdo binário é codificado em base64, que faz com que o tamanho da mensagem aumente em cerca de 30%. Isso o torna ineficaz para mensagens pequenas. Além do mais, existe o XML FastInfoset que já é um padrão eficiente para mensagens menores.

Pessoal, com esse artigo conseguimos de uma maneira muito simples entender o que é MTOM e ver como é extremamente simples enviar conteúdo binário para um WebService. Não se esqueçam de dar uma olhada no projeto completo no GitHub.

Artigos relacionados