1.1 --- a/src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java Thu Aug 08 10:10:38 2013 -0700 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java Fri Aug 23 09:57:21 2013 +0100 1.3 @@ -30,6 +30,8 @@ 1.4 import com.sun.istack.internal.XMLStreamReaderToContentHandler; 1.5 import com.sun.xml.internal.bind.api.Bridge; 1.6 import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer; 1.7 +import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; 1.8 +import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark; 1.9 import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator; 1.10 import com.sun.xml.internal.ws.api.SOAPVersion; 1.11 import com.sun.xml.internal.ws.api.message.AttachmentSet; 1.12 @@ -37,15 +39,20 @@ 1.13 import com.sun.xml.internal.ws.api.message.HeaderList; 1.14 import com.sun.xml.internal.ws.api.message.Message; 1.15 import com.sun.xml.internal.ws.api.message.MessageHeaders; 1.16 +import com.sun.xml.internal.ws.api.message.StreamingSOAP; 1.17 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; 1.18 import com.sun.xml.internal.ws.encoding.TagInfoset; 1.19 import com.sun.xml.internal.ws.message.AbstractMessageImpl; 1.20 import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl; 1.21 +import com.sun.xml.internal.ws.protocol.soap.VersionMismatchException; 1.22 import com.sun.xml.internal.ws.spi.db.XMLBridge; 1.23 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; 1.24 import com.sun.xml.internal.ws.util.xml.DummyLocation; 1.25 import com.sun.xml.internal.ws.util.xml.StAXSource; 1.26 +import com.sun.xml.internal.ws.util.xml.XMLReaderComposite; 1.27 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter; 1.28 +import com.sun.xml.internal.ws.util.xml.XMLReaderComposite.ElemInfo; 1.29 + 1.30 import org.xml.sax.ContentHandler; 1.31 import org.xml.sax.ErrorHandler; 1.32 import org.xml.sax.SAXException; 1.33 @@ -55,6 +62,7 @@ 1.34 import javax.xml.bind.JAXBException; 1.35 import javax.xml.bind.Unmarshaller; 1.36 import javax.xml.stream.*; 1.37 + 1.38 import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; 1.39 import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; 1.40 import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; 1.41 @@ -62,7 +70,9 @@ 1.42 import javax.xml.ws.WebServiceException; 1.43 import java.util.ArrayList; 1.44 import java.util.Enumeration; 1.45 +import java.util.HashMap; 1.46 import java.util.List; 1.47 +import java.util.Map; 1.48 1.49 /** 1.50 * {@link Message} implementation backed by {@link XMLStreamReader}. 1.51 @@ -70,7 +80,7 @@ 1.52 * TODO: we need another message class that keeps {@link XMLStreamReader} that points 1.53 * at the start of the envelope element. 1.54 */ 1.55 -public class StreamMessage extends AbstractMessageImpl { 1.56 +public class StreamMessage extends AbstractMessageImpl implements StreamingSOAP { 1.57 /** 1.58 * The reader will be positioned at 1.59 * the first child of the SOAP body 1.60 @@ -93,44 +103,51 @@ 1.61 */ 1.62 private String bodyEpilogue = null; 1.63 1.64 - private final String payloadLocalName; 1.65 + private String payloadLocalName; 1.66 1.67 - private final String payloadNamespaceURI; 1.68 - 1.69 - /** 1.70 - * infoset about the SOAP envelope, header, and body. 1.71 - * 1.72 - * <p> 1.73 - * If the creater of this object didn't care about those, 1.74 - * we use stock values. 1.75 - */ 1.76 - private @NotNull TagInfoset envelopeTag; 1.77 - private @NotNull TagInfoset headerTag; 1.78 - private @NotNull TagInfoset bodyTag; 1.79 + private String payloadNamespaceURI; 1.80 1.81 /** 1.82 * Used only for debugging. This records where the message was consumed. 1.83 */ 1.84 private Throwable consumedAt; 1.85 1.86 - /** 1.87 - * Default s:Envelope, s:Header, and s:Body tag infoset definitions. 1.88 - * 1.89 - * We need 3 for SOAP 1.1, 3 for SOAP 1.2. 1.90 - */ 1.91 - private static final TagInfoset[] DEFAULT_TAGS; 1.92 - 1.93 - static { 1.94 - DEFAULT_TAGS = new TagInfoset[6]; 1.95 - create(SOAPVersion.SOAP_11); 1.96 - create(SOAPVersion.SOAP_12); 1.97 - } 1.98 + private XMLStreamReader envelopeReader; 1.99 1.100 public StreamMessage(SOAPVersion v) { 1.101 super(v); 1.102 payloadLocalName = null; 1.103 payloadNamespaceURI = null; 1.104 } 1.105 + 1.106 + public StreamMessage(SOAPVersion v, @NotNull XMLStreamReader envelope, @NotNull AttachmentSet attachments) { 1.107 + super(v); 1.108 + envelopeReader = envelope; 1.109 + attachmentSet = attachments; 1.110 + } 1.111 + 1.112 + public XMLStreamReader readEnvelope() { 1.113 + if (envelopeReader == null) { 1.114 + List<XMLStreamReader> hReaders = new java.util.ArrayList<XMLStreamReader>(); 1.115 + ElemInfo envElem = new ElemInfo(envelopeTag, null); 1.116 + ElemInfo hdrElem = (headerTag != null) ? new ElemInfo(headerTag, envElem) : null; 1.117 + ElemInfo bdyElem = new ElemInfo(bodyTag, envElem); 1.118 + for (Header h : getHeaders().asList()) { 1.119 + try { 1.120 + hReaders.add(h.readHeader()); 1.121 + } catch (XMLStreamException e) { 1.122 + throw new RuntimeException(e); 1.123 + } 1.124 + } 1.125 + XMLStreamReader soapHeader = (hdrElem != null) ? new XMLReaderComposite(hdrElem, hReaders.toArray(new XMLStreamReader[hReaders.size()])) : null; 1.126 + XMLStreamReader[] payload = {readPayload()}; 1.127 + XMLStreamReader soapBody = new XMLReaderComposite(bdyElem, payload); 1.128 + XMLStreamReader[] soapContent = (soapHeader != null) ? new XMLStreamReader[]{soapHeader, soapBody} : new XMLStreamReader[]{soapBody}; 1.129 + return new XMLReaderComposite(envElem, soapContent); 1.130 + } 1.131 + return envelopeReader; 1.132 + } 1.133 + 1.134 /** 1.135 * Creates a {@link StreamMessage} from a {@link XMLStreamReader} 1.136 * that points at the start element of the payload, and headers. 1.137 @@ -147,6 +164,10 @@ 1.138 */ 1.139 public StreamMessage(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { 1.140 super(soapVersion); 1.141 + init(headers, attachmentSet, reader, soapVersion); 1.142 + } 1.143 + 1.144 + private void init(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { 1.145 this.headers = headers; 1.146 this.attachmentSet = attachmentSet; 1.147 this.reader = reader; 1.148 @@ -175,9 +196,9 @@ 1.149 1.150 // use the default infoset representation for headers 1.151 int base = soapVersion.ordinal()*3; 1.152 - this.envelopeTag = DEFAULT_TAGS[base]; 1.153 - this.headerTag = DEFAULT_TAGS[base+1]; 1.154 - this.bodyTag = DEFAULT_TAGS[base+2]; 1.155 + this.envelopeTag = DEFAULT_TAGS.get(base); 1.156 + this.headerTag = DEFAULT_TAGS.get(base+1); 1.157 + this.bodyTag = DEFAULT_TAGS.get(base+2); 1.158 } 1.159 1.160 /** 1.161 @@ -197,7 +218,12 @@ 1.162 } 1.163 1.164 public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @Nullable String bodyPrologue, @NotNull TagInfoset bodyTag, @Nullable String bodyEpilogue, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { 1.165 - this(headers,attachmentSet,reader,soapVersion); 1.166 + super(soapVersion); 1.167 + init(envelopeTag, headerTag, attachmentSet, headers, bodyPrologue, bodyTag, bodyEpilogue, reader, soapVersion); 1.168 + } 1.169 + 1.170 + private void init(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @Nullable String bodyPrologue, @NotNull TagInfoset bodyTag, @Nullable String bodyEpilogue, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { 1.171 + init(headers,attachmentSet,reader,soapVersion); 1.172 if(envelopeTag == null ) { 1.173 throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null"); 1.174 } 1.175 @@ -212,10 +238,12 @@ 1.176 } 1.177 1.178 public boolean hasHeaders() { 1.179 + if ( envelopeReader != null ) readEnvelope(this); 1.180 return headers!=null && headers.hasHeaders(); 1.181 } 1.182 1.183 public MessageHeaders getHeaders() { 1.184 + if ( envelopeReader != null ) readEnvelope(this); 1.185 if (headers == null) { 1.186 headers = new HeaderList(getSOAPVersion()); 1.187 } 1.188 @@ -223,14 +251,17 @@ 1.189 } 1.190 1.191 public String getPayloadLocalPart() { 1.192 + if ( envelopeReader != null ) readEnvelope(this); 1.193 return payloadLocalName; 1.194 } 1.195 1.196 public String getPayloadNamespaceURI() { 1.197 + if ( envelopeReader != null ) readEnvelope(this); 1.198 return payloadNamespaceURI; 1.199 } 1.200 1.201 public boolean hasPayload() { 1.202 + if ( envelopeReader != null ) readEnvelope(this); 1.203 return payloadLocalName!=null; 1.204 } 1.205 1.206 @@ -329,6 +360,7 @@ 1.207 } 1.208 1.209 public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException { 1.210 + if ( envelopeReader != null ) readEnvelope(this); 1.211 assert unconsumed(); 1.212 1.213 if(payloadLocalName==null) { 1.214 @@ -379,6 +411,7 @@ 1.215 } 1.216 1.217 public void writeTo(XMLStreamWriter sw) throws XMLStreamException{ 1.218 + if ( envelopeReader != null ) readEnvelope(this); 1.219 writeEnvelope(sw); 1.220 } 1.221 1.222 @@ -387,6 +420,7 @@ 1.223 * @param writer 1.224 */ 1.225 private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException { 1.226 + if ( envelopeReader != null ) readEnvelope(this); 1.227 writer.writeStartDocument(); 1.228 envelopeTag.writeStart(writer); 1.229 1.230 @@ -411,6 +445,7 @@ 1.231 } 1.232 1.233 public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException { 1.234 + if ( envelopeReader != null ) readEnvelope(this); 1.235 assert unconsumed(); 1.236 1.237 try { 1.238 @@ -465,8 +500,10 @@ 1.239 } 1.240 } 1.241 1.242 - // TODO: this method should be probably rewritten to respect spaces between eelements; is it used at all? 1.243 + // TODO: this method should be probably rewritten to respect spaces between elements; is it used at all? 1.244 + @Override 1.245 public Message copy() { 1.246 + if ( envelopeReader != null ) readEnvelope(this); 1.247 try { 1.248 assert unconsumed(); 1.249 consumedAt = null; // but we don't want to mark it as consumed 1.250 @@ -528,6 +565,7 @@ 1.251 } 1.252 1.253 public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException { 1.254 + if ( envelopeReader != null ) readEnvelope(this); 1.255 contentHandler.setDocumentLocator(NULL_LOCATOR); 1.256 contentHandler.startDocument(); 1.257 envelopeTag.writeStart(contentHandler); 1.258 @@ -570,23 +608,159 @@ 1.259 return true; 1.260 } 1.261 1.262 - private static void create(SOAPVersion v) { 1.263 - int base = v.ordinal()*3; 1.264 - DEFAULT_TAGS[base ] = new TagInfoset(v.nsUri,"Envelope","S",EMPTY_ATTS,"S",v.nsUri); 1.265 - DEFAULT_TAGS[base+1] = new TagInfoset(v.nsUri,"Header","S",EMPTY_ATTS); 1.266 - DEFAULT_TAGS[base+2] = new TagInfoset(v.nsUri,"Body","S",EMPTY_ATTS); 1.267 - } 1.268 - 1.269 public String getBodyPrologue() { 1.270 + if ( envelopeReader != null ) readEnvelope(this); 1.271 return bodyPrologue; 1.272 } 1.273 1.274 public String getBodyEpilogue() { 1.275 + if ( envelopeReader != null ) readEnvelope(this); 1.276 return bodyEpilogue; 1.277 } 1.278 1.279 public XMLStreamReader getReader() { 1.280 + if ( envelopeReader != null ) readEnvelope(this); 1.281 assert unconsumed(); 1.282 return reader; 1.283 } 1.284 + 1.285 + 1.286 + private static final String SOAP_ENVELOPE = "Envelope"; 1.287 + private static final String SOAP_HEADER = "Header"; 1.288 + private static final String SOAP_BODY = "Body"; 1.289 + 1.290 + protected interface StreamHeaderDecoder { 1.291 + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark); 1.292 + } 1.293 + 1.294 + static final StreamHeaderDecoder SOAP12StreamHeaderDecoder = new StreamHeaderDecoder() { 1.295 + @Override 1.296 + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) { 1.297 + return new StreamHeader12(reader, mark); 1.298 + } 1.299 + }; 1.300 + 1.301 + static final StreamHeaderDecoder SOAP11StreamHeaderDecoder = new StreamHeaderDecoder() { 1.302 + @Override 1.303 + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) { 1.304 + return new StreamHeader11(reader, mark); 1.305 + } 1.306 + }; 1.307 + 1.308 + static private void readEnvelope(StreamMessage message) { 1.309 + if ( message.envelopeReader == null ) return; 1.310 + XMLStreamReader reader = message.envelopeReader; 1.311 + message.envelopeReader = null; 1.312 + SOAPVersion soapVersion = message.soapVersion; 1.313 + // Move to soap:Envelope and verify 1.314 + if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT) 1.315 + XMLStreamReaderUtil.nextElementContent(reader); 1.316 + XMLStreamReaderUtil.verifyReaderState(reader,XMLStreamConstants.START_ELEMENT); 1.317 + if (SOAP_ENVELOPE.equals(reader.getLocalName()) && !soapVersion.nsUri.equals(reader.getNamespaceURI())) { 1.318 + throw new VersionMismatchException(soapVersion, soapVersion.nsUri, reader.getNamespaceURI()); 1.319 + } 1.320 + XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_ENVELOPE); 1.321 + 1.322 + TagInfoset envelopeTag = new TagInfoset(reader); 1.323 + 1.324 + // Collect namespaces on soap:Envelope 1.325 + Map<String,String> namespaces = new HashMap<String,String>(); 1.326 + for(int i=0; i< reader.getNamespaceCount();i++){ 1.327 + namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); 1.328 + } 1.329 + 1.330 + // Move to next element 1.331 + XMLStreamReaderUtil.nextElementContent(reader); 1.332 + XMLStreamReaderUtil.verifyReaderState(reader, 1.333 + javax.xml.stream.XMLStreamConstants.START_ELEMENT); 1.334 + 1.335 + HeaderList headers = null; 1.336 + TagInfoset headerTag = null; 1.337 + 1.338 + if (reader.getLocalName().equals(SOAP_HEADER) 1.339 + && reader.getNamespaceURI().equals(soapVersion.nsUri)) { 1.340 + headerTag = new TagInfoset(reader); 1.341 + 1.342 + // Collect namespaces on soap:Header 1.343 + for(int i=0; i< reader.getNamespaceCount();i++){ 1.344 + namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); 1.345 + } 1.346 + // skip <soap:Header> 1.347 + XMLStreamReaderUtil.nextElementContent(reader); 1.348 + 1.349 + // If SOAP header blocks are present (i.e. not <soap:Header/>) 1.350 + if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) { 1.351 + headers = new HeaderList(soapVersion); 1.352 + 1.353 + try { 1.354 + // Cache SOAP header blocks 1.355 + StreamHeaderDecoder headerDecoder = SOAPVersion.SOAP_11.equals(soapVersion) ? SOAP11StreamHeaderDecoder : SOAP12StreamHeaderDecoder; 1.356 + cacheHeaders(reader, namespaces, headers, headerDecoder); 1.357 + } catch (XMLStreamException e) { 1.358 + // TODO need to throw more meaningful exception 1.359 + throw new WebServiceException(e); 1.360 + } 1.361 + } 1.362 + 1.363 + // Move to soap:Body 1.364 + XMLStreamReaderUtil.nextElementContent(reader); 1.365 + } 1.366 + 1.367 + // Verify that <soap:Body> is present 1.368 + XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_BODY); 1.369 + TagInfoset bodyTag = new TagInfoset(reader); 1.370 + 1.371 + String bodyPrologue = XMLStreamReaderUtil.nextWhiteSpaceContent(reader); 1.372 + message.init(envelopeTag,headerTag,message.attachmentSet,headers,bodyPrologue,bodyTag,null,reader,soapVersion); 1.373 + // when there's no payload, 1.374 + // it's tempting to use EmptyMessageImpl, but it doesn't preserve the infoset 1.375 + // of <envelope>,<header>, and <body>, so we need to stick to StreamMessage. 1.376 + } 1.377 + 1.378 + 1.379 + private static XMLStreamBuffer cacheHeaders(XMLStreamReader reader, 1.380 + Map<String, String> namespaces, HeaderList headers, 1.381 + StreamHeaderDecoder headerDecoder) throws XMLStreamException { 1.382 + MutableXMLStreamBuffer buffer = createXMLStreamBuffer(); 1.383 + StreamReaderBufferCreator creator = new StreamReaderBufferCreator(); 1.384 + creator.setXMLStreamBuffer(buffer); 1.385 + 1.386 + // Reader is positioned at the first header block 1.387 + while(reader.getEventType() == javax.xml.stream.XMLStreamConstants.START_ELEMENT) { 1.388 + Map<String,String> headerBlockNamespaces = namespaces; 1.389 + 1.390 + // Collect namespaces on SOAP header block 1.391 + if (reader.getNamespaceCount() > 0) { 1.392 + headerBlockNamespaces = new HashMap<String,String>(namespaces); 1.393 + for (int i = 0; i < reader.getNamespaceCount(); i++) { 1.394 + headerBlockNamespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); 1.395 + } 1.396 + } 1.397 + 1.398 + // Mark 1.399 + XMLStreamBuffer mark = new XMLStreamBufferMark(headerBlockNamespaces, creator); 1.400 + // Create Header 1.401 + headers.add(headerDecoder.decodeHeader(reader, mark)); 1.402 + 1.403 + 1.404 + // Cache the header block 1.405 + // After caching Reader will be positioned at next header block or 1.406 + // the end of the </soap:header> 1.407 + creator.createElementFragment(reader, false); 1.408 + if (reader.getEventType() != XMLStreamConstants.START_ELEMENT && 1.409 + reader.getEventType() != XMLStreamConstants.END_ELEMENT) { 1.410 + XMLStreamReaderUtil.nextElementContent(reader); 1.411 + } 1.412 + } 1.413 + 1.414 + return buffer; 1.415 + } 1.416 + 1.417 + private static MutableXMLStreamBuffer createXMLStreamBuffer() { 1.418 + // TODO: Decode should own one MutableXMLStreamBuffer for reuse 1.419 + // since it is more efficient. ISSUE: possible issue with 1.420 + // lifetime of information in the buffer if accessed beyond 1.421 + // the pipe line. 1.422 + return new MutableXMLStreamBuffer(); 1.423 + } 1.424 }