Using JAX-WS-RI or Metro, I can write WebService using the interface com.sun.xml.ws.api.server.AsyncProvider.
I can choose to receive the entire 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> { ...
Then I can write something that 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, the reason for this is the adaptation of an existing XML-based application to the SOAP stack. The application defines a complete set of schemes for the messages it receives, and it is easy to create a WSDL to define the service.
For small XML messages, everything works fine.
However, if I want to process XML in a more stream-oriented manner in some operations where messages are bulky, I run into two problems using MTOM attachments. I change my provider settings 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 schemes for this operation accordingly
<complexType name="sendBulk"> <sequence> <element name="data" type="base64Binary" xmime:expectedContentTypes="application/xml" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/> </sequence> </complexType> <element name="sendBulk" type="tns:sendBulk"/> <complexType name="sendBulkResponse"> <sequence> <element name="return" type="xsd:string" /> </sequence> </complexType> <element name="sendBulkResponse" type="tns:sendBulkResponse"/>
Customize bindings as needed;
<binding name="AddNumbersBinding" type="tns:AddNumbersPortType"> <wsp:PolicyReference URI="#AddNumbersAsynch_policy"/> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="sendBulk"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding>
I create a client and change 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 sent as a multiparty mime with the corresponding 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* daff762b-9651-40aa-ae2f-30d30a3c5e2e@example.jaxws.sun.com >";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* daff762b-9651-40aa-ae2f-30d30a3c5e2e@example.jaxws.sun.com > Content-Type: application/xop+xml;charset=utf-8;type="text/xml" Content-Transfer-Encoding: binary <?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:8080/ProviderTest2/addnumbersAsynchMessage</To><Action xmlns="http://www.w3.org/2005/08/addressing" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" S:mustUnderstand="1">http://message.asynch.duke.example.org/AddNumbersPortType/sendBulkRequest</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing"> <Address>http://www.w3.org/2005/08/addressing/anonymous</Address> </ReplyTo><FaultTo xmlns="http://www.w3.org/2005/08/addressing"> <Address>http://www.w3.org/2005/08/addressing/anonymous</Address> </FaultTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:406ce4bc-7332-4849-b16b-69cec6ca21ea</MessageID></S:Header><S:Body><sendBulk xmlns="http://message.asynch.duke.example.org"><data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid: 5f88ee1b-0aa1-46ef-bad3-93df9b2aaa02@example.jaxws.sun.com "/></data></sendBulk></S:Body></S:Envelope> --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<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"); }
Then I want the top parsing to output the incoming message and get the context for the main attachment - for example, the name of the element that covers it, but not the attachment itself, YET;
XMLStreamWriter sw = new MyXMLStreamWriter(); source.writeTo(sw);
We hope that the attachment parser has not yet read all this part of the incoming HTTP stream, or if you have it, it puts it in a file where I can read a piece of it with a piece. The problem is that I cannot find a way to parse a message that does not convert incoming data to base64 encoded data in the application. After I receive the SAX / StaX event indicating the start of the element, it is followed by an event with characters, which is base64 encoded. Looking at the StreamMessage code, it is impossible to βinsertβ any XMLReader or XMLStreamReader, etc. Before the "existing XMLStreamReader, to which the message is already attached (not surprised, because we are part of the message processing path). But the XMLStreamReader implementation is com.sun.xml.ws.encoding.MtomCodec $ MtomXMLStreamReaderEx, which always" unpacks "the attachments in the string It is not possible to parameterize the class com.sun.xml.ws.encoding.MtomCodec so that it does not pass events first to its internal class MtomXMLStreamReaderEx, which decompresses the attached attachment.
I tried to read the attachment in a temporary file, and then replaced the message context attachments for FileDataSource, but since Metro ignored them, it only caused the original DataSource to throw an error like ReadOnce.
Map<String, DataHandler> attachments = (Map<String, DataHandler>) ctxt.getMessageContext().get(javax.xml.ws.handler.MessageContext.INBOUND_MESSAGE_ATTACHMENTS); HashMap<String, DataHandler> clonedAttachments = new HashMap<String, DataHandler>(attachments); for(String attachmentKey: attachments.keySet()) { StreamingDataHandler handler = (StreamingDataHandler) attachments.get(attachmentKey); File tempFile = File.createTempFile("Attachment" + attachmentKey, ".xmlb64", new File("C:\\Users\\Administrator\\workspaceTool1\\TomcatWorkingDir")); handler.moveTo(tempFile); FileDataSource newSource = new FileDataSource(tempFile); clonedAttachments.put(attachmentKey, new DataHandler(newSource)); System.out.println("Got attachment " + attachmentKey + " of type " + attachments.get(attachmentKey)); } if(attachments.isEmpty()) { System.out.println("Got No attachments"); } else { ctxt.getMessageContext().put(javax.xml.ws.handler.MessageContext.INBOUND_MESSAGE_ATTACHMENTS, clonedAttachments); }
I also tried disabling MTOM in the provider by disabling annotation;
@MTOM(enabled=false)
and then delete it, and then the StreamingAttachment annotation to no avail. I also set the enable-mtom = "false" attribute on the endpoint element for it in the sun-jaxws.xml file, again to no avail; it seems that xop: Include parsing is hardwired. I do not want to change wsdl because I want clients to use MTOM if possible. (but see my swaRef answer below)
Can I get Metro to do what I want if I use swaRef, but I realized that MTOM is the protocol that got the support? With swaRef, I need to edit the WSDL;
<types> <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://swaRef.asynch.duke.example.org" elementFormDefault="qualified"> <xsd:import namespace="http://ws-i.org/profiles/basic/1.1/xsd" schemaLocation="http://www.ws-i.org/profiles/basic/1.1/swaref.xsd"/> <complexType name="sendBulk"> <sequence> <element name="data" type="wsi:swaRef" xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"/> </sequence> </complexType> <element name="sendBulk" type="tns:sendBulk"/>
I also need to generate a new client and call the sendBulk method differently;
FileDataSource newSource = new FileDataSource(file); DataHandler data = new DataHandler(newSource); String result = port.sendBulk(data);
But now, when I analyze the data element, I get the corresponding cid, which I can then use to extract the attachment, as I want;
DataHandler handler = attachments.get(dataText.substring("cid:".length())); InputStream input = handler.getInputStream();
So, with MTOM and Metro, although the attachments may not have been read until the moment when I analyze the data element or really effectively transfer the fragments to the temporary file, I cannot read the βrestβ input message without reading the entire memory attachment, which seems self-destructive? Besides dumping Metro - re: Axis suggestions, CXF is welcome - is there any way around this that anyone has discovered?
Function request recorded.