metro-jax-ws icon indicating copy to clipboard operation
metro-jax-ws copied to clipboard

How can I parse an inbound message with MTOM attachments under Metro without pulling in all the attachment data?

Open Tomas-Kraus opened this issue 2 years ago • 0 comments

Using JAX-WS-RI or Metro I can write a WebService using the com.sun.xml.ws.api.server.AsyncProvider<T> interface.

I can choose to get the whole message including the SOAP headers

import javax.xml.transform.Source; import com.sun.xml.ws.api.server.AsyncProvider; import com.sun.xml.ws.api.server.AsyncProviderCallback;

import javax.xml.ws.ServiceMode; import javax.xml.ws.WebServiceContext; import javax.xml.ws.WebServiceProvider;

@ServiceMode(value=Service.Mode.MESSAGE) @WebServiceProvider() public class AddNumbersAsynchImpl implements AsyncProvider<Source> { ...

I can then write something which parses the message and processes accordingly

public void invoke(Source source, AsyncProviderCallback<Source> cbak, WebServiceContext ctxt) { DOMResult dom = new DOMResult(); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.transform(source, dom);

XPath xpath = XPathFactory.newInstance().newXPath(); xpath.setNamespaceContext(new MyNamespaceContext());

String opName = (String) xpath.evaluate("name(/S:Envelope/S:Body/X:*[1])", dom.getNode(), XPathConstants.STRING);

if(knownOp(opName))

{ doOp(opName, dom.getNode(), cbak); }

else

{ doFault("Unknown operation " + opName, cbak); }

}

However part of the reason for doing this is to adapt an existing XML based application into the SOAP stack. The application defines a complete set of schemas for the messages that it takes and it easy to generate a WSDL to define the service.

For small XML messages this all works fine.

However if I wish to process the XML in a more stream orientated fashion in some operations where the messages are bulky I come across two problems using MTOM attachments. I change the set up of my Provider as follows;

import com.sun.xml.ws.api.message.Message; import javax.xml.ws.soap.MTOM; import com.sun.xml.ws.developer.StreamingAttachment; import com.sun.xml.ws.developer.StreamingDataHandler;

ServiceMode(value=Service.Mode.MESSAGE) @WebServiceProvider() @StreamingAttachment(parseEagerly=true, memoryThreshold=1000L) @MTOM(enabled=true, threshold=1000) public class AddNumbersAsynchImplMessage implements AsyncProvider<Message> { ...

I Add the appropriate MTOM policy to my WSDL;

<definitions name="AddNumbersAsynch" targetNamespace="http://asynch.duke.example.org" xmlns:tns="http://asynch.duke.example.org" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsrm="http://docs.oasis-open.org/ws-rx/wsrmp/200702" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsoma="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <wsp:Policy wsu:Id="AddNumbersAsynch_policy"> <wsam:Addressing wsp:Optional="false" /> wsoma:OptimizedMimeSerialization/ </wsp:Policy>

And declare my message schemas for this operation as appropriate

Setting up the binding as appropriate;

I generate the client and amend the code

port = new AddNumbersService().getAddNumbersPort(new SyncStartForAsyncFeature(), new MTOMFeature(1000)); Source data = new StreamSource(new FileInputStream(file)); String result = port.sendBulk(data);

and the http dump shows that the data is being sent as a multipart mime with the appropriate xop:include element in the 'message body'

--[HTTP request - http://localhost:8080/ProviderTest2/addnumbersAsynchMessage]-- Accept: text/xml, multipart/related Content-Type: multipart/related;start="rootpart*[email protected]";type="application/xop+xml";boundary="uuid:daff762b-9651-40aa-ae2f-30d30a3c5e2e";start-info="text/xml" SOAPAction: "http://message.asynch.duke.example.org/AddNumbersPortType/sendBulkRequest" User-Agent: Metro/2.2 (branches/2.2-7015; 2012-02-20T20:31:25+0000) JAXWS-RI/2.2.6 JAXWS/2.2 svn-revision#unknown --uuid:daff762b-9651-40aa-ae2f-30d30a3c5e2e Content-Id: rootpart*[email protected] Content-Type: application/xop+xml;charset=utf-8;type="text/xml" Content-Transfer-Encoding: binary

[http://localhost:8080/ProviderTest2/addnumbersAsynchMessage](http://localhost:8080/ProviderTest2/addnumbersAsynchMessage)[http://message.asynch.duke.example.org/AddNumbersPortType/sendBulkRequest](http://message.asynch.duke.example.org/AddNumbersPortType/sendBulkRequest)
[http://www.w3.org/2005/08/addressing/anonymous](http://www.w3.org/2005/08/addressing/anonymous)
[http://www.w3.org/2005/08/addressing/anonymous](http://www.w3.org/2005/08/addressing/anonymous)
uuid:406ce4bc-7332-4849-b16b-69cec6ca21ea
--uuid:daff762b-9651-40aa-ae2f-30d30a3c5e2e Content-Id: Content-Type: application/xml; charset=UTF-8 Content-Transfer-Encoding: binary

....snip!

Inside the server I get an attachment;

public void invoke(Message source, AsyncProviderCallback<Message> cbak, WebServiceContext ctxt) {

Map<String, DataHandler> attachments = (Map<String, DataHandler>) ctxt.getMessageContext().get(javax.xml.ws.handler.MessageContext.INBOUND_MESSAGE_ATTACHMENTS); for(String attachmentKey: attachments.keySet())

{ StreamingDataHandler handler = (StreamingDataHandler) attachments.get(attachmentKey); System.out.println("Got attachment " + attachmentKey + " of type " + attachments.get(attachmentKey)); }

if(attachments.isEmpty())

{ System.out.println("Got No attachments"); }

I then wish top parse the incoming message and get the context for the bulk attachment - for example the name of the element that encloses it - but not the bulk attachment itself, YET;

XMLStreamWriter sw = new MyXMLStreamWriter(); source.writeTo(sw);

Hopefully the attachment parser has not read all that part of the incoimuing HTTP stream yet, or if it has it has chunked it off into a file, where I can then read it chunk by chunk. The trouble is that I cannot find a way of parsing Message that does not convert the incoming xop:Include into the base64 encoded data in the attachment. After I get the SAX/StaX event indicating the start of the element, it is followed by a characters event which is the base64 encoded data. Looking at the code for StreamMessage there is no way to "insert" any XMLReader or XMLStreamReader etc. 'in front of' the existing XMLStreamReader that the Message is already bound to ( not suprising as we are part way through message processing). But that XMLStreamReader implementation is a com.sun.xml.ws.encoding.MtomCodec$MtomXMLStreamReaderEx which always 'unpacks' attachments in line into memory. There is no way to paramterize the com.sun.xml.ws.encoding.MtomCodec class so that it does not first pass events to its inner MtomXMLStreamReaderEx class which unpacks the attachment inline.

So though the aatachments may have not been read up to this point, or indeed streamed efficiently in chunks to a temporary file, I have no way to read the "rest" of the input message without reading the whole of the attachment into memory which seems self defeating? Apart from dumping Metro - suggestions re: Axis, CXF welcomed - is there any way around this anyone has discovered? For example wrapping the DataHandler in the attachments map in the MessageContext?

Environment

Windows Server 2008 SP2 x86, Java 1.6 (build 1.6.0_32-b05)

Source: https://github.com/javaee/metro-jax-ws/issues/1070 Author: glassfishrobot

Tomas-Kraus avatar Jun 02 '22 17:06 Tomas-Kraus