001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.message.stream;
038:
039: import com.sun.xml.stream.buffer.MutableXMLStreamBuffer;
040: import com.sun.xml.ws.api.message.Message;
041: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
042: import com.sun.xml.ws.api.pipe.StreamSOAPCodec;
043: import java.io.IOException;
044: import javax.xml.soap.SOAPException;
045: import javax.xml.soap.SOAPMessage;
046: import javax.xml.stream.XMLOutputFactory;
047: import javax.xml.stream.XMLStreamReader;
048: import com.sun.istack.NotNull;
049: import com.sun.istack.Nullable;
050: import com.sun.xml.bind.api.Bridge;
051: import com.sun.xml.ws.api.message.AttachmentSet;
052: import com.sun.xml.ws.api.message.HeaderList;
053: import com.sun.xml.wss.jaxws.impl.logging.LogDomainConstants;
054: import java.util.logging.Logger;
055: import org.xml.sax.ContentHandler;
056: import org.xml.sax.ErrorHandler;
057: import org.xml.sax.SAXException;
058: import javax.xml.bind.JAXBException;
059: import javax.xml.bind.Unmarshaller;
060: import javax.xml.stream.XMLStreamException;
061: import javax.xml.stream.XMLStreamWriter;
062: import javax.xml.transform.Source;
063:
064: /**
065: *
066: * @author K.Venugopal@sun.com
067: */
068: public class LazyStreamBasedMessage extends Message {
069: protected static final Logger logger = Logger.getLogger(
070: LogDomainConstants.WSS_JAXWS_IMPL_DOMAIN,
071: LogDomainConstants.WSS_JAXWS_IMPL_DOMAIN_BUNDLE);
072: private StreamSOAPCodec codec = null;
073: private boolean readMessage = false;
074: private XMLStreamReader reader = null;
075: private Message message = null;
076: private MutableXMLStreamBuffer buffer = null;
077:
078: /** Creates a new instance of StreamMessage */
079: public LazyStreamBasedMessage(XMLStreamReader message,
080: StreamSOAPCodec codec) {
081: this .reader = message;
082: this .codec = codec;
083: }
084:
085: public StreamSOAPCodec getCodec() {
086: return codec;
087: }
088:
089: private synchronized void cacheMessage() {
090: if (!readMessage) {
091: message = codec.decode(reader);
092: readMessage = true;
093: }
094: }
095:
096: /**
097: * Returns true if headers are present in the message.
098: *
099: * @return
100: * true if headers are present.
101: */
102: public boolean hasHeaders() {
103: if (!readMessage) {
104: cacheMessage();
105: }
106: return message.hasHeaders();
107: }
108:
109: /**
110: * Gets all the headers of this message.
111: *
112: * <h3>Implementation Note</h3>
113: * <p>
114: * {@link Message} implementation is allowed to defer
115: * the construction of {@link HeaderList} object. So
116: * if you only want to check for the existence of any header
117: * element, use {@link #hasHeaders()}.
118: *
119: * @return
120: * always return the same non-null object.
121: */
122: public HeaderList getHeaders() {
123: if (!readMessage) {
124: cacheMessage();
125: }
126: return message.getHeaders();
127: }
128:
129: /**
130: * Gets the attachments of this message
131: * (attachments live outside a message.)
132: */
133: public @NotNull
134: AttachmentSet getAttachments() {
135: if (!readMessage) {
136: cacheMessage();
137: }
138: return message.getAttachments();
139: }
140:
141: /**
142: * Returns true if this message is a request message for a
143: * one way operation according to the given WSDL. False otherwise.
144: *
145: * <p>
146: * This method is functionally equivalent as doing
147: * {@code getOperation(port).getOperation().isOneWay()}
148: * (with proper null check and all.) But this method
149: * can sometimes work faster than that (for example,
150: * on the client side when used with SEI.)
151: *
152: * @param port
153: * {@link Message}s are always created under the context of
154: * one {@link WSDLPort} and they never go outside that context.
155: * Pass in that "governing" {@link WSDLPort} object here.
156: * We chose to receive this as a parameter instead of
157: * keeping {@link WSDLPort} in a message, just to save the storage.
158: *
159: * <p>
160: * The implementation of this method involves caching the return
161: * value, so the behavior is undefined if multiple callers provide
162: * different {@link WSDLPort} objects, which is a bug of the caller.
163: */
164: public boolean isOneWay(@NotNull
165: WSDLPort port) {
166: if (!readMessage) {
167: cacheMessage();
168: }
169: return message.isOneWay(port);
170: }
171:
172: /**
173: * Gets the local name of the payload element.
174: *
175: * @return
176: * null if a {@link Message} doesn't have any payload.
177: */
178: public @Nullable
179: String getPayloadLocalPart() {
180: if (!readMessage) {
181: cacheMessage();
182: }
183: return message.getPayloadLocalPart();
184: }
185:
186: /**
187: * Gets the namespace URI of the payload element.
188: *
189: * @return
190: * null if a {@link Message} doesn't have any payload.
191: */
192: public String getPayloadNamespaceURI() {
193: if (!readMessage) {
194: cacheMessage();
195: }
196: return message.getPayloadNamespaceURI();
197: }
198:
199: // I'm not putting @Nullable on it because doing null check on getPayloadLocalPart() should be suffice
200:
201: /**
202: * Returns true if a {@link Message} has a payload.
203: *
204: * <p>
205: * A message without a payload is a SOAP message that looks like:
206: * <pre><xmp>
207: * <S:Envelope>
208: * <S:Header>
209: * ...
210: * </S:Header>
211: * <S:Body />
212: * </S:Envelope>
213: * </xmp></pre>
214: */
215: public boolean hasPayload() {
216: if (!readMessage) {
217: cacheMessage();
218: }
219: return message.hasPayload();
220: }
221:
222: /**
223: * Returns true if this message is a fault.
224: *
225: * <p>
226: * Just a convenience method built on {@link #getPayloadNamespaceURI()}
227: * and {@link #getPayloadLocalPart()}.
228: */
229: public boolean isFault() {
230: return false;
231: // if(!readMessage){
232: // cacheMessage();
233: // }
234: // return message.isFault();
235: }
236:
237: /**
238: * Consumes this message including the envelope.
239: * returns it as a {@link Source} object.
240: */
241: public Source readEnvelopeAsSource() {
242: if (!readMessage) {
243: cacheMessage();
244: }
245: return message.readEnvelopeAsSource();
246: }
247:
248: /**
249: * Returns the payload as a {@link Source} object.
250: *
251: * This consumes the message.
252: *
253: * @return
254: * if there's no payload, this method returns null.
255: */
256: public Source readPayloadAsSource() {
257: if (!readMessage) {
258: cacheMessage();
259: }
260: return message.readPayloadAsSource();
261: }
262:
263: /**
264: * Creates the equivalent {@link SOAPMessage} from this message.
265: *
266: * This consumes the message.
267: *
268: * @throws SOAPException
269: * if there's any error while creating a {@link SOAPMessage}.
270: */
271: public SOAPMessage readAsSOAPMessage() throws SOAPException {
272: if (!readMessage) {
273: cacheMessage();
274: }
275: return message.readAsSOAPMessage();
276: }
277:
278: /**
279: * Reads the payload as a JAXB object by using the given unmarshaller.
280: *
281: * This consumes the message.
282: *
283: * @throws JAXBException
284: * If JAXB reports an error during the processing.
285: */
286: public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller)
287: throws JAXBException {
288: if (!readMessage) {
289: cacheMessage();
290: }
291: throw new UnsupportedOperationException();
292: }
293:
294: /**
295: * Reads the payload as a JAXB object according to the given {@link Bridge}.
296: *
297: * This consumes the message.
298: *
299: * @throws JAXBException
300: * If JAXB reports an error during the processing.
301: */
302: public <T> T readPayloadAsJAXB(Bridge<T> bridge)
303: throws JAXBException {
304: if (!readMessage) {
305: cacheMessage();
306: }
307: return message.readPayloadAsJAXB(bridge);
308: }
309:
310: /**
311: * Reads the payload as a {@link XMLStreamReader}
312: *
313: * This consumes the message.
314: *
315: * @return
316: * If there's no payload, this method returns null.
317: * Otherwise always non-null valid {@link XMLStreamReader} that points to
318: * the payload tag name.
319: */
320: public XMLStreamReader readPayload() throws XMLStreamException {
321: if (!readMessage) {
322: cacheMessage();
323: }
324: return message.readPayload();
325: }
326:
327: /**
328: * Writes the payload to StAX.
329: *
330: * This method writes just the payload of the message to the writer.
331: * This consumes the message.
332: * The implementation will not write
333: * {@link XMLStreamWriter#writeStartDocument()}
334: * nor
335: * {@link XMLStreamWriter#writeEndDocument()}
336: *
337: * <p>
338: * If there's no payload, this method is no-op.
339: *
340: * @throws XMLStreamException
341: * If the {@link XMLStreamWriter} reports an error,
342: * or some other errors happen during the processing.
343: */
344: public void writePayloadTo(XMLStreamWriter sw)
345: throws XMLStreamException {
346: if (!readMessage) {
347: cacheMessage();
348: }
349: message.writePayloadTo(sw);
350: }
351:
352: /**
353: * Writes the whole SOAP message (but not attachments)
354: * to the given writer.
355: *
356: * This consumes the message.
357: *
358: * @throws XMLStreamException
359: * If the {@link XMLStreamWriter} reports an error,
360: * or some other errors happen during the processing.
361: */
362: public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
363: if (!readMessage) {
364: cacheMessage();
365: }
366: message.writeTo(sw);
367: }
368:
369: /**
370: * Writes the whole SOAP envelope as SAX events.
371: *
372: * <p>
373: * This consumes the message.
374: *
375: * @param contentHandler
376: * must not be nulll.
377: * @param errorHandler
378: * must not be null.
379: * any error encountered during the SAX event production must be
380: * first reported to this error handler. Fatal errors can be then
381: * thrown as {@link SAXParseException}. {@link SAXException}s thrown
382: * from {@link ErrorHandler} should propagate directly through this method.
383: */
384: public void writeTo(ContentHandler contentHandler,
385: ErrorHandler errorHandler) throws SAXException {
386: if (!readMessage) {
387: cacheMessage();
388: }
389: message.writeTo(contentHandler, errorHandler);
390: }
391:
392: // TODO: do we need a method that reads payload as a fault?
393: // do we want a separte streaming representation of fault?
394: // or would SOAPFault in SAAJ do?
395:
396: /**
397: * Creates a copy of a {@link Message}.
398: *
399: * <p>
400: * This method creates a new {@link Message} whose header/payload/attachments/properties
401: * are identical to this {@link Message}. Once created, the created {@link Message}
402: * and the original {@link Message} behaves independently --- adding header/
403: * attachment to one {@link Message} doesn't affect another {@link Message}
404: * at all.
405: *
406: * <p>
407: * This method does <b>NOT</b> consume a message.
408: *
409: * <p>
410: * To enable efficient copy operations, there's a few restrictions on
411: * how copied message can be used.
412: *
413: * <ol>
414: * <li>The original and the copy may not be
415: * used concurrently by two threads (this allows two {@link Message}s
416: * to share some internal resources, such as JAXB marshallers.)
417: * Note that it's OK for the original and the copy to be processed
418: * by two threads, as long as they are not concurrent.
419: *
420: * <li>The copy has the same 'life scope'
421: * as the original (this allows shallower copy, such as
422: * JAXB beans wrapped in {@link JAXBMessage}.)
423: * </ol>
424: *
425: * <p>
426: * A 'life scope' of a message created during a message processing
427: * in a pipeline is until a pipeline processes the next message.
428: * A message cannot be kept beyond its life scope.
429: *
430: * (This experimental design is to allow message objects to be reused
431: * --- feedback appreciated.)
432: *
433: *
434: *
435: * <h3>Design Rationale</h3>
436: * <p>
437: * Since a {@link Message} body is read-once, sometimes
438: * (such as when you do fail-over, or WS-RM) you need to
439: * create an idential copy of a {@link Message}.
440: *
441: * <p>
442: * The actual copy operation depends on the layout
443: * of the data in memory, hence it's best to be done by
444: * the {@link Message} implementation itself.
445: *
446: * <p>
447: * The restrictions placed on the use of copied {@link Message} can be
448: * relaxed if necessary, but it will make the copy method more expensive.
449: */
450: // TODO: update the class javadoc with 'lifescope'
451: // and move the discussion about life scope there.
452: public Message copy() {
453: if (!readMessage) {
454: cacheMessage();
455: }
456: return message.copy();
457: }
458:
459: public XMLStreamReader readMessage() {
460:
461: if (!readMessage) {
462: return reader;
463: }
464:
465: if (buffer == null) {
466: try {
467: buffer = new com.sun.xml.stream.buffer.MutableXMLStreamBuffer();
468: javax.xml.stream.XMLStreamWriter writer = buffer
469: .createFromXMLStreamWriter();
470:
471: message.writeTo(writer);
472: } catch (javax.xml.stream.XMLStreamException ex) {
473: logger.log(java.util.logging.Level.SEVERE,
474: LogStringsMessages
475: .WSSMSG_0001_PROBLEM_CACHING(), ex);
476: }
477: }
478: try {
479: reader = buffer.readAsXMLStreamReader();
480: return reader;
481: } catch (XMLStreamException ex) {
482: logger.log(java.util.logging.Level.SEVERE,
483: LogStringsMessages
484: .WSSMSG_0002_ERROR_READING_BUFFER(), ex);
485: }
486: return null;
487: }
488:
489: public void print() throws XMLStreamException {
490: if (readMessage) {
491: try {
492: message.readAsSOAPMessage().writeTo(
493: java.lang.System.out);
494: return;
495: } catch (SOAPException ex) {
496: logger.log(java.util.logging.Level.SEVERE,
497: LogStringsMessages.WSSMSG_0003_ERROR_PRINT(),
498: ex);
499: } catch (IOException ex) {
500: logger.log(java.util.logging.Level.SEVERE,
501: LogStringsMessages.WSSMSG_0003_ERROR_PRINT(),
502: ex);
503: }
504: }
505: if (buffer == null) {
506: buffer = new MutableXMLStreamBuffer();
507: buffer.createFromXMLStreamReader(reader);
508: reader = buffer.readAsXMLStreamReader();
509: }
510: XMLOutputFactory xof = XMLOutputFactory.newInstance();
511: buffer.writeToXMLStreamWriter(xof
512: .createXMLStreamWriter(System.out));
513: }
514:
515: }
|