javaee / metro-jax-ws

https://javaee.github.io/metro-jax-ws/
Other
132 stars 68 forks source link

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

Open glassfishrobot opened 12 years ago

glassfishrobot commented 12 years ago

Using JAX-WS-RI or Metro I can write a WebService using the com.sun.xml.ws.api.server.AsyncProvider 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 { ...

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

public void invoke(Source source, AsyncProviderCallback 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 { ...

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">

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="";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: 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: <5f88ee1b-0aa1-46ef-bad3-93df9b2aaa02@example.jaxws.sun.com> 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 cbak, WebServiceContext ctxt) { Map attachments = (Map) 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 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)
glassfishrobot commented 12 years ago

Reported by jfk9

glassfishrobot commented 12 years ago

jfk9 said: A more complete description, including a comparison with swaRef approach which does what I want can be found at http://stackoverflow.com/questions/10700340/how-can-i-parse-an-inbound-message-with-mtom-attachments-under-metro-without-pul

glassfishrobot commented 12 years ago

jfk9 said: Metro 2.2 February 20, 2012

glassfishrobot commented 7 years ago

This issue was imported from java.net JIRA JAX_WS-1070