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

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 384
8f2986ff0235
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

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

mercurial