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

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
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.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;
55
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;
61
62 import javax.xml.bind.JAXBException;
63 import javax.xml.bind.Unmarshaller;
64 import javax.xml.stream.*;
65
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;
76
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;
89
90 // lazily created
91 private @Nullable MessageHeaders headers;
92
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;
100
101 /**
102 * instantiated after writing message to XMLStreamWriter
103 */
104 private String bodyEpilogue = null;
105
106 private String payloadLocalName;
107
108 private String payloadNamespaceURI;
109
110 /**
111 * Used only for debugging. This records where the message was consumed.
112 */
113 private Throwable consumedAt;
114
115 private XMLStreamReader envelopeReader;
116
117 public StreamMessage(SOAPVersion v) {
118 super(v);
119 payloadLocalName = null;
120 payloadNamespaceURI = null;
121 }
122
123 public StreamMessage(SOAPVersion v, @NotNull XMLStreamReader envelope, @NotNull AttachmentSet attachments) {
124 super(v);
125 envelopeReader = envelope;
126 attachmentSet = attachments;
127 }
128
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 }
150
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 }
169
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;
174
175 if(reader.getEventType()== START_DOCUMENT)
176 XMLStreamReaderUtil.nextElementContent(reader);
177
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 }
196
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 }
203
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 }
219
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 }
224
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 }
239
240 public boolean hasHeaders() {
241 if ( envelopeReader != null ) readEnvelope(this);
242 return headers!=null && headers.hasHeaders();
243 }
244
245 public MessageHeaders getHeaders() {
246 if ( envelopeReader != null ) readEnvelope(this);
247 if (headers == null) {
248 headers = new HeaderList(getSOAPVersion());
249 }
250 return headers;
251 }
252
253 public String getPayloadLocalPart() {
254 if ( envelopeReader != null ) readEnvelope(this);
255 return payloadLocalName;
256 }
257
258 public String getPayloadNamespaceURI() {
259 if ( envelopeReader != null ) readEnvelope(this);
260 return payloadNamespaceURI;
261 }
262
263 public boolean hasPayload() {
264 if ( envelopeReader != null ) readEnvelope(this);
265 return payloadLocalName!=null;
266 }
267
268 public Source readPayloadAsSource() {
269 if(hasPayload()) {
270 assert unconsumed();
271 return new StAXSource(reader, true, getInscopeNamespaces());
272 } else
273 return null;
274 }
275
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();
285
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 }
290
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 }
295
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 }
304
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 }
333
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 }
345
346 @Override
347 public void consume() {
348 assert unconsumed();
349 XMLStreamReaderUtil.readRest(reader);
350 XMLStreamReaderUtil.close(reader);
351 XMLStreamReaderFactory.recycle(reader);
352 }
353
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 }
361
362 public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException {
363 if ( envelopeReader != null ) readEnvelope(this);
364 assert unconsumed();
365
366 if(payloadLocalName==null) {
367 return; // no body
368 }
369
370 if (bodyPrologue != null) {
371 writer.writeCharacters(bodyPrologue);
372 }
373
374 XMLStreamReaderToXMLStreamWriter conv = new XMLStreamReaderToXMLStreamWriter();
375
376 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
377 String name = reader.getLocalName();
378 String nsUri = reader.getNamespaceURI();
379
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){
383
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 }
397
398 } else {
399 // payload opening element: copy payload to writer
400 conv.bridge(reader,writer);
401 }
402 }
403
404 XMLStreamReaderUtil.readRest(reader);
405 XMLStreamReaderUtil.close(reader);
406 XMLStreamReaderFactory.recycle(reader);
407 }
408
409 private boolean isBodyElement(String name, String nsUri) {
410 return name.equals("Body") && nsUri.equals(soapVersion.nsUri);
411 }
412
413 public void writeTo(XMLStreamWriter sw) throws XMLStreamException{
414 if ( envelopeReader != null ) readEnvelope(this);
415 writeEnvelope(sw);
416 }
417
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);
426
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 }
446
447 public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
448 if ( envelopeReader != null ) readEnvelope(this);
449 assert unconsumed();
450
451 try {
452 if(payloadLocalName==null)
453 return; // no body
454
455 if (bodyPrologue != null) {
456 char[] chars = bodyPrologue.toCharArray();
457 contentHandler.characters(chars, 0, chars.length);
458 }
459
460 XMLStreamReaderToContentHandler conv = new XMLStreamReaderToContentHandler(reader,contentHandler,true,fragment,getInscopeNamespaces());
461
462 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
463 String name = reader.getLocalName();
464 String nsUri = reader.getNamespaceURI();
465
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){
469
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 }
484
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;
496
497 SAXParseException x = new SAXParseException(
498 e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e);
499 errorHandler.error(x);
500 }
501 }
502
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);
512
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);
517
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);
526
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
541
542 XMLStreamReaderUtil.readRest(reader);
543 XMLStreamReaderUtil.close(reader);
544 XMLStreamReaderFactory.recycle(reader);
545
546 reader = xsb.readAsXMLStreamReader();
547 XMLStreamReader clone = xsb.readAsXMLStreamReader();
548
549 // advance to the start tag of the <Body> first child element
550 proceedToRootElement(reader);
551 proceedToRootElement(clone);
552
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 }
558
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 }
566
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 }
590
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.
601
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 }
610
611 public String getBodyPrologue() {
612 if ( envelopeReader != null ) readEnvelope(this);
613 return bodyPrologue;
614 }
615
616 public String getBodyEpilogue() {
617 if ( envelopeReader != null ) readEnvelope(this);
618 return bodyEpilogue;
619 }
620
621 public XMLStreamReader getReader() {
622 if ( envelopeReader != null ) readEnvelope(this);
623 assert unconsumed();
624 return reader;
625 }
626
627
628 private static final String SOAP_ENVELOPE = "Envelope";
629 private static final String SOAP_HEADER = "Header";
630 private static final String SOAP_BODY = "Body";
631
632 protected interface StreamHeaderDecoder {
633 public Header decodeHeader(XMLStreamReader reader, XMLStreamBuffer mark);
634 }
635
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 };
642
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 };
649
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);
663
664 TagInfoset envelopeTag = new TagInfoset(reader);
665
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 }
671
672 // Move to next element
673 XMLStreamReaderUtil.nextElementContent(reader);
674 XMLStreamReaderUtil.verifyReaderState(reader,
675 javax.xml.stream.XMLStreamConstants.START_ELEMENT);
676
677 HeaderList headers = null;
678 TagInfoset headerTag = null;
679
680 if (reader.getLocalName().equals(SOAP_HEADER)
681 && reader.getNamespaceURI().equals(soapVersion.nsUri)) {
682 headerTag = new TagInfoset(reader);
683
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);
690
691 // If SOAP header blocks are present (i.e. not <soap:Header/>)
692 if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
693 headers = new HeaderList(soapVersion);
694
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 }
704
705 // Move to soap:Body
706 XMLStreamReaderUtil.nextElementContent(reader);
707 }
708
709 // Verify that <soap:Body> is present
710 XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_BODY);
711 TagInfoset bodyTag = new TagInfoset(reader);
712
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 }
719
720
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);
727
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;
731
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 }
739
740 // Mark
741 XMLStreamBuffer mark = new XMLStreamBufferMark(headerBlockNamespaces, creator);
742 // Create Header
743 headers.add(headerDecoder.decodeHeader(reader, mark));
744
745
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 }
755
756 return buffer;
757 }
758
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