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

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 397
b99d7e355d4b
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     1 /*
     2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.ws.message.stream;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.istack.internal.Nullable;
    30 import com.sun.istack.internal.XMLStreamReaderToContentHandler;
    31 import com.sun.xml.internal.bind.api.Bridge;
    32 import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
    33 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
    34 import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark;
    35 import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator;
    36 import com.sun.xml.internal.ws.api.SOAPVersion;
    37 import com.sun.xml.internal.ws.api.message.AttachmentSet;
    38 import com.sun.xml.internal.ws.api.message.Header;
    39 import com.sun.xml.internal.ws.api.message.HeaderList;
    40 import com.sun.xml.internal.ws.api.message.Message;
    41 import com.sun.xml.internal.ws.api.message.MessageHeaders;
    42 import com.sun.xml.internal.ws.api.message.StreamingSOAP;
    43 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
    44 import com.sun.xml.internal.ws.encoding.TagInfoset;
    45 import com.sun.xml.internal.ws.message.AbstractMessageImpl;
    46 import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl;
    47 import com.sun.xml.internal.ws.protocol.soap.VersionMismatchException;
    48 import com.sun.xml.internal.ws.spi.db.XMLBridge;
    49 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
    50 import com.sun.xml.internal.ws.util.xml.DummyLocation;
    51 import com.sun.xml.internal.ws.util.xml.StAXSource;
    52 import com.sun.xml.internal.ws.util.xml.XMLReaderComposite;
    53 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter;
    54 import com.sun.xml.internal.ws.util.xml.XMLReaderComposite.ElemInfo;
    56 import org.xml.sax.ContentHandler;
    57 import org.xml.sax.ErrorHandler;
    58 import org.xml.sax.SAXException;
    59 import org.xml.sax.SAXParseException;
    60 import org.xml.sax.helpers.NamespaceSupport;
    62 import javax.xml.bind.JAXBException;
    63 import javax.xml.bind.Unmarshaller;
    64 import javax.xml.stream.*;
    66 import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
    67 import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
    68 import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
    69 import javax.xml.transform.Source;
    70 import javax.xml.ws.WebServiceException;
    71 import java.util.ArrayList;
    72 import java.util.Enumeration;
    73 import java.util.HashMap;
    74 import java.util.List;
    75 import java.util.Map;
    77 /**
    78  * {@link Message} implementation backed by {@link XMLStreamReader}.
    79  *
    80  * TODO: we need another message class that keeps {@link XMLStreamReader} that points
    81  * at the start of the envelope element.
    82  */
    83 public class StreamMessage extends AbstractMessageImpl implements StreamingSOAP {
    84     /**
    85      * The reader will be positioned at
    86      * the first child of the SOAP body
    87      */
    88     private @NotNull XMLStreamReader reader;
    90     // lazily created
    91     private @Nullable MessageHeaders headers;
    93     /**
    94      * Because the StreamMessage leaves out the white spaces around payload
    95      * when being instantiated the space characters between soap:Body opening and
    96      * payload is stored in this field to be reused later (necessary for message security);
    97      * Instantiated after StreamMessage creation
    98      */
    99     private String bodyPrologue = null;
   101     /**
   102      * instantiated after writing message to XMLStreamWriter
   103      */
   104     private String bodyEpilogue = null;
   106     private String payloadLocalName;
   108     private String payloadNamespaceURI;
   110     /**
   111      * Used only for debugging. This records where the message was consumed.
   112      */
   113     private Throwable consumedAt;
   115     private XMLStreamReader envelopeReader;
   117     public StreamMessage(SOAPVersion v) {
   118         super(v);
   119         payloadLocalName = null;
   120         payloadNamespaceURI = null;
   121     }
   123     public StreamMessage(SOAPVersion v, @NotNull XMLStreamReader envelope, @NotNull AttachmentSet attachments) {
   124         super(v);
   125         envelopeReader = envelope;
   126         attachmentSet = attachments;
   127     }
   129     public XMLStreamReader readEnvelope() {
   130         if (envelopeReader == null) {
   131             List<XMLStreamReader> hReaders = new java.util.ArrayList<XMLStreamReader>();
   132             ElemInfo envElem =  new ElemInfo(envelopeTag, null);
   133             ElemInfo hdrElem =  (headerTag != null) ? new ElemInfo(headerTag, envElem) : null;
   134             ElemInfo bdyElem =  new ElemInfo(bodyTag,   envElem);
   135             for (Header h : getHeaders().asList()) {
   136                 try {
   137                     hReaders.add(h.readHeader());
   138                 } catch (XMLStreamException e) {
   139                     throw new RuntimeException(e);
   140                 }
   141             }
   142             XMLStreamReader soapHeader = (hdrElem != null) ? new XMLReaderComposite(hdrElem, hReaders.toArray(new XMLStreamReader[hReaders.size()])) : null;
   143             XMLStreamReader[] payload = {readPayload()};
   144             XMLStreamReader soapBody = new XMLReaderComposite(bdyElem, payload);
   145             XMLStreamReader[] soapContent = (soapHeader != null) ? new XMLStreamReader[]{soapHeader, soapBody} : new XMLStreamReader[]{soapBody};
   146             return new XMLReaderComposite(envElem, soapContent);
   147         }
   148         return envelopeReader;
   149     }
   151     /**
   152      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
   153      * that points at the start element of the payload, and headers.
   154      *
   155      * <p>
   156      * This method creates a {@link Message} from a payload.
   157      *
   158      * @param headers
   159      *      if null, it means no headers. if non-null,
   160      *      it will be owned by this message.
   161      * @param reader
   162      *      points at the start element/document of the payload (or the end element of the &lt;s:Body>
   163      *      if there's no payload)
   164      */
   165     public StreamMessage(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   166         super(soapVersion);
   167         init(headers, attachmentSet, reader, soapVersion);
   168     }
   170     private void init(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   171         this.headers = headers;
   172         this.attachmentSet = attachmentSet;
   173         this.reader = reader;
   175         if(reader.getEventType()== START_DOCUMENT)
   176             XMLStreamReaderUtil.nextElementContent(reader);
   178         //if the reader is pointing to the end element </soapenv:Body> then its empty message
   179         // or no payload
   180         if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   181             String body = reader.getLocalName();
   182             String nsUri = reader.getNamespaceURI();
   183             assert body != null;
   184             assert nsUri != null;
   185             //if its not soapenv:Body then throw exception, we received malformed stream
   186             if(body.equals("Body") && nsUri.equals(soapVersion.nsUri)){
   187                 this.payloadLocalName = null;
   188                 this.payloadNamespaceURI = null;
   189             }else{ //TODO: i18n and also we should be throwing better message that this
   190                 throw new WebServiceException("Malformed stream: {"+nsUri+"}"+body);
   191             }
   192         }else{
   193             this.payloadLocalName = reader.getLocalName();
   194             this.payloadNamespaceURI = reader.getNamespaceURI();
   195         }
   197         // use the default infoset representation for headers
   198         int base = soapVersion.ordinal()*3;
   199         this.envelopeTag = DEFAULT_TAGS.get(base);
   200         this.headerTag = DEFAULT_TAGS.get(base+1);
   201         this.bodyTag = DEFAULT_TAGS.get(base+2);
   202     }
   204     /**
   205      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
   206      * and the complete infoset of the SOAP envelope.
   207      *
   208      * <p>
   209      * See {@link #StreamMessage(MessageHeaders, AttachmentSet, XMLStreamReader, SOAPVersion)} for
   210      * the description of the basic parameters.
   211      *
   212      * @param headerTag
   213      *      Null if the message didn't have a header tag.
   214      *
   215      */
   216     public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
   217         this(envelopeTag, headerTag, attachmentSet, headers, null, bodyTag, null, reader, soapVersion);
   218     }
   220     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) {
   221         super(soapVersion);
   222         init(envelopeTag, headerTag, attachmentSet, headers, bodyPrologue, bodyTag, bodyEpilogue, reader, soapVersion);
   223     }
   225     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) {
   226         init(headers,attachmentSet,reader,soapVersion);
   227         if(envelopeTag == null ) {
   228             throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null");
   229         }
   230         if(bodyTag == null ) {
   231             throw new IllegalArgumentException("BodyTag TagInfoset cannot be null");
   232         }
   233         this.envelopeTag = envelopeTag;
   234         this.headerTag = headerTag;
   235         this.bodyTag = bodyTag;
   236         this.bodyPrologue = bodyPrologue;
   237         this.bodyEpilogue = bodyEpilogue;
   238     }
   240     public boolean hasHeaders() {
   241         if ( envelopeReader != null ) readEnvelope(this);
   242         return headers!=null && headers.hasHeaders();
   243     }
   245     public MessageHeaders getHeaders() {
   246         if ( envelopeReader != null ) readEnvelope(this);
   247         if (headers == null) {
   248             headers = new HeaderList(getSOAPVersion());
   249         }
   250         return headers;
   251     }
   253     public String getPayloadLocalPart() {
   254         if ( envelopeReader != null ) readEnvelope(this);
   255         return payloadLocalName;
   256     }
   258     public String getPayloadNamespaceURI() {
   259         if ( envelopeReader != null ) readEnvelope(this);
   260         return payloadNamespaceURI;
   261     }
   263     public boolean hasPayload() {
   264         if ( envelopeReader != null ) readEnvelope(this);
   265         return payloadLocalName!=null;
   266     }
   268     public Source readPayloadAsSource() {
   269         if(hasPayload()) {
   270             assert unconsumed();
   271             return new StAXSource(reader, true, getInscopeNamespaces());
   272         } else
   273             return null;
   274     }
   276     /**
   277      * There is no way to enumerate inscope namespaces for XMLStreamReader. That means
   278      * namespaces declared in envelope, and body tags need to be computed using their
   279      * {@link TagInfoset}s.
   280      *
   281      * @return array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
   282      */
   283     private String[] getInscopeNamespaces() {
   284         NamespaceSupport nss = new NamespaceSupport();
   286         nss.pushContext();
   287         for(int i=0; i < envelopeTag.ns.length; i+=2) {
   288             nss.declarePrefix(envelopeTag.ns[i], envelopeTag.ns[i+1]);
   289         }
   291         nss.pushContext();
   292         for(int i=0; i < bodyTag.ns.length; i+=2) {
   293             nss.declarePrefix(bodyTag.ns[i], bodyTag.ns[i+1]);
   294         }
   296         List<String> inscope = new ArrayList<String>();
   297         for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) {
   298             String prefix = (String)en.nextElement();
   299             inscope.add(prefix);
   300             inscope.add(nss.getURI(prefix));
   301         }
   302         return inscope.toArray(new String[inscope.size()]);
   303     }
   305     public Object readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
   306         if(!hasPayload())
   307             return null;
   308         assert unconsumed();
   309         // TODO: How can the unmarshaller process this as a fragment?
   310         if(hasAttachments())
   311             unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshallerImpl(getAttachments()));
   312         try {
   313             return unmarshaller.unmarshal(reader);
   314         } finally{
   315             unmarshaller.setAttachmentUnmarshaller(null);
   316             XMLStreamReaderUtil.readRest(reader);
   317             XMLStreamReaderUtil.close(reader);
   318             XMLStreamReaderFactory.recycle(reader);
   319         }
   320     }
   321     /** @deprecated */
   322     public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
   323         if(!hasPayload())
   324             return null;
   325         assert unconsumed();
   326         T r = bridge.unmarshal(reader,
   327             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
   328         XMLStreamReaderUtil.readRest(reader);
   329         XMLStreamReaderUtil.close(reader);
   330         XMLStreamReaderFactory.recycle(reader);
   331         return r;
   332     }
   334     public <T> T readPayloadAsJAXB(XMLBridge<T> bridge) throws JAXBException {
   335         if(!hasPayload())
   336             return null;
   337         assert unconsumed();
   338         T r = bridge.unmarshal(reader,
   339             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
   340         XMLStreamReaderUtil.readRest(reader);
   341         XMLStreamReaderUtil.close(reader);
   342         XMLStreamReaderFactory.recycle(reader);
   343         return r;
   344     }
   346     @Override
   347     public void consume() {
   348         assert unconsumed();
   349         XMLStreamReaderUtil.readRest(reader);
   350         XMLStreamReaderUtil.close(reader);
   351         XMLStreamReaderFactory.recycle(reader);
   352     }
   354     public XMLStreamReader readPayload() {
   355         if(!hasPayload())
   356             return null;
   357         // TODO: What about access at and beyond </soap:Body>
   358         assert unconsumed();
   359         return this.reader;
   360     }
   362     public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException {
   363         if ( envelopeReader != null ) readEnvelope(this);
   364         assert unconsumed();
   366         if(payloadLocalName==null) {
   367             return; // no body
   368         }
   370         if (bodyPrologue != null) {
   371             writer.writeCharacters(bodyPrologue);
   372         }
   374         XMLStreamReaderToXMLStreamWriter conv = new XMLStreamReaderToXMLStreamWriter();
   376         while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   377             String name = reader.getLocalName();
   378             String nsUri = reader.getNamespaceURI();
   380             // After previous conv.bridge() call the cursor will be at END_ELEMENT.
   381             // Check if its not soapenv:Body then move to next ELEMENT
   382             if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   384                 if (!isBodyElement(name, nsUri)){
   385                     // closing payload element: store epilogue for further signing, if applicable
   386                     // however if there more than one payloads exist - the last one is stored
   387                     String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
   388                     if (whiteSpaces != null) {
   389                         this.bodyEpilogue = whiteSpaces;
   390                         // write it to the message too
   391                         writer.writeCharacters(whiteSpaces);
   392                     }
   393                 } else {
   394                     // body closed > exit
   395                     break;
   396                 }
   398             } else {
   399                 // payload opening element: copy payload to writer
   400                 conv.bridge(reader,writer);
   401             }
   402         }
   404         XMLStreamReaderUtil.readRest(reader);
   405         XMLStreamReaderUtil.close(reader);
   406         XMLStreamReaderFactory.recycle(reader);
   407     }
   409     private boolean isBodyElement(String name, String nsUri) {
   410         return name.equals("Body") && nsUri.equals(soapVersion.nsUri);
   411     }
   413     public void writeTo(XMLStreamWriter sw) throws XMLStreamException{
   414         if ( envelopeReader != null ) readEnvelope(this);
   415         writeEnvelope(sw);
   416     }
   418     /**
   419      * This method should be called when the StreamMessage is created with a payload
   420      * @param writer
   421      */
   422     private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException {
   423         if ( envelopeReader != null ) readEnvelope(this);
   424         writer.writeStartDocument();
   425         envelopeTag.writeStart(writer);
   427         //write headers
   428         MessageHeaders hl = getHeaders();
   429         if (hl.hasHeaders() && headerTag == null) headerTag = new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
   430         if (headerTag != null) {
   431             headerTag.writeStart(writer);
   432             if (hl.hasHeaders()){
   433                 for(Header h : hl.asList()){
   434                     h.writeTo(writer);
   435                 }
   436             }
   437             writer.writeEndElement();
   438         }
   439         bodyTag.writeStart(writer);
   440         if(hasPayload())
   441             writePayloadTo(writer);
   442         writer.writeEndElement();
   443         writer.writeEndElement();
   444         writer.writeEndDocument();
   445     }
   447     public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
   448         if ( envelopeReader != null ) readEnvelope(this);
   449         assert unconsumed();
   451         try {
   452             if(payloadLocalName==null)
   453                 return; // no body
   455             if (bodyPrologue != null) {
   456                 char[] chars = bodyPrologue.toCharArray();
   457                 contentHandler.characters(chars, 0, chars.length);
   458             }
   460             XMLStreamReaderToContentHandler conv = new XMLStreamReaderToContentHandler(reader,contentHandler,true,fragment,getInscopeNamespaces());
   462             while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   463                 String name = reader.getLocalName();
   464                 String nsUri = reader.getNamespaceURI();
   466                 // After previous conv.bridge() call the cursor will be at END_ELEMENT.
   467                 // Check if its not soapenv:Body then move to next ELEMENT
   468                 if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
   470                     if (!isBodyElement(name, nsUri)){
   471                         // closing payload element: store epilogue for further signing, if applicable
   472                         // however if there more than one payloads exist - the last one is stored
   473                         String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
   474                         if (whiteSpaces != null) {
   475                             this.bodyEpilogue = whiteSpaces;
   476                             // write it to the message too
   477                             char[] chars = whiteSpaces.toCharArray();
   478                             contentHandler.characters(chars, 0, chars.length);
   479                         }
   480                     } else {
   481                         // body closed > exit
   482                         break;
   483                     }
   485                 } else {
   486                     // payload opening element: copy payload to writer
   487                     conv.bridge();
   488                 }
   489             }
   490             XMLStreamReaderUtil.readRest(reader);
   491             XMLStreamReaderUtil.close(reader);
   492             XMLStreamReaderFactory.recycle(reader);
   493         } catch (XMLStreamException e) {
   494             Location loc = e.getLocation();
   495             if(loc==null)   loc = DummyLocation.INSTANCE;
   497             SAXParseException x = new SAXParseException(
   498                 e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e);
   499             errorHandler.error(x);
   500         }
   501     }
   503     // TODO: this method should be probably rewritten to respect spaces between elements; is it used at all?
   504     @Override
   505     public Message copy() {
   506         if ( envelopeReader != null ) readEnvelope(this);
   507         try {
   508             assert unconsumed();
   509             consumedAt = null; // but we don't want to mark it as consumed
   510             MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
   511             StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb);
   513             // preserving inscope namespaces from envelope, and body. Other option
   514             // would be to create a filtering XMLStreamReader from reader+envelopeTag+bodyTag
   515             c.storeElement(envelopeTag.nsUri, envelopeTag.localName, envelopeTag.prefix, envelopeTag.ns);
   516             c.storeElement(bodyTag.nsUri, bodyTag.localName, bodyTag.prefix, bodyTag.ns);
   518             if (hasPayload()) {
   519                 // Loop all the way for multi payload case
   520                 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
   521                     String name = reader.getLocalName();
   522                     String nsUri = reader.getNamespaceURI();
   523                     if(isBodyElement(name, nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
   524                         break;
   525                     c.create(reader);
   527                     // Skip whitespaces in between payload and </Body> or between elements
   528                     // those won't be in the message itself, but we store them in field bodyEpilogue
   529                     if (reader.isWhiteSpace()) {
   530                         bodyEpilogue = XMLStreamReaderUtil.currentWhiteSpaceContent(reader);
   531                     } else {
   532                         // clear it in case the existing was not the last one
   533                         // (we are interested only in the last one?)
   534                         bodyEpilogue = null;
   535                     }
   536                 }
   537             }
   538             c.storeEndElement();        // create structure element for </Body>
   539             c.storeEndElement();        // create structure element for </Envelope>
   540             c.storeEndElement();        // create structure element for END_DOCUMENT
   542             XMLStreamReaderUtil.readRest(reader);
   543             XMLStreamReaderUtil.close(reader);
   544             XMLStreamReaderFactory.recycle(reader);
   546             reader = xsb.readAsXMLStreamReader();
   547             XMLStreamReader clone = xsb.readAsXMLStreamReader();
   549             // advance to the start tag of the <Body> first child element
   550             proceedToRootElement(reader);
   551             proceedToRootElement(clone);
   553             return new StreamMessage(envelopeTag, headerTag, attachmentSet, HeaderList.copy(headers), bodyPrologue, bodyTag, bodyEpilogue, clone, soapVersion);
   554         } catch (XMLStreamException e) {
   555             throw new WebServiceException("Failed to copy a message",e);
   556         }
   557     }
   559     private void proceedToRootElement(XMLStreamReader xsr) throws XMLStreamException {
   560         assert xsr.getEventType()==START_DOCUMENT;
   561         xsr.nextTag();
   562         xsr.nextTag();
   563         xsr.nextTag();
   564         assert xsr.getEventType()==START_ELEMENT || xsr.getEventType()==END_ELEMENT;
   565     }
   567     public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException {
   568         if ( envelopeReader != null ) readEnvelope(this);
   569         contentHandler.setDocumentLocator(NULL_LOCATOR);
   570         contentHandler.startDocument();
   571         envelopeTag.writeStart(contentHandler);
   572         if (hasHeaders() && headerTag == null) headerTag = new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
   573         if (headerTag != null) {
   574             headerTag.writeStart(contentHandler);
   575             if (hasHeaders()) {
   576                 MessageHeaders headers = getHeaders();
   577                 for (Header h : headers.asList()) {
   578                     // shouldn't JDK be smart enough to use array-style indexing for this foreach!?
   579                     h.writeTo(contentHandler,errorHandler);
   580                 }
   581             }
   582             headerTag.writeEnd(contentHandler);
   583         }
   584         bodyTag.writeStart(contentHandler);
   585         writePayloadTo(contentHandler,errorHandler, true);
   586         bodyTag.writeEnd(contentHandler);
   587         envelopeTag.writeEnd(contentHandler);
   588         contentHandler.endDocument();
   589     }
   591     /**
   592      * Used for an assertion. Returns true when the message is unconsumed,
   593      * or otherwise throw an exception.
   594      *
   595      * <p>
   596      * Calling this method also marks the stream as 'consumed'
   597      */
   598     private boolean unconsumed() {
   599         if(payloadLocalName==null)
   600             return true;    // no payload. can be consumed multiple times.
   602         if(reader.getEventType()!=XMLStreamReader.START_ELEMENT) {
   603             AssertionError error = new AssertionError("StreamMessage has been already consumed. See the nested exception for where it's consumed");
   604             error.initCause(consumedAt);
   605             throw error;
   606         }
   607         consumedAt = new Exception().fillInStackTrace();
   608         return true;
   609     }
   611     public String getBodyPrologue() {
   612         if ( envelopeReader != null ) readEnvelope(this);
   613         return bodyPrologue;
   614     }
   616     public String getBodyEpilogue() {
   617         if ( envelopeReader != null ) readEnvelope(this);
   618         return bodyEpilogue;
   619     }
   621     public XMLStreamReader getReader() {
   622         if ( envelopeReader != null ) readEnvelope(this);
   623         assert unconsumed();
   624         return reader;
   625     }
   628     private static final String SOAP_ENVELOPE = "Envelope";
   629     private static final String SOAP_HEADER = "Header";
   630     private static final String SOAP_BODY = "Body";
   632     protected interface StreamHeaderDecoder {
   633         public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark);
   634     }
   636     static final StreamHeaderDecoder SOAP12StreamHeaderDecoder = new StreamHeaderDecoder() {
   637         @Override
   638         public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) {
   639             return new StreamHeader12(reader, mark);
   640         }
   641     };
   643     static final StreamHeaderDecoder SOAP11StreamHeaderDecoder = new StreamHeaderDecoder() {
   644         @Override
   645         public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark) {
   646             return new StreamHeader11(reader, mark);
   647         }
   648     };
   650     static private void readEnvelope(StreamMessage message) {
   651         if ( message.envelopeReader == null ) return;
   652         XMLStreamReader reader = message.envelopeReader;
   653         message.envelopeReader = null;
   654         SOAPVersion soapVersion = message.soapVersion;
   655         // Move to soap:Envelope and verify
   656         if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT)
   657             XMLStreamReaderUtil.nextElementContent(reader);
   658         XMLStreamReaderUtil.verifyReaderState(reader,XMLStreamConstants.START_ELEMENT);
   659         if (SOAP_ENVELOPE.equals(reader.getLocalName()) && !soapVersion.nsUri.equals(reader.getNamespaceURI())) {
   660             throw new VersionMismatchException(soapVersion, soapVersion.nsUri, reader.getNamespaceURI());
   661         }
   662         XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_ENVELOPE);
   664         TagInfoset envelopeTag = new TagInfoset(reader);
   666         // Collect namespaces on soap:Envelope
   667         Map<String,String> namespaces = new HashMap<String,String>();
   668         for(int i=0; i< reader.getNamespaceCount();i++){
   669                 namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
   670         }
   672         // Move to next element
   673         XMLStreamReaderUtil.nextElementContent(reader);
   674         XMLStreamReaderUtil.verifyReaderState(reader,
   675                 javax.xml.stream.XMLStreamConstants.START_ELEMENT);
   677         HeaderList headers = null;
   678         TagInfoset headerTag = null;
   680         if (reader.getLocalName().equals(SOAP_HEADER)
   681                 && reader.getNamespaceURI().equals(soapVersion.nsUri)) {
   682             headerTag = new TagInfoset(reader);
   684             // Collect namespaces on soap:Header
   685             for(int i=0; i< reader.getNamespaceCount();i++){
   686                 namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
   687             }
   688             // skip <soap:Header>
   689             XMLStreamReaderUtil.nextElementContent(reader);
   691             // If SOAP header blocks are present (i.e. not <soap:Header/>)
   692             if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
   693                 headers = new HeaderList(soapVersion);
   695                 try {
   696                     // Cache SOAP header blocks
   697                     StreamHeaderDecoder headerDecoder = SOAPVersion.SOAP_11.equals(soapVersion) ? SOAP11StreamHeaderDecoder : SOAP12StreamHeaderDecoder;
   698                     cacheHeaders(reader, namespaces, headers, headerDecoder);
   699                 } catch (XMLStreamException e) {
   700                     // TODO need to throw more meaningful exception
   701                     throw new WebServiceException(e);
   702                 }
   703             }
   705             // Move to soap:Body
   706             XMLStreamReaderUtil.nextElementContent(reader);
   707         }
   709         // Verify that <soap:Body> is present
   710         XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_BODY);
   711         TagInfoset bodyTag = new TagInfoset(reader);
   713         String bodyPrologue = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
   714         message.init(envelopeTag,headerTag,message.attachmentSet,headers,bodyPrologue,bodyTag,null,reader,soapVersion);
   715         // when there's no payload,
   716         // it's tempting to use EmptyMessageImpl, but it doesn't preserve the infoset
   717         // of <envelope>,<header>, and <body>, so we need to stick to StreamMessage.
   718     }
   721     private static XMLStreamBuffer cacheHeaders(XMLStreamReader reader,
   722             Map<String, String> namespaces, HeaderList headers,
   723             StreamHeaderDecoder headerDecoder) throws XMLStreamException {
   724         MutableXMLStreamBuffer buffer = createXMLStreamBuffer();
   725         StreamReaderBufferCreator creator = new StreamReaderBufferCreator();
   726         creator.setXMLStreamBuffer(buffer);
   728         // Reader is positioned at the first header block
   729         while(reader.getEventType() == javax.xml.stream.XMLStreamConstants.START_ELEMENT) {
   730             Map<String,String> headerBlockNamespaces = namespaces;
   732             // Collect namespaces on SOAP header block
   733             if (reader.getNamespaceCount() > 0) {
   734                 headerBlockNamespaces = new HashMap<String,String>(namespaces);
   735                 for (int i = 0; i < reader.getNamespaceCount(); i++) {
   736                     headerBlockNamespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
   737                 }
   738             }
   740             // Mark
   741             XMLStreamBuffer mark = new XMLStreamBufferMark(headerBlockNamespaces, creator);
   742             // Create Header
   743             headers.add(headerDecoder.decodeHeader(reader, mark));
   746             // Cache the header block
   747             // After caching Reader will be positioned at next header block or
   748             // the end of the </soap:header>
   749             creator.createElementFragment(reader, false);
   750             if (reader.getEventType() != XMLStreamConstants.START_ELEMENT &&
   751                     reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
   752                 XMLStreamReaderUtil.nextElementContent(reader);
   753             }
   754         }
   756         return buffer;
   757     }
   759     private static MutableXMLStreamBuffer createXMLStreamBuffer() {
   760         // TODO: Decode should own one MutableXMLStreamBuffer for reuse
   761         // since it is more efficient. ISSUE: possible issue with
   762         // lifetime of information in the buffer if accessed beyond
   763         // the pipe line.
   764         return new MutableXMLStreamBuffer();
   765     }
   766 }

mercurial