--- old/src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java 2013-08-13 18:18:02.476014983 +0200 +++ new/src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java 2013-08-13 18:18:02.424012680 +0200 @@ -30,6 +30,8 @@ import com.sun.istack.internal.XMLStreamReaderToContentHandler; import com.sun.xml.internal.bind.api.Bridge; import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer; +import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; +import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark; import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator; import com.sun.xml.internal.ws.api.SOAPVersion; import com.sun.xml.internal.ws.api.message.AttachmentSet; @@ -37,15 +39,20 @@ import com.sun.xml.internal.ws.api.message.HeaderList; import com.sun.xml.internal.ws.api.message.Message; import com.sun.xml.internal.ws.api.message.MessageHeaders; +import com.sun.xml.internal.ws.api.message.StreamingSOAP; import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; import com.sun.xml.internal.ws.encoding.TagInfoset; import com.sun.xml.internal.ws.message.AbstractMessageImpl; import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl; +import com.sun.xml.internal.ws.protocol.soap.VersionMismatchException; import com.sun.xml.internal.ws.spi.db.XMLBridge; import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; import com.sun.xml.internal.ws.util.xml.DummyLocation; import com.sun.xml.internal.ws.util.xml.StAXSource; +import com.sun.xml.internal.ws.util.xml.XMLReaderComposite; import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter; +import com.sun.xml.internal.ws.util.xml.XMLReaderComposite.ElemInfo; + import org.xml.sax.ContentHandler; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; @@ -55,6 +62,7 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.stream.*; + import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; @@ -62,7 +70,9 @@ import javax.xml.ws.WebServiceException; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * {@link Message} implementation backed by {@link XMLStreamReader}. @@ -70,7 +80,7 @@ * TODO: we need another message class that keeps {@link XMLStreamReader} that points * at the start of the envelope element. */ -public class StreamMessage extends AbstractMessageImpl { +public class StreamMessage extends AbstractMessageImpl implements StreamingSOAP { /** * The reader will be positioned at * the first child of the SOAP body @@ -93,44 +103,51 @@ */ private String bodyEpilogue = null; - private final String payloadLocalName; + private String payloadLocalName; - private final String payloadNamespaceURI; - - /** - * infoset about the SOAP envelope, header, and body. - * - *

- * If the creater of this object didn't care about those, - * we use stock values. - */ - private @NotNull TagInfoset envelopeTag; - private @NotNull TagInfoset headerTag; - private @NotNull TagInfoset bodyTag; + private String payloadNamespaceURI; /** * Used only for debugging. This records where the message was consumed. */ private Throwable consumedAt; - /** - * Default s:Envelope, s:Header, and s:Body tag infoset definitions. - * - * We need 3 for SOAP 1.1, 3 for SOAP 1.2. - */ - private static final TagInfoset[] DEFAULT_TAGS; - - static { - DEFAULT_TAGS = new TagInfoset[6]; - create(SOAPVersion.SOAP_11); - create(SOAPVersion.SOAP_12); - } + private XMLStreamReader envelopeReader; public StreamMessage(SOAPVersion v) { super(v); payloadLocalName = null; payloadNamespaceURI = null; } + + public StreamMessage(SOAPVersion v, @NotNull XMLStreamReader envelope, @NotNull AttachmentSet attachments) { + super(v); + envelopeReader = envelope; + attachmentSet = attachments; + } + + public XMLStreamReader readEnvelope() { + if (envelopeReader == null) { + List hReaders = new java.util.ArrayList(); + ElemInfo envElem = new ElemInfo(envelopeTag, null); + ElemInfo hdrElem = (headerTag != null) ? new ElemInfo(headerTag, envElem) : null; + ElemInfo bdyElem = new ElemInfo(bodyTag, envElem); + for (Header h : getHeaders().asList()) { + try { + hReaders.add(h.readHeader()); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + } + XMLStreamReader soapHeader = (hdrElem != null) ? new XMLReaderComposite(hdrElem, hReaders.toArray(new XMLStreamReader[hReaders.size()])) : null; + XMLStreamReader[] payload = {readPayload()}; + XMLStreamReader soapBody = new XMLReaderComposite(bdyElem, payload); + XMLStreamReader[] soapContent = (soapHeader != null) ? new XMLStreamReader[]{soapHeader, soapBody} : new XMLStreamReader[]{soapBody}; + return new XMLReaderComposite(envElem, soapContent); + } + return envelopeReader; + } + /** * Creates a {@link StreamMessage} from a {@link XMLStreamReader} * that points at the start element of the payload, and headers. @@ -147,6 +164,10 @@ */ public StreamMessage(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { super(soapVersion); + init(headers, attachmentSet, reader, soapVersion); + } + + private void init(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { this.headers = headers; this.attachmentSet = attachmentSet; this.reader = reader; @@ -175,9 +196,9 @@ // use the default infoset representation for headers int base = soapVersion.ordinal()*3; - this.envelopeTag = DEFAULT_TAGS[base]; - this.headerTag = DEFAULT_TAGS[base+1]; - this.bodyTag = DEFAULT_TAGS[base+2]; + this.envelopeTag = DEFAULT_TAGS.get(base); + this.headerTag = DEFAULT_TAGS.get(base+1); + this.bodyTag = DEFAULT_TAGS.get(base+2); } /** @@ -197,7 +218,12 @@ } 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) { - this(headers,attachmentSet,reader,soapVersion); + super(soapVersion); + init(envelopeTag, headerTag, attachmentSet, headers, bodyPrologue, bodyTag, bodyEpilogue, reader, soapVersion); + } + + 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) { + init(headers,attachmentSet,reader,soapVersion); if(envelopeTag == null ) { throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null"); } @@ -212,10 +238,12 @@ } public boolean hasHeaders() { + if ( envelopeReader != null ) readEnvelope(this); return headers!=null && headers.hasHeaders(); } public MessageHeaders getHeaders() { + if ( envelopeReader != null ) readEnvelope(this); if (headers == null) { headers = new HeaderList(getSOAPVersion()); } @@ -223,14 +251,17 @@ } public String getPayloadLocalPart() { + if ( envelopeReader != null ) readEnvelope(this); return payloadLocalName; } public String getPayloadNamespaceURI() { + if ( envelopeReader != null ) readEnvelope(this); return payloadNamespaceURI; } public boolean hasPayload() { + if ( envelopeReader != null ) readEnvelope(this); return payloadLocalName!=null; } @@ -329,6 +360,7 @@ } public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException { + if ( envelopeReader != null ) readEnvelope(this); assert unconsumed(); if(payloadLocalName==null) { @@ -379,6 +411,7 @@ } public void writeTo(XMLStreamWriter sw) throws XMLStreamException{ + if ( envelopeReader != null ) readEnvelope(this); writeEnvelope(sw); } @@ -387,6 +420,7 @@ * @param writer */ private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException { + if ( envelopeReader != null ) readEnvelope(this); writer.writeStartDocument(); envelopeTag.writeStart(writer); @@ -411,6 +445,7 @@ } public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException { + if ( envelopeReader != null ) readEnvelope(this); assert unconsumed(); try { @@ -465,8 +500,10 @@ } } - // TODO: this method should be probably rewritten to respect spaces between eelements; is it used at all? + // TODO: this method should be probably rewritten to respect spaces between elements; is it used at all? + @Override public Message copy() { + if ( envelopeReader != null ) readEnvelope(this); try { assert unconsumed(); consumedAt = null; // but we don't want to mark it as consumed @@ -528,6 +565,7 @@ } public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException { + if ( envelopeReader != null ) readEnvelope(this); contentHandler.setDocumentLocator(NULL_LOCATOR); contentHandler.startDocument(); envelopeTag.writeStart(contentHandler); @@ -570,23 +608,159 @@ return true; } - private static void create(SOAPVersion v) { - int base = v.ordinal()*3; - DEFAULT_TAGS[base ] = new TagInfoset(v.nsUri,"Envelope","S",EMPTY_ATTS,"S",v.nsUri); - DEFAULT_TAGS[base+1] = new TagInfoset(v.nsUri,"Header","S",EMPTY_ATTS); - DEFAULT_TAGS[base+2] = new TagInfoset(v.nsUri,"Body","S",EMPTY_ATTS); - } - public String getBodyPrologue() { + if ( envelopeReader != null ) readEnvelope(this); return bodyPrologue; } public String getBodyEpilogue() { + if ( envelopeReader != null ) readEnvelope(this); return bodyEpilogue; } public XMLStreamReader getReader() { + if ( envelopeReader != null ) readEnvelope(this); assert unconsumed(); return reader; } + + + private static final String SOAP_ENVELOPE = "Envelope"; + private static final String SOAP_HEADER = "Header"; + private static final String SOAP_BODY = "Body"; + + protected interface StreamHeaderDecoder { + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark); + } + + static final StreamHeaderDecoder SOAP12StreamHeaderDecoder = new StreamHeaderDecoder() { + @Override + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) { + return new StreamHeader12(reader, mark); + } + }; + + static final StreamHeaderDecoder SOAP11StreamHeaderDecoder = new StreamHeaderDecoder() { + @Override + public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) { + return new StreamHeader11(reader, mark); + } + }; + + static private void readEnvelope(StreamMessage message) { + if ( message.envelopeReader == null ) return; + XMLStreamReader reader = message.envelopeReader; + message.envelopeReader = null; + SOAPVersion soapVersion = message.soapVersion; + // Move to soap:Envelope and verify + if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT) + XMLStreamReaderUtil.nextElementContent(reader); + XMLStreamReaderUtil.verifyReaderState(reader,XMLStreamConstants.START_ELEMENT); + if (SOAP_ENVELOPE.equals(reader.getLocalName()) && !soapVersion.nsUri.equals(reader.getNamespaceURI())) { + throw new VersionMismatchException(soapVersion, soapVersion.nsUri, reader.getNamespaceURI()); + } + XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_ENVELOPE); + + TagInfoset envelopeTag = new TagInfoset(reader); + + // Collect namespaces on soap:Envelope + Map namespaces = new HashMap(); + for(int i=0; i< reader.getNamespaceCount();i++){ + namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); + } + + // Move to next element + XMLStreamReaderUtil.nextElementContent(reader); + XMLStreamReaderUtil.verifyReaderState(reader, + javax.xml.stream.XMLStreamConstants.START_ELEMENT); + + HeaderList headers = null; + TagInfoset headerTag = null; + + if (reader.getLocalName().equals(SOAP_HEADER) + && reader.getNamespaceURI().equals(soapVersion.nsUri)) { + headerTag = new TagInfoset(reader); + + // Collect namespaces on soap:Header + for(int i=0; i< reader.getNamespaceCount();i++){ + namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); + } + // skip + XMLStreamReaderUtil.nextElementContent(reader); + + // If SOAP header blocks are present (i.e. not ) + if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) { + headers = new HeaderList(soapVersion); + + try { + // Cache SOAP header blocks + StreamHeaderDecoder headerDecoder = SOAPVersion.SOAP_11.equals(soapVersion) ? SOAP11StreamHeaderDecoder : SOAP12StreamHeaderDecoder; + cacheHeaders(reader, namespaces, headers, headerDecoder); + } catch (XMLStreamException e) { + // TODO need to throw more meaningful exception + throw new WebServiceException(e); + } + } + + // Move to soap:Body + XMLStreamReaderUtil.nextElementContent(reader); + } + + // Verify that is present + XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_BODY); + TagInfoset bodyTag = new TagInfoset(reader); + + String bodyPrologue = XMLStreamReaderUtil.nextWhiteSpaceContent(reader); + message.init(envelopeTag,headerTag,message.attachmentSet,headers,bodyPrologue,bodyTag,null,reader,soapVersion); + // when there's no payload, + // it's tempting to use EmptyMessageImpl, but it doesn't preserve the infoset + // of ,

, and , so we need to stick to StreamMessage. + } + + + private static XMLStreamBuffer cacheHeaders(XMLStreamReader reader, + Map namespaces, HeaderList headers, + StreamHeaderDecoder headerDecoder) throws XMLStreamException { + MutableXMLStreamBuffer buffer = createXMLStreamBuffer(); + StreamReaderBufferCreator creator = new StreamReaderBufferCreator(); + creator.setXMLStreamBuffer(buffer); + + // Reader is positioned at the first header block + while(reader.getEventType() == javax.xml.stream.XMLStreamConstants.START_ELEMENT) { + Map headerBlockNamespaces = namespaces; + + // Collect namespaces on SOAP header block + if (reader.getNamespaceCount() > 0) { + headerBlockNamespaces = new HashMap(namespaces); + for (int i = 0; i < reader.getNamespaceCount(); i++) { + headerBlockNamespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i)); + } + } + + // Mark + XMLStreamBuffer mark = new XMLStreamBufferMark(headerBlockNamespaces, creator); + // Create Header + headers.add(headerDecoder.decodeHeader(reader, mark)); + + + // Cache the header block + // After caching Reader will be positioned at next header block or + // the end of the + creator.createElementFragment(reader, false); + if (reader.getEventType() != XMLStreamConstants.START_ELEMENT && + reader.getEventType() != XMLStreamConstants.END_ELEMENT) { + XMLStreamReaderUtil.nextElementContent(reader); + } + } + + return buffer; + } + + private static MutableXMLStreamBuffer createXMLStreamBuffer() { + // TODO: Decode should own one MutableXMLStreamBuffer for reuse + // since it is more efficient. ISSUE: possible issue with + // lifetime of information in the buffer if accessed beyond + // the pipe line. + return new MutableXMLStreamBuffer(); + } }