使用 JAX-WS-RI 或 Metro,我可以使用 com.sun.xml.ws.api.server.AsyncProvider 接口编写 WebService。
我可以选择获取包括 SOAP 标头在内的整个消息
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> {
...
然后我可以写一些东西来解析消息并相应地处理
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);
}
}
然而,这样做的部分原因是为了使现有的基于 XML 的应用程序适应 SOAP 堆栈。该应用程序为它所接受的消息定义了一套完整的模式,并且很容易生成一个 WSDL 来定义服务。
对于小的 XML 消息,这一切都很好。
但是,如果我希望在一些消息庞大的操作中以更面向流的方式处理 XML,我会遇到两个使用 MTOM 附件的问题。我将我的 Provider 的设置更改如下;
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> {
...
我将适当的 MTOM 策略添加到我的 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>
并酌情为该操作声明我的消息模式
<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"/>
适当设置绑定;
<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>
我生成客户端并修改代码
port = new AddNumbersService().getAddNumbersPort(new SyncStartForAsyncFeature(), new MTOMFeature(1000));
Source data = new StreamSource(new FileInputStream(file));
String result = port.sendBulk(data);
并且 http 转储显示数据正在以多部分 mime 的形式发送,并在“消息正文”中使用适当的 xop:include 元素
---[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!
在服务器内部,我得到一个附件;
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");
}
然后我希望顶部解析传入的消息并获取批量附件的上下文 - 例如包含它的元素的名称 - 但不是批量附件本身,YET;
XMLStreamWriter sw = new MyXMLStreamWriter();
source.writeTo(sw);
希望附件解析器还没有读取所有输入的 HTTP 流,或者如果它已经将它分块到一个文件中,然后我可以在其中逐块读取它。问题是我找不到一种解析 Message 的方法,它不会将传入的消息转换为附件中的 base64 编码数据。在我得到指示元素开始的 SAX/StaX 事件后,它后面跟着一个字符事件,它是 base64 编码的数据。查看 StreamMessage 的代码,无法“插入”任何 XMLReader 或 XMLStreamReader 等。在 Message 已经绑定到的现有 XMLStreamReader 的“前面”(并不令人惊讶,因为我们正在处理消息)。但是那个 XMLStreamReader 实现是一个 com.sun.xml.ws.encoding.MtomCodec$MtomXMLStreamReaderEx ,它总是' 将一行中的附件解包到内存中。无法对 com.sun.xml.ws.encoding.MtomCodec 类进行参数化,以便它不会首先将事件传递给其内部的 MtomXMLStreamReaderEx 类,该类将内联附件解包。
我尝试将附件读入一个临时文件,然后将消息上下文附件替换为 FileDataSource,但由于 Metro 忽略了这些,因此只会使原始 DataSource 引发 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);
}
我还尝试通过将注释设置为禁用来关闭提供程序中的 MTOM;
@MTOM(enabled=false)
然后删除它,然后删除 StreamingAttachment 注释无济于事。我还在 sun-jaxws.xml 文件中为它的端点元素设置了 enable-mtom="false" 属性,同样无济于事;似乎 xop:Include 解析是硬连线的。我不想更改 wsdl,因为我确实希望客户尽可能使用 MTOM。(但请参阅下面的我的 swaRef 答案)
如果我使用 swaRef,我可以让 Metro 做我想做的事,但我的理解是 MTOM 是获得支持的协议?使用 swaRef 我必须编辑 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"/>
我还必须生成一个新客户端并以不同的方式调用 sendBulk 方法;
FileDataSource newSource = new FileDataSource(file);
DataHandler data = new DataHandler(newSource);
String result = port.sendBulk(data);
但是现在当我解析数据元素时,我得到了相应的 cid,然后我可以根据需要使用它来检索附件;
DataHandler handler = attachments.get(dataText.substring("cid:".length()));
InputStream input = handler.getInputStream();
因此,对于 MTOM 和 Metro,尽管附件可能还没有被读取到我解析数据元素的程度,或者确实以块的形式有效地流式传输到临时文件,但我无法读取输入消息的“其余部分”没有将整个依恋读入记忆中,这似乎是自我挫败?除了倾倒 Metro - 建议重新:Axis,欢迎 CXF - 有没有人发现这个问题的任何方法?
记录了一个功能请求。