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

Wed, 27 Apr 2016 01:27:09 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:27:09 +0800
changeset 0
373ffda63c9a
child 637
9c07ef4934dd
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/jaxws/
changeset: 657:d47a47f961ee
tag: jdk8u25-b17

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

mercurial