Sunteți pe pagina 1din 11

Technical Draft

This document describes how to connect MTOM/XOP web services (WS-i compliance) using an Axis 1 web service client.

Consuming MTOM/XOP web services from Axis 1

2

1. Creating a MTOM/XOP web service with Apache CXF

2

1.a. Web service interface

2

1.b. Web service implementation

2

1.c. Web service response bean

3

1.d. Spring configuration for CXF

3

1.e. Web descriptor

4

1.f. Generated WSDL

4

2. Consuming the web service with Apache Axis 1

6

2.a. Create standard axis client through WSDL file

6

2.b. Axis client configuration

6

2.c. Axis Response Handler for XOP

6

2. d. Sample web service client

10

3. Final considerations

11

3.a. Sample SOAP Request

11

3.b. Sample SOAP Response

11

3.c. Resources

11

1
1

Consuming MTOM/XOP web services from Axis 1

Axis 1 does not support MTOM specification for the client side (and it only supports this specification partially for the server side [1]). However, sometimes this combination is required due to technologic restrictions.

In this tutorial is described a way to access MTOM/XOP web services for file transfers using Axis 1 as client by implementing two Java projects:

1. Creating a MTOM/XOP web service with Apache CXF 2

2. Consuming the web service with Apache Axis 1

1. Creating a MTOM/XOP web service with Apache CXF

Let s create a simple web service exposing a XOP node to download a file. Several samples to build a CXF web services client are available [2], so only main resources are detailed.

1.a. Web service interface

package cxf.mtom.server.service;

import javax.jws.WebService;

import cxf.mtom.server.bean.ResponseDownloadFile;

@WebService public interface DownloadFile {

ResponseDownloadFile getFile() throws Exception;

}

1.b. Web service implementation

package cxf.mtom.server.service;

import java.io.File;

import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.jws.WebService;

import cxf.mtom.server.bean.ResponseDownloadFile;

@WebService(endpointInterface = "cxf.mtom.server.service.DownloadFile", serviceName = "DownloadFileWS") public class DownloadFileImpl implements DownloadFile {

public ResponseDownloadFile getFile() throws Exception { ResponseDownloadFile rdf = new ResponseDownloadFile(); rdf.setFileName("c:/tmp/readme.txt"); rdf.setFileType("plain/text");

2
2

rdf.setFile(new DataHandler(new FileDataSource(new File("C:/tmp/readme.txt")))); return rdf;

}

}

1.c. Web service response bean

package cxf.mtom.server.bean;

import javax.activation.DataHandler;

/** * Web service response bean. */ public class ResponseDownloadFile {

private String fileName; private String fileType; private DataHandler file;

public String getFileName() { return fileName;

}

public void setFileName(String fileName) { this.fileName = fileName;

}

public String getFileType() { return fileType;

}

public void setFileType(String fileType) { this.fileType = fileType;

}

public DataHandler getFile() { return file;

}

public void setFile(DataHandler file) { this.file = file;

}

}

1.d. Spring configuration for CXF

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

3
3

http://cxf.apache.org/jaxws

http://cxf.apache.org/schemas/jaxws.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="downloadfile" implementor="cxf.mtom.server.service.DownloadFileImpl" address="/DownloadFileWS"> <jaxws:properties> <entry key="mtom-enabled" value="true"/> </jaxws:properties> </jaxws:endpoint> </beans>

1.e. Web descriptor

<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<display-name>DownloadFile</display-name>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:cxf.xml</param-value>

</context-param>

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

<servlet>

<servlet-name>CXFServlet</servlet-name>

<servlet-class>

org.apache.cxf.transport.servlet.CXFServlet

</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>CXFServlet</servlet-name>

<url-pattern>/services/*</url-pattern>

</servlet-mapping>

</web-app>

1.f. Generated WSDL

(p. e. http://localhost:8080/cxfMTOMServer/services/DownloadFileWS?wsdl)

<?xml version='1.0' encoding='utf-8'?> <wsdl:definitions name="DownloadFileWS" targetNamespace="http://service.server.mtom.cxf/"

xmlns:ns1="http://schemas.xmlsoap.org/soap/http"

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://service.server.mtom.cxf/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

4
4

<wsdl:types> <xs:schema elementFormDefault="unqualified" targetNamespace="http://service.server.mtom.cxf/" version="1.0" xmlns:tns="http://service.server.mtom.cxf/"

xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="getFile" type="tns:getFile" /> <xs:element name="getFileResponse" type="tns:getFileResponse" /> <xs:complexType name="getFile"> <xs:sequence /> </xs:complexType> <xs:complexType name="getFileResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="tns:responseDownloadFile" /> </xs:sequence> </xs:complexType> <xs:complexType name="responseDownloadFile"> <xs:sequence> <!-- XOP Node --> <xs:element minOccurs="0" name="file" type="xs:base64Binary" /> <xs:element minOccurs="0" name="fileName" type="xs:string" /> <xs:element minOccurs="0" name="fileType" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema> </wsdl:types> <wsdl:message name="getFileResponse"> <wsdl:part element="tns:getFileResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="getFile"> <wsdl:part element="tns:getFile" name="parameters"></wsdl:part> </wsdl:message> <wsdl:portType name="DownloadFile"> <wsdl:operation name="getFile"> <wsdl:input message="tns:getFile" name="getFile"> </wsdl:input> <wsdl:output message="tns:getFileResponse" name="getFileResponse"></wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="DownloadFileWSSoapBinding" type="tns:DownloadFile"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="getFile"> <soap:operation soapAction="" style="document" /> <wsdl:input name="getFile"> <soap:body use="literal" /> </wsdl:input> <wsdl:output name="getFileResponse"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="DownloadFileWS"> <wsdl:port binding="tns:DownloadFileWSSoapBinding"

5
5

name="DownloadFileImplPort"> <soap:address

location="http://localhost:8080/cxfMTOMServer/services/DownloadFileWS"

/>

</wsdl:port>

</wsdl:service>

</wsdl:definitions>

2. Consuming the web service with Apache Axis 1

And now the Axis 1 client to consume CXF web service.

2.a. Create standard axis client through WSDL file

Use wsdl2java tool [3].

2.b. Axis client configuration (client-config.wsdd)

<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<handler name="log" type="java:org.apache.axis.handlers.LogHandler"/>

<!-- MTOM Axis 1 Handler --> <handler name="xop" type="java:axis.mtom.client.handler.XOPHandler"> <parameter name="serviceName" value="{http://service.server.mtom.cxf/}DownloadFileWS"/> <parameter name="operationName" value="getFile"/> <parameter name="mtomNodePath" value="getFileResponse.return.file"/> </handler>

<globalConfiguration> <requestFlow> <handler type="log"/> </requestFlow> <responseFlow> <handler type="log"/> <handler type="xop"/> </responseFlow> </globalConfiguration>

<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/>

</deployment>

2.c. Axis Response Handler for XOP

package axis.mtom.client.handler;

import java.io.InputStream; import java.io.StringWriter; import java.util.Iterator;

import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPMessage;

6
6

import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult;

import org.apache.axis.AxisFault; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.attachments.AttachmentPart; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.handlers.BasicHandler; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList;

/**

*

Retrieve MTOM/XOP file referenced by "mtomNodePath" parameter

*

from "client-config.wsdd" file.

*

Exposes a Stream including file content to clients through

*

ThreadLocal mechanism.

*

*/ public class XOPHandler extends BasicHandler {

// "client-config.wsdd" parameters private static final String HANDLER_OPTION_MTOM_NODE_PATH = "mtomNodePath"; private static final String HANDLER_OPTION_OPERATION_NAME = "operationName"; private static final String HANDLER_OPTION_SERVICE_NAME = "serviceName";

// Stream including file content

private static ThreadLocal documentStream = new ThreadLocal(); public static InputStream getDocumentStream() { return (InputStream)documentStream.get();

}

/**

* Copy Stream reference to file content by searching specified SOAP node on client-config.wsdd file. */ public void invoke(MessageContext msgContext) throws AxisFault {

// Only process targeted service and operation if (isTargetedServiceAndOperation(msgContext)) {

// Recover SOAP node from client-config.wsdd parameter String mtomNodePath = (String) getOption(HANDLER_OPTION_MTOM_NODE_PATH); if (mtomNodePath == null) { System.out.println("No mtomNodePath parameter specified on client- config.wsdd file"); } else {

String[] pathToNode = mtomNodePath.split("\\."); int nodesLevel = pathToNode.length;

try {

7
7

// Recover SOAP Body Message msg = msgContext.getResponseMessage(); SOAPMessage soapMessage = msgContext.getResponseMessage(); SOAPBody soapBody = soapMessage.getSOAPBody();

// Search mtomNodePath in SOAP Body Node currentNode = null; int currentNodeLevel = 0; NodeList nodeList = soapBody.getChildNodes(); while (currentNodeLevel < nodesLevel && nodeList != null) { currentNode = null; for (int i = 0; i < nodeList.getLength(); i++) { if

(nodeList.item(i).getLocalName().equals(pathToNode[currentNodeLevel]))

{

currentNode = nodeList.item(i);

break;

}

}

if (currentNode != null) { nodeList = currentNode.getChildNodes();

} else {

nodeList = null;

}

currentNodeLevel++;

}

// mtomNodePath found if (currentNode != null) {

// Recover reference to SOAP Attachment from node attribute "Include" String attachmentRef = null; if (currentNode.getChildNodes() != null && currentNode.getChildNodes().getLength() > 0) { NamedNodeMap nnm =

currentNode.getChildNodes().item(0).getAttributes();

for (int j = 0; j < nnm.getLength(); j++) { attachmentRef = nnm.item(j).getNodeValue();

}

// (!) Delete XOP node to prevent Axis service unmarshalling errors

currentNode.getParentNode().removeChild(currentNode);

} else {

System.out.println("No reference founded at node " + mtomNodePath);

}

// Recover SOAP Attachment from attachments using the reference if (attachmentRef != null) {

Iterator attachments = msg.getAttachments(); while (attachments.hasNext()) { AttachmentPart attachmentPart = (AttachmentPart)attachments.next(); if (attachmentPart.getContentIdRef().equals(attachmentRef)) { documentStream.set(attachmentPart.getDataHandler().getInputStream());

}

}

// Perform SOAP Body operations to persist changes TransformerFactory tff = TransformerFactory.newInstance(); Transformer tf = tff.newTransformer(); Source sc = soapMessage.getSOAPPart().getContent(); StringWriter modifiedBody = new StringWriter();

8
8

StreamResult result = new StreamResult(modifiedBody); tf.transform(sc, result); Message modifiedMsg = new Message(modifiedBody.toString(), false); msgContext.setResponseMessage(modifiedMsg);

} else {

System.out.println("No reference to XOP file founded at node " + mtomNodePath);

}

} else {

System.out.println("Node " + mtomNodePath + " not found at Response SOAP Body");

}

}

catch (Exception e) {

throw new AxisFault("Handler exception", e);

}

}

}

}

/**

* Check targeted service and operation from "client-config.wsdd"

parameters

* with runtime service and operation from the web service response.

*/ private boolean isTargetedServiceAndOperation(MessageContext msgContext) {

boolean parametersFound = false;

String serviceName = (String) getOption(HANDLER_OPTION_SERVICE_NAME); if (serviceName == null) { System.out.println("No serviceName property specified on client- config.wsdd file");

}

Service locator = (Service) msgContext.getProperty(Call.WSDL_SERVICE); if (locator != null && locator.getServiceName().toString().equals(serviceName)) { String operationName = (String) getOption(HANDLER_OPTION_OPERATION_NAME); if (operationName == null) { System.out.println("No operationName property specified on client- config.wsdd file");

}

else if (msgContext.getOperation().getName().equals(operationName))

{

parametersFound = true;

}

}

return parametersFound;

}

}

9
9

2. d. Sample web service client

package axis.mtom.client;

import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream;

import axis.mtom.client.handler.XOPHandler;

import cxf.mtom.server.service.DownloadFileWSLocator; import cxf.mtom.server.service.DownloadFileWSSoapBindingStub; import cxf.mtom.server.service.ResponseDownloadFile;

/**

* Access MTOM / XOP service from Axis 1

*/ public class TestClient {

public static void main(String[] args) throws Exception {

// Axis client invocation DownloadFileWSSoapBindingStub binding = (DownloadFileWSSoapBindingStub) new DownloadFileWSLocator().getDownloadFileImplPort(); ResponseDownloadFile rdf = binding.getFile();

// Recovered file name System.out.println(rdf.getFileName());

// Recovered file type System.out.println(rdf.getFileType());

// UNUSED METHOD -> System.out.println(rdf.getFile()); // Recover file content convertStreamToFile(XOPHandler.getDocumentStream(), rdf.getFileName());

}

/**

* Sample method: write Stream to File

* @param is

* @param fileName

* @throws IOException

*/ public static void convertStreamToFile(InputStream is, String fileName) throws IOException {

if (is != null) {

FileOutputStream fos = new FileOutputStream(fileName);

byte[] buffer = new byte[1024]; try { int n; while ((n = is.read(buffer)) != -1) {

10
10

fos.write(buffer, 0, n);

}

} finally { is.close();

fos.close();

}

}

}

}

3. Final considerations

3.a. Sample SOAP Request

<?xml version="1.0" encoding="utf-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soapenv:Body> <getFile xmlns="http://service.server.mtom.cxf/" /> </soapenv:Body> </soapenv:Envelope>

3.b. Sample SOAP Response

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:getFileResponse xmlns:ns2="http://service.server.mtom.cxf/"> <return> <file> <!-- XOP node --> <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:89f719ee-ed51-4955-a80d-103f1d851763-4@cxf.apache.org" /> </file> <fileName>c:/tmp/readme.txt</fileName> <fileType>plain/text</fileType> </return>

</ns2:getFileResponse>

</soap:Body>

</soap:Envelope>

--MIMEBoundary4A7AE55984E7438034

content-type: application/octet-stream content-transfer-encoding: binary content-id: <89f719ee-ed51-4955-a80d-103f1d851763-4@cxf.apache.org>

Binary Data

--MIMEBoundary4A7AE55984E7438034--

3.c. Resources

[1] http://axis.apache.org/axis2/java/core/docs/mtom-guide.html [2] http://cxf.apache.org/docs/writing-a-service-with-spring.html [3] http://axis.apache.org/axis/java/user-

guide.html#WSDL2JavaBuildingStubsSkeletonsAndDataTypesFromWSDL

11
11