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  */
  25 
  26 package com.sun.xml.internal.ws.message.stream;
  27 
  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.stax.StreamReaderBufferCreator;
  34 import com.sun.xml.internal.ws.api.SOAPVersion;
  35 import com.sun.xml.internal.ws.api.message.AttachmentSet;
  36 import com.sun.xml.internal.ws.api.message.Header;
  37 import com.sun.xml.internal.ws.api.message.HeaderList;
  38 import com.sun.xml.internal.ws.api.message.Message;
  39 import com.sun.xml.internal.ws.api.message.MessageHeaders;
  40 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
  41 import com.sun.xml.internal.ws.encoding.TagInfoset;
  42 import com.sun.xml.internal.ws.message.AbstractMessageImpl;
  43 import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl;
  44 import com.sun.xml.internal.ws.spi.db.XMLBridge;
  45 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
  46 import com.sun.xml.internal.ws.util.xml.DummyLocation;
  47 import com.sun.xml.internal.ws.util.xml.StAXSource;
  48 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderToXMLStreamWriter;
  49 import org.xml.sax.ContentHandler;
  50 import org.xml.sax.ErrorHandler;
  51 import org.xml.sax.SAXException;
  52 import org.xml.sax.SAXParseException;
  53 import org.xml.sax.helpers.NamespaceSupport;
  54 
  55 import javax.xml.bind.JAXBException;
  56 import javax.xml.bind.Unmarshaller;
  57 import javax.xml.stream.*;
  58 import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
  59 import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
  60 import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
  61 import javax.xml.transform.Source;
  62 import javax.xml.ws.WebServiceException;
  63 import java.util.ArrayList;
  64 import java.util.Enumeration;
  65 import java.util.List;
  66 
  67 /**
  68  * {@link Message} implementation backed by {@link XMLStreamReader}.
  69  *
  70  * TODO: we need another message class that keeps {@link XMLStreamReader} that points
  71  * at the start of the envelope element.
  72  */
  73 public class StreamMessage extends AbstractMessageImpl {
  74     /**
  75      * The reader will be positioned at
  76      * the first child of the SOAP body
  77      */
  78     private @NotNull XMLStreamReader reader;
  79 
  80     // lazily created
  81     private @Nullable MessageHeaders headers;
  82 
  83     /**
  84      * Because the StreamMessage leaves out the white spaces around payload
  85      * when being instantiated the space characters between soap:Body opening and
  86      * payload is stored in this field to be reused later (necessary for message security);
  87      * Instantiated after StreamMessage creation
  88      */
  89     private String bodyPrologue = null;
  90 
  91     /**
  92      * instantiated after writing message to XMLStreamWriter
  93      */
  94     private String bodyEpilogue = null;
  95 
  96     private final String payloadLocalName;
  97 
  98     private final String payloadNamespaceURI;
  99 
 100     /**
 101      * infoset about the SOAP envelope, header, and body.
 102      *
 103      * <p>
 104      * If the creater of this object didn't care about those,
 105      * we use stock values.
 106      */
 107     private @NotNull TagInfoset envelopeTag;
 108     private @NotNull TagInfoset headerTag;
 109     private @NotNull TagInfoset bodyTag;
 110 
 111     /**
 112      * Used only for debugging. This records where the message was consumed.
 113      */
 114     private Throwable consumedAt;
 115 
 116     /**
 117      * Default s:Envelope, s:Header, and s:Body tag infoset definitions.
 118      *
 119      * We need 3 for SOAP 1.1, 3 for SOAP 1.2.
 120      */
 121     private static final TagInfoset[] DEFAULT_TAGS;
 122 
 123     static {
 124         DEFAULT_TAGS = new TagInfoset[6];
 125         create(SOAPVersion.SOAP_11);
 126         create(SOAPVersion.SOAP_12);
 127     }
 128 
 129     public StreamMessage(SOAPVersion v) {
 130         super(v);
 131         payloadLocalName = null;
 132         payloadNamespaceURI = null;
 133     }
 134     /**
 135      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
 136      * that points at the start element of the payload, and headers.
 137      *
 138      * <p>
 139      * This method creates a {@link Message} from a payload.
 140      *
 141      * @param headers
 142      *      if null, it means no headers. if non-null,
 143      *      it will be owned by this message.
 144      * @param reader
 145      *      points at the start element/document of the payload (or the end element of the &lt;s:Body>
 146      *      if there's no payload)
 147      */
 148     public StreamMessage(@Nullable MessageHeaders headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
 149         super(soapVersion);
 150         this.headers = headers;
 151         this.attachmentSet = attachmentSet;
 152         this.reader = reader;
 153 
 154         if(reader.getEventType()== START_DOCUMENT)
 155             XMLStreamReaderUtil.nextElementContent(reader);
 156 
 157         //if the reader is pointing to the end element </soapenv:Body> then its empty message
 158         // or no payload
 159         if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
 160             String body = reader.getLocalName();
 161             String nsUri = reader.getNamespaceURI();
 162             assert body != null;
 163             assert nsUri != null;
 164             //if its not soapenv:Body then throw exception, we received malformed stream
 165             if(body.equals("Body") && nsUri.equals(soapVersion.nsUri)){
 166                 this.payloadLocalName = null;
 167                 this.payloadNamespaceURI = null;
 168             }else{ //TODO: i18n and also we should be throwing better message that this
 169                 throw new WebServiceException("Malformed stream: {"+nsUri+"}"+body);
 170             }
 171         }else{
 172             this.payloadLocalName = reader.getLocalName();
 173             this.payloadNamespaceURI = reader.getNamespaceURI();
 174         }
 175 
 176         // use the default infoset representation for headers
 177         int base = soapVersion.ordinal()*3;
 178         this.envelopeTag = DEFAULT_TAGS[base];
 179         this.headerTag = DEFAULT_TAGS[base+1];
 180         this.bodyTag = DEFAULT_TAGS[base+2];
 181     }
 182 
 183     /**
 184      * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
 185      * and the complete infoset of the SOAP envelope.
 186      *
 187      * <p>
 188      * See {@link #StreamMessage(MessageHeaders, AttachmentSet, XMLStreamReader, SOAPVersion)} for
 189      * the description of the basic parameters.
 190      *
 191      * @param headerTag
 192      *      Null if the message didn't have a header tag.
 193      *
 194      */
 195     public StreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable MessageHeaders headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion) {
 196         this(envelopeTag, headerTag, attachmentSet, headers, null, bodyTag, null, reader, soapVersion);
 197     }
 198 
 199     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) {
 200         this(headers,attachmentSet,reader,soapVersion);
 201         if(envelopeTag == null ) {
 202             throw new IllegalArgumentException("EnvelopeTag TagInfoset cannot be null");
 203         }
 204         if(bodyTag == null ) {
 205             throw new IllegalArgumentException("BodyTag TagInfoset cannot be null");
 206         }
 207         this.envelopeTag = envelopeTag;
 208         this.headerTag = headerTag;
 209         this.bodyTag = bodyTag;
 210         this.bodyPrologue = bodyPrologue;
 211         this.bodyEpilogue = bodyEpilogue;
 212     }
 213 
 214     public boolean hasHeaders() {
 215         return headers!=null && headers.hasHeaders();
 216     }
 217 
 218     public MessageHeaders getHeaders() {
 219         if (headers == null) {
 220             headers = new HeaderList(getSOAPVersion());
 221         }
 222         return headers;
 223     }
 224 
 225     public String getPayloadLocalPart() {
 226         return payloadLocalName;
 227     }
 228 
 229     public String getPayloadNamespaceURI() {
 230         return payloadNamespaceURI;
 231     }
 232 
 233     public boolean hasPayload() {
 234         return payloadLocalName!=null;
 235     }
 236 
 237     public Source readPayloadAsSource() {
 238         if(hasPayload()) {
 239             assert unconsumed();
 240             return new StAXSource(reader, true, getInscopeNamespaces());
 241         } else
 242             return null;
 243     }
 244 
 245     /**
 246      * There is no way to enumerate inscope namespaces for XMLStreamReader. That means
 247      * namespaces declared in envelope, and body tags need to be computed using their
 248      * {@link TagInfoset}s.
 249      *
 250      * @return array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
 251      */
 252     private String[] getInscopeNamespaces() {
 253         NamespaceSupport nss = new NamespaceSupport();
 254 
 255         nss.pushContext();
 256         for(int i=0; i < envelopeTag.ns.length; i+=2) {
 257             nss.declarePrefix(envelopeTag.ns[i], envelopeTag.ns[i+1]);
 258         }
 259 
 260         nss.pushContext();
 261         for(int i=0; i < bodyTag.ns.length; i+=2) {
 262             nss.declarePrefix(bodyTag.ns[i], bodyTag.ns[i+1]);
 263         }
 264 
 265         List<String> inscope = new ArrayList<String>();
 266         for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) {
 267             String prefix = (String)en.nextElement();
 268             inscope.add(prefix);
 269             inscope.add(nss.getURI(prefix));
 270         }
 271         return inscope.toArray(new String[inscope.size()]);
 272     }
 273 
 274     public Object readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
 275         if(!hasPayload())
 276             return null;
 277         assert unconsumed();
 278         // TODO: How can the unmarshaller process this as a fragment?
 279         if(hasAttachments())
 280             unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshallerImpl(getAttachments()));
 281         try {
 282             return unmarshaller.unmarshal(reader);
 283         } finally{
 284             unmarshaller.setAttachmentUnmarshaller(null);
 285             XMLStreamReaderUtil.readRest(reader);
 286             XMLStreamReaderUtil.close(reader);
 287             XMLStreamReaderFactory.recycle(reader);
 288         }
 289     }
 290     /** @deprecated */
 291     public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
 292         if(!hasPayload())
 293             return null;
 294         assert unconsumed();
 295         T r = bridge.unmarshal(reader,
 296             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
 297         XMLStreamReaderUtil.readRest(reader);
 298         XMLStreamReaderUtil.close(reader);
 299         XMLStreamReaderFactory.recycle(reader);
 300         return r;
 301     }
 302 
 303     public <T> T readPayloadAsJAXB(XMLBridge<T> bridge) throws JAXBException {
 304         if(!hasPayload())
 305             return null;
 306         assert unconsumed();
 307         T r = bridge.unmarshal(reader,
 308             hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
 309         XMLStreamReaderUtil.readRest(reader);
 310         XMLStreamReaderUtil.close(reader);
 311         XMLStreamReaderFactory.recycle(reader);
 312         return r;
 313     }
 314 
 315     @Override
 316     public void consume() {
 317         assert unconsumed();
 318         XMLStreamReaderUtil.readRest(reader);
 319         XMLStreamReaderUtil.close(reader);
 320         XMLStreamReaderFactory.recycle(reader);
 321     }
 322 
 323     public XMLStreamReader readPayload() {
 324         if(!hasPayload())
 325             return null;
 326         // TODO: What about access at and beyond </soap:Body>
 327         assert unconsumed();
 328         return this.reader;
 329     }
 330 
 331     public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException {
 332         assert unconsumed();
 333 
 334         if(payloadLocalName==null) {
 335             return; // no body
 336         }
 337 
 338         if (bodyPrologue != null) {
 339             writer.writeCharacters(bodyPrologue);
 340         }
 341 
 342         XMLStreamReaderToXMLStreamWriter conv = new XMLStreamReaderToXMLStreamWriter();
 343 
 344         while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
 345             String name = reader.getLocalName();
 346             String nsUri = reader.getNamespaceURI();
 347 
 348             // After previous conv.bridge() call the cursor will be at END_ELEMENT.
 349             // Check if its not soapenv:Body then move to next ELEMENT
 350             if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
 351 
 352                 if (!isBodyElement(name, nsUri)){
 353                     // closing payload element: store epilogue for further signing, if applicable
 354                     // however if there more than one payloads exist - the last one is stored
 355                     String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
 356                     if (whiteSpaces != null) {
 357                         this.bodyEpilogue = whiteSpaces;
 358                         // write it to the message too
 359                         writer.writeCharacters(whiteSpaces);
 360                     }
 361                 } else {
 362                     // body closed > exit
 363                     break;
 364                 }
 365 
 366             } else {
 367                 // payload opening element: copy payload to writer
 368                 conv.bridge(reader,writer);
 369             }
 370         }
 371 
 372         XMLStreamReaderUtil.readRest(reader);
 373         XMLStreamReaderUtil.close(reader);
 374         XMLStreamReaderFactory.recycle(reader);
 375     }
 376 
 377     private boolean isBodyElement(String name, String nsUri) {
 378         return name.equals("Body") && nsUri.equals(soapVersion.nsUri);
 379     }
 380 
 381     public void writeTo(XMLStreamWriter sw) throws XMLStreamException{
 382         writeEnvelope(sw);
 383     }
 384 
 385     /**
 386      * This method should be called when the StreamMessage is created with a payload
 387      * @param writer
 388      */
 389     private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException {
 390         writer.writeStartDocument();
 391         envelopeTag.writeStart(writer);
 392 
 393         //write headers
 394         MessageHeaders hl = getHeaders();
 395         if (hl.hasHeaders() && headerTag == null) headerTag = new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
 396         if (headerTag != null) {
 397             headerTag.writeStart(writer);
 398             if (hl.hasHeaders()){
 399                 for(Header h : hl.asList()){
 400                     h.writeTo(writer);
 401                 }
 402             }
 403             writer.writeEndElement();
 404         }
 405         bodyTag.writeStart(writer);
 406         if(hasPayload())
 407             writePayloadTo(writer);
 408         writer.writeEndElement();
 409         writer.writeEndElement();
 410         writer.writeEndDocument();
 411     }
 412 
 413     public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
 414         assert unconsumed();
 415 
 416         try {
 417             if(payloadLocalName==null)
 418                 return; // no body
 419 
 420             if (bodyPrologue != null) {
 421                 char[] chars = bodyPrologue.toCharArray();
 422                 contentHandler.characters(chars, 0, chars.length);
 423             }
 424 
 425             XMLStreamReaderToContentHandler conv = new XMLStreamReaderToContentHandler(reader,contentHandler,true,fragment,getInscopeNamespaces());
 426 
 427             while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
 428                 String name = reader.getLocalName();
 429                 String nsUri = reader.getNamespaceURI();
 430 
 431                 // After previous conv.bridge() call the cursor will be at END_ELEMENT.
 432                 // Check if its not soapenv:Body then move to next ELEMENT
 433                 if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
 434 
 435                     if (!isBodyElement(name, nsUri)){
 436                         // closing payload element: store epilogue for further signing, if applicable
 437                         // however if there more than one payloads exist - the last one is stored
 438                         String whiteSpaces = XMLStreamReaderUtil.nextWhiteSpaceContent(reader);
 439                         if (whiteSpaces != null) {
 440                             this.bodyEpilogue = whiteSpaces;
 441                             // write it to the message too
 442                             char[] chars = whiteSpaces.toCharArray();
 443                             contentHandler.characters(chars, 0, chars.length);
 444                         }
 445                     } else {
 446                         // body closed > exit
 447                         break;
 448                     }
 449 
 450                 } else {
 451                     // payload opening element: copy payload to writer
 452                     conv.bridge();
 453                 }
 454             }
 455             XMLStreamReaderUtil.readRest(reader);
 456             XMLStreamReaderUtil.close(reader);
 457             XMLStreamReaderFactory.recycle(reader);
 458         } catch (XMLStreamException e) {
 459             Location loc = e.getLocation();
 460             if(loc==null)   loc = DummyLocation.INSTANCE;
 461 
 462             SAXParseException x = new SAXParseException(
 463                 e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e);
 464             errorHandler.error(x);
 465         }
 466     }
 467 
 468     // TODO: this method should be probably rewritten to respect spaces between eelements; is it used at all?
 469     public Message copy() {
 470         try {
 471             assert unconsumed();
 472             consumedAt = null; // but we don't want to mark it as consumed
 473             MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
 474             StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb);
 475 
 476             // preserving inscope namespaces from envelope, and body. Other option
 477             // would be to create a filtering XMLStreamReader from reader+envelopeTag+bodyTag
 478             c.storeElement(envelopeTag.nsUri, envelopeTag.localName, envelopeTag.prefix, envelopeTag.ns);
 479             c.storeElement(bodyTag.nsUri, bodyTag.localName, bodyTag.prefix, bodyTag.ns);
 480 
 481             if (hasPayload()) {
 482                 // Loop all the way for multi payload case
 483                 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
 484                     String name = reader.getLocalName();
 485                     String nsUri = reader.getNamespaceURI();
 486                     if(isBodyElement(name, nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
 487                         break;
 488                     c.create(reader);
 489 
 490                     // Skip whitespaces in between payload and </Body> or between elements
 491                     // those won't be in the message itself, but we store them in field bodyEpilogue
 492                     if (reader.isWhiteSpace()) {
 493                         bodyEpilogue = XMLStreamReaderUtil.currentWhiteSpaceContent(reader);
 494                     } else {
 495                         // clear it in case the existing was not the last one
 496                         // (we are interested only in the last one?)
 497                         bodyEpilogue = null;
 498                     }
 499                 }
 500             }
 501             c.storeEndElement();        // create structure element for </Body>
 502             c.storeEndElement();        // create structure element for </Envelope>
 503             c.storeEndElement();        // create structure element for END_DOCUMENT
 504 
 505             XMLStreamReaderUtil.readRest(reader);
 506             XMLStreamReaderUtil.close(reader);
 507             XMLStreamReaderFactory.recycle(reader);
 508 
 509             reader = xsb.readAsXMLStreamReader();
 510             XMLStreamReader clone = xsb.readAsXMLStreamReader();
 511 
 512             // advance to the start tag of the <Body> first child element
 513             proceedToRootElement(reader);
 514             proceedToRootElement(clone);
 515 
 516             return new StreamMessage(envelopeTag, headerTag, attachmentSet, HeaderList.copy(headers), bodyPrologue, bodyTag, bodyEpilogue, clone, soapVersion);
 517         } catch (XMLStreamException e) {
 518             throw new WebServiceException("Failed to copy a message",e);
 519         }
 520     }
 521 
 522     private void proceedToRootElement(XMLStreamReader xsr) throws XMLStreamException {
 523         assert xsr.getEventType()==START_DOCUMENT;
 524         xsr.nextTag();
 525         xsr.nextTag();
 526         xsr.nextTag();
 527         assert xsr.getEventType()==START_ELEMENT || xsr.getEventType()==END_ELEMENT;
 528     }
 529 
 530     public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException {
 531         contentHandler.setDocumentLocator(NULL_LOCATOR);
 532         contentHandler.startDocument();
 533         envelopeTag.writeStart(contentHandler);
 534         if (hasHeaders() && headerTag == null) headerTag = new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
 535         if (headerTag != null) {
 536             headerTag.writeStart(contentHandler);
 537             if (hasHeaders()) {
 538                 MessageHeaders headers = getHeaders();
 539                 for (Header h : headers.asList()) {
 540                     // shouldn't JDK be smart enough to use array-style indexing for this foreach!?
 541                     h.writeTo(contentHandler,errorHandler);
 542                 }
 543             }
 544             headerTag.writeEnd(contentHandler);
 545         }
 546         bodyTag.writeStart(contentHandler);
 547         writePayloadTo(contentHandler,errorHandler, true);
 548         bodyTag.writeEnd(contentHandler);
 549         envelopeTag.writeEnd(contentHandler);
 550         contentHandler.endDocument();
 551     }
 552 
 553     /**
 554      * Used for an assertion. Returns true when the message is unconsumed,
 555      * or otherwise throw an exception.
 556      *
 557      * <p>
 558      * Calling this method also marks the stream as 'consumed'
 559      */
 560     private boolean unconsumed() {
 561         if(payloadLocalName==null)
 562             return true;    // no payload. can be consumed multiple times.
 563 
 564         if(reader.getEventType()!=XMLStreamReader.START_ELEMENT) {
 565             AssertionError error = new AssertionError("StreamMessage has been already consumed. See the nested exception for where it's consumed");
 566             error.initCause(consumedAt);
 567             throw error;
 568         }
 569         consumedAt = new Exception().fillInStackTrace();
 570         return true;
 571     }
 572 
 573     private static void create(SOAPVersion v) {
 574         int base = v.ordinal()*3;
 575         DEFAULT_TAGS[base  ] = new TagInfoset(v.nsUri,"Envelope","S",EMPTY_ATTS,"S",v.nsUri);
 576         DEFAULT_TAGS[base+1] = new TagInfoset(v.nsUri,"Header","S",EMPTY_ATTS);
 577         DEFAULT_TAGS[base+2] = new TagInfoset(v.nsUri,"Body","S",EMPTY_ATTS);
 578     }
 579 
 580     public String getBodyPrologue() {
 581         return bodyPrologue;
 582     }
 583 
 584     public String getBodyEpilogue() {
 585         return bodyEpilogue;
 586     }
 587 
 588     public XMLStreamReader getReader() {
 589         assert unconsumed();
 590         return reader;
 591     }
 592 }