src/share/jaxws_classes/com/sun/xml/internal/ws/message/stream/StreamMessage.java

Print this page

        

*** 28,78 **** import com.sun.istack.internal.NotNull; import com.sun.istack.internal.Nullable; 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.stax.StreamReaderBufferCreator; import com.sun.xml.internal.ws.api.SOAPVersion; import com.sun.xml.internal.ws.api.message.AttachmentSet; import com.sun.xml.internal.ws.api.message.Header; 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.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.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.XMLStreamReaderToXMLStreamWriter; import org.xml.sax.ContentHandler; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.NamespaceSupport; 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; import javax.xml.transform.Source; import javax.xml.ws.WebServiceException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * {@link Message} implementation backed by {@link XMLStreamReader}. * * TODO: we need another message class that keeps {@link XMLStreamReader} that points * at the start of the envelope element. */ ! public class StreamMessage extends AbstractMessageImpl { /** * The reader will be positioned at * the first child of the SOAP body */ private @NotNull XMLStreamReader reader; --- 28,88 ---- import com.sun.istack.internal.NotNull; import com.sun.istack.internal.Nullable; 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; import com.sun.xml.internal.ws.api.message.Header; 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; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.NamespaceSupport; 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; import javax.xml.transform.Source; 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}. * * TODO: we need another message class that keeps {@link XMLStreamReader} that points * at the start of the envelope element. */ ! public class StreamMessage extends AbstractMessageImpl implements StreamingSOAP { /** * The reader will be positioned at * the first child of the SOAP body */ private @NotNull XMLStreamReader reader;
*** 91,138 **** /** * instantiated after writing message to XMLStreamWriter */ private String bodyEpilogue = null; ! private final String payloadLocalName; ! private final String payloadNamespaceURI; ! ! /** ! * infoset about the SOAP envelope, header, and body. ! * ! * <p> ! * 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; /** * 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); ! } public StreamMessage(SOAPVersion v) { super(v); payloadLocalName = null; payloadNamespaceURI = null; } /** * Creates a {@link StreamMessage} from a {@link XMLStreamReader} * that points at the start element of the payload, and headers. * * <p> --- 101,155 ---- /** * instantiated after writing message to XMLStreamWriter */ private String bodyEpilogue = null; ! private String payloadLocalName; ! private String payloadNamespaceURI; /** * Used only for debugging. This records where the message was consumed. */ private Throwable consumedAt; ! 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<XMLStreamReader> hReaders = new java.util.ArrayList<XMLStreamReader>(); + 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. * * <p>
*** 145,154 **** --- 162,175 ---- * points at the start element/document of the payload (or the end element of the &lt;s:Body> * if there's no payload) */ 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; if(reader.getEventType()== START_DOCUMENT)
*** 173,185 **** this.payloadNamespaceURI = reader.getNamespaceURI(); } // 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]; } /** * Creates a {@link StreamMessage} from a {@link XMLStreamReader} * and the complete infoset of the SOAP envelope. --- 194,206 ---- this.payloadNamespaceURI = reader.getNamespaceURI(); } // use the default infoset representation for headers int base = soapVersion.ordinal()*3; ! this.envelopeTag = DEFAULT_TAGS.get(base); ! this.headerTag = DEFAULT_TAGS.get(base+1); ! this.bodyTag = DEFAULT_TAGS.get(base+2); } /** * Creates a {@link StreamMessage} from a {@link XMLStreamReader} * and the complete infoset of the SOAP envelope.
*** 195,205 **** public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { this(envelopeTag, headerTag, attachmentSet, headers, null, bodyTag, null, reader, soapVersion); } 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); if(envelopeTag == null ) { throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null"); } if(bodyTag == null ) { throw new IllegalArgumentException("BodyTag TagInfoset cannot be null"); --- 216,231 ---- public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) { this(envelopeTag, headerTag, attachmentSet, headers, null, bodyTag, null, reader, soapVersion); } 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) { ! 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"); } if(bodyTag == null ) { throw new IllegalArgumentException("BodyTag TagInfoset cannot be null");
*** 210,238 **** --- 236,269 ---- this.bodyPrologue = bodyPrologue; this.bodyEpilogue = bodyEpilogue; } 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()); } return headers; } 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; } public Source readPayloadAsSource() { if(hasPayload()) {
*** 327,336 **** --- 358,368 ---- assert unconsumed(); return this.reader; } public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException { + if ( envelopeReader != null ) readEnvelope(this); assert unconsumed(); if(payloadLocalName==null) { return; // no body }
*** 377,394 **** --- 409,428 ---- private boolean isBodyElement(String name, String nsUri) { return name.equals("Body") && nsUri.equals(soapVersion.nsUri); } public void writeTo(XMLStreamWriter sw) throws XMLStreamException{ + if ( envelopeReader != null ) readEnvelope(this); writeEnvelope(sw); } /** * This method should be called when the StreamMessage is created with a payload * @param writer */ private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException { + if ( envelopeReader != null ) readEnvelope(this); writer.writeStartDocument(); envelopeTag.writeStart(writer); //write headers MessageHeaders hl = getHeaders();
*** 409,418 **** --- 443,453 ---- writer.writeEndElement(); writer.writeEndDocument(); } public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException { + if ( envelopeReader != null ) readEnvelope(this); assert unconsumed(); try { if(payloadLocalName==null) return; // no body
*** 463,474 **** e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e); errorHandler.error(x); } } ! // TODO: this method should be probably rewritten to respect spaces between eelements; is it used at all? public Message copy() { try { assert unconsumed(); consumedAt = null; // but we don't want to mark it as consumed MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer(); StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb); --- 498,511 ---- e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e); errorHandler.error(x); } } ! // 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 MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer(); StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb);
*** 526,535 **** --- 563,573 ---- xsr.nextTag(); assert xsr.getEventType()==START_ELEMENT || xsr.getEventType()==END_ELEMENT; } public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException { + if ( envelopeReader != null ) readEnvelope(this); contentHandler.setDocumentLocator(NULL_LOCATOR); contentHandler.startDocument(); envelopeTag.writeStart(contentHandler); if (hasHeaders() && headerTag == null) headerTag = new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS); if (headerTag != null) {
*** 568,592 **** } consumedAt = new Exception().fillInStackTrace(); 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() { return bodyPrologue; } public String getBodyEpilogue() { return bodyEpilogue; } public XMLStreamReader getReader() { assert unconsumed(); return reader; } } --- 606,766 ---- } consumedAt = new Exception().fillInStackTrace(); return true; } 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<String,String> namespaces = new HashMap<String,String>(); + 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 <soap:Header> + XMLStreamReaderUtil.nextElementContent(reader); + + // If SOAP header blocks are present (i.e. not <soap:Header/>) + 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 <soap:Body> 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 <envelope>,<header>, and <body>, so we need to stick to StreamMessage. + } + + + private static XMLStreamBuffer cacheHeaders(XMLStreamReader reader, + Map<String, String> 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<String,String> headerBlockNamespaces = namespaces; + + // Collect namespaces on SOAP header block + if (reader.getNamespaceCount() > 0) { + headerBlockNamespaces = new HashMap<String,String>(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 </soap:header> + 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(); + } }