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: package com.sun.xml.ws.api.message;
037:
038: import com.sun.istack.NotNull;
039: import com.sun.istack.Nullable;
040: import com.sun.xml.bind.api.Bridge;
041: import com.sun.xml.ws.api.BindingID;
042: import com.sun.xml.ws.api.SOAPVersion;
043: import com.sun.xml.ws.api.WSBinding;
044: import com.sun.xml.ws.api.addressing.AddressingVersion;
045: import com.sun.xml.ws.api.model.JavaMethod;
046: import com.sun.xml.ws.api.model.SEIModel;
047: import com.sun.xml.ws.api.model.wsdl.WSDLBoundOperation;
048: import com.sun.xml.ws.api.model.wsdl.WSDLBoundPortType;
049: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
050: import com.sun.xml.ws.api.pipe.Codec;
051: import com.sun.xml.ws.api.pipe.Pipe;
052: import com.sun.xml.ws.api.streaming.XMLStreamReaderFactory;
053: import com.sun.xml.ws.client.dispatch.DispatchImpl;
054: import com.sun.xml.ws.message.AttachmentSetImpl;
055: import com.sun.xml.ws.message.jaxb.JAXBMessage;
056: import org.jvnet.staxex.XMLStreamReaderEx;
057: import org.jvnet.staxex.XMLStreamWriterEx;
058: import org.xml.sax.ContentHandler;
059: import org.xml.sax.ErrorHandler;
060: import org.xml.sax.SAXException;
061: import org.xml.sax.SAXParseException;
062:
063: import javax.xml.bind.JAXBException;
064: import javax.xml.bind.Unmarshaller;
065: import javax.xml.namespace.QName;
066: import javax.xml.soap.SOAPException;
067: import javax.xml.soap.SOAPMessage;
068: import javax.xml.stream.XMLStreamException;
069: import javax.xml.stream.XMLStreamReader;
070: import javax.xml.stream.XMLStreamWriter;
071: import javax.xml.transform.Source;
072: import javax.xml.ws.Dispatch;
073: import java.io.InputStream;
074: import java.lang.reflect.Method;
075: import java.lang.reflect.Proxy;
076: import java.util.UUID;
077:
078: /**
079: * Represents a SOAP message.
080: *
081: *
082: * <h2>What is a message?</h2>
083: * <p>
084: * A {@link Message} consists of the following:
085: *
086: * <ol>
087: * <li>
088: * Random-accessible list of headers.
089: * a header is a representation of an element inside
090: * <soap:Header>.
091: * It can be read multiple times,
092: * can be added or removed, but it is not modifiable.
093: * See {@link HeaderList} for more about headers.
094: *
095: * <li>
096: * The payload of the message, which is a representation
097: * of an element inside <soap:Body>.
098: * the payload is streamed, and therefore it can be
099: * only read once (or can be only written to something once.)
100: * once a payload is used, a message is said to be <b>consumed</b>.
101: * A message {@link #hasPayload() may not have any payload.}
102: *
103: * <li>
104: * Attachments.
105: * TODO: can attachments be streamed? I suspect so.
106: * does anyone need to read attachment twice?
107: *
108: * </ol>
109: *
110: *
111: * <h2>How does this abstraction work?</h2>
112: * <p>
113: * The basic idea behind the {@link Message} is to hide the actual
114: * data representation. For example, a {@link Message} might be
115: * constructed on top of an {@link InputStream} from the accepted HTTP connection,
116: * or it might be constructed on top of a JAXB object as a result
117: * of the method invocation through {@link Proxy}. There will be
118: * a {@link Message} implementation for each of those cases.
119: *
120: * <p>
121: * This interface provides a lot of methods that access the payload
122: * in many different forms, and implementations can implement those
123: * methods in the best possible way.
124: *
125: * <p>
126: * A particular attention is paid to make sure that a {@link Message}
127: * object can be constructed on a stream that is not fully read yet.
128: * We believe this improves the turn-around time on the server side.
129: *
130: * <p>
131: * It is often useful to wrap a {@link Message} into another {@link Message},
132: * for example to encrypt the body, or to verify the signature as the body
133: * is read.
134: *
135: * <p>
136: * This representation is also used for a REST-ful XML message.
137: * In such case we'll construct a {@link Message} with empty
138: * attachments and headers, and when serializing all headers
139: * and attachments will be ignored.
140: *
141: *
142: *
143: * <h2>Message and XOP</h2>
144: * <p>
145: * XOP is considered as an {@link Codec}, and therefore when you are looking at
146: * {@link Message}, you'll never see <xop:Include> or any such elements
147: * (instead you'll see the base64 data inlined.) If a consumer of infoset isn't
148: * interested in handling XOP by himself, this allows him to work with XOP
149: * correctly even without noticing it.
150: *
151: * <p>
152: * For producers and consumers that are interested in accessing the binary data
153: * more efficiently, they can use {@link XMLStreamReaderEx} and
154: * {@link XMLStreamWriterEx}.
155: *
156: *
157: *
158: * <h2>Message lifespan</h2>
159: * <p>
160: * Often {@link Packet} include information local to a particular
161: * invocaion (such as {@code HttpServletRequest}, from this angle, it makes sense
162: * to tie a lifespan of a message to one pipeline invocation.
163: * <p>
164: * On the other hand, if you think about WS-RM, it often needs to hold on to
165: * a message longer than a pipeline invocation (you might get an HTTP request,
166: * get a message X, get a second HTTP request, get another message Y, and
167: * only then you might want to process X.)
168: * <p>
169: * TODO: what do we do about this?
170: *
171: *
172: * <pre>
173: * TODO: can body element have foreign attributes? maybe ID for security?
174: * Yes, when the SOAP body is signed there will be an ID attribute present
175: * But in this case any security based impl may need access
176: * to the concrete representation.
177: * TODO: HTTP headers?
178: * Yes. Abstracted as transport-based properties.
179: * TODO: who handles SOAP 1.1 and SOAP 1.2 difference?
180: * As separate channel implementations responsible for the creation of the
181: * message?
182: * TODO: session?
183: * TODO: Do we need to expose SOAPMessage explicitly?
184: * SOAPMessage could be the concrete representation but is it necessary to
185: * transform between different concrete representations?
186: * Perhaps this comes down to how use channels for creation and processing.
187: * TODO: Do we need to distinguish better between creation and processing?
188: * Do we really need the requirement that a created message can be resused
189: * for processing. Shall we bifurcate?
190: *
191: * TODO: SOAP version issue
192: * SOAP version is determined by the context, so message itself doesn't carry it around (?)
193: *
194: * TODO: wrapping message needs easier. in particular properties and attachments.
195: * </pre>
196: *
197: * @author Kohsuke Kawaguchi
198: */
199: public abstract class Message {
200:
201: /**
202: * Returns true if headers are present in the message.
203: *
204: * @return
205: * true if headers are present.
206: */
207: public abstract boolean hasHeaders();
208:
209: /**
210: * Gets all the headers of this message.
211: *
212: * <h3>Implementation Note</h3>
213: * <p>
214: * {@link Message} implementation is allowed to defer
215: * the construction of {@link HeaderList} object. So
216: * if you only want to check for the existence of any header
217: * element, use {@link #hasHeaders()}.
218: *
219: * @return
220: * always return the same non-null object.
221: */
222: public abstract @NotNull
223: HeaderList getHeaders();
224:
225: /**
226: * Gets the attachments of this message
227: * (attachments live outside a message.)
228: */
229: public @NotNull
230: AttachmentSet getAttachments() {
231: if (attachmentSet == null) {
232: attachmentSet = new AttachmentSetImpl();
233: }
234: return attachmentSet;
235: }
236:
237: /**
238: * Optimization hint for the derived class to check
239: * if we may have some attachments.
240: */
241: protected boolean hasAttachments() {
242: return attachmentSet != null;
243: }
244:
245: protected AttachmentSet attachmentSet;
246:
247: private WSDLBoundOperation operation = null;
248:
249: /**
250: * Returns the operation of which this message is an instance of.
251: *
252: * <p>
253: * This method relies on {@link WSDLBoundPortType#getOperation(String, String)} but
254: * it does so in an efficient way.
255: *
256: * <p>
257: * This method works only for a request. A pipe can determine an operation for a request,
258: * and then keep it in a local variable to use it with a response, so there should be
259: * no need to find out operation from a response (besides, there might not be any response!).
260: *
261: * @param boundPortType
262: * This represents the port for which this message is used.
263: * Most {@link Pipe}s should get this information when they are created,
264: * since a pippeline always work against a particular type of {@link WSDLPort}.
265: *
266: * @return
267: * Null if the operation was not found. This is possible, for example when a protocol
268: * message is sent through a pipeline, or when we receive an invalid request on the server,
269: * or when we are on the client and the user appliation sends a random DOM through
270: * {@link Dispatch}, so this error needs to be handled gracefully.
271: */
272: public final @Nullable
273: WSDLBoundOperation getOperation(@NotNull
274: WSDLBoundPortType boundPortType) {
275: if (operation == null)
276: operation = boundPortType.getOperation(
277: getPayloadNamespaceURI(), getPayloadLocalPart());
278: return operation;
279: }
280:
281: /**
282: * The same as {@link #getOperation(WSDLBoundPortType)} but
283: * takes {@link WSDLPort} for convenience.
284: */
285: public final @Nullable
286: WSDLBoundOperation getOperation(@NotNull
287: WSDLPort port) {
288: return getOperation(port.getBinding());
289: }
290:
291: /**
292: * Returns the java Method of which this message is an instance of.
293: *
294: *
295: * <p>
296: * This method works only for a request. A pipe can determine a {@link Method}
297: * for a request, and then keep it in a local variable to use it with a response,
298: * so there should be no need to find out operation from a response (besides,
299: * there might not be any response!).
300: *
301: * @param seiModel
302: * This represents the java model for the endpoint
303: * Some server {@link Pipe}s would get this information when they are created.
304: *
305: * @return
306: * Null if there is no corresponding Method for this message. This is
307: * possible, for example when a protocol message is sent through a
308: * pipeline, or when we receive an invalid request on the server,
309: * or when we are on the client and the user appliation sends a random
310: * DOM through {@link Dispatch}, so this error needs to be handled
311: * gracefully.
312: */
313: public final @Nullable
314: JavaMethod getMethod(@NotNull
315: SEIModel seiModel) {
316: String localPart = getPayloadLocalPart();
317: String nsUri;
318: if (localPart == null) {
319: localPart = "";
320: nsUri = "";
321: } else {
322: nsUri = getPayloadNamespaceURI();
323: }
324: QName name = new QName(nsUri, localPart);
325: return seiModel.getJavaMethod(name);
326: }
327:
328: private Boolean isOneWay;
329:
330: /**
331: * Returns true if this message is a request message for a
332: * one way operation according to the given WSDL. False otherwise.
333: *
334: * <p>
335: * This method is functionally equivalent as doing
336: * {@code getOperation(port).getOperation().isOneWay()}
337: * (with proper null check and all.) But this method
338: * can sometimes work faster than that (for example,
339: * on the client side when used with SEI.)
340: *
341: * @param port
342: * {@link Message}s are always created under the context of
343: * one {@link WSDLPort} and they never go outside that context.
344: * Pass in that "governing" {@link WSDLPort} object here.
345: * We chose to receive this as a parameter instead of
346: * keeping {@link WSDLPort} in a message, just to save the storage.
347: *
348: * <p>
349: * The implementation of this method involves caching the return
350: * value, so the behavior is undefined if multiple callers provide
351: * different {@link WSDLPort} objects, which is a bug of the caller.
352: */
353: public boolean isOneWay(@NotNull
354: WSDLPort port) {
355: if (isOneWay == null) {
356: // we don't know, so compute.
357: WSDLBoundOperation op = getOperation(port);
358: if (op != null)
359: isOneWay = op.getOperation().isOneWay();
360: else
361: // the contract is to return true only when it's known to be one way.
362: isOneWay = false;
363: }
364: return isOneWay;
365: }
366:
367: /**
368: * Makes an assertion that this {@link Message} is
369: * a request message for an one-way operation according
370: * to the context WSDL.
371: *
372: * <p>
373: * This method is really only intended to be invoked from within
374: * the JAX-WS runtime, and not by any code building on top of it.
375: *
376: * <p>
377: * This method can be invoked only when the caller "knows" what
378: * WSDL says. Also, there's no point in invoking this method if the caller
379: * is doing {@code getOperation(port).getOperation().isOneWay()},
380: * or sniffing the payload tag name.
381: * In particular, this includes {@link DispatchImpl}.
382: *
383: * <p>
384: * Once called, this allows {@link #isOneWay(WSDLPort)} method
385: * to return a value quickly.
386: *
387: * @see #isOneWay(WSDLPort)
388: */
389: public final void assertOneWay(boolean value) {
390: // if two callers make different assertions, that's a bug.
391: // this is an assertion, not a runtime check because
392: // nobody outside JAX-WS should be using this.
393: assert isOneWay == null || isOneWay == value;
394:
395: isOneWay = value;
396: }
397:
398: /**
399: * Gets the local name of the payload element.
400: *
401: * @return
402: * null if a {@link Message} doesn't have any payload.
403: */
404: public abstract @Nullable
405: String getPayloadLocalPart();
406:
407: /**
408: * Gets the namespace URI of the payload element.
409: *
410: * @return
411: * null if a {@link Message} doesn't have any payload.
412: */
413: public abstract String getPayloadNamespaceURI();
414:
415: // I'm not putting @Nullable on it because doing null check on getPayloadLocalPart() should be suffice
416:
417: /**
418: * Returns true if a {@link Message} has a payload.
419: *
420: * <p>
421: * A message without a payload is a SOAP message that looks like:
422: * <pre><xmp>
423: * <S:Envelope>
424: * <S:Header>
425: * ...
426: * </S:Header>
427: * <S:Body />
428: * </S:Envelope>
429: * </xmp></pre>
430: */
431: public abstract boolean hasPayload();
432:
433: /**
434: * Returns true if this message is a fault.
435: *
436: * <p>
437: * Just a convenience method built on {@link #getPayloadNamespaceURI()}
438: * and {@link #getPayloadLocalPart()}.
439: */
440: public boolean isFault() {
441: // TODO: is SOAP version a property of a Message?
442: // or is it defined by external factors?
443: // how do I compare?
444: String localPart = getPayloadLocalPart();
445: if (localPart == null || !localPart.equals("Fault"))
446: return false;
447:
448: String nsUri = getPayloadNamespaceURI();
449: return nsUri.equals(SOAPVersion.SOAP_11.nsUri)
450: || nsUri.equals(SOAPVersion.SOAP_12.nsUri);
451: }
452:
453: /**
454: * Consumes this message including the envelope.
455: * returns it as a {@link Source} object.
456: */
457: public abstract Source readEnvelopeAsSource();
458:
459: /**
460: * Returns the payload as a {@link Source} object.
461: *
462: * This consumes the message.
463: *
464: * @return
465: * if there's no payload, this method returns null.
466: */
467: public abstract Source readPayloadAsSource();
468:
469: /**
470: * Creates the equivalent {@link SOAPMessage} from this message.
471: *
472: * This consumes the message.
473: *
474: * @throws SOAPException
475: * if there's any error while creating a {@link SOAPMessage}.
476: */
477: public abstract SOAPMessage readAsSOAPMessage()
478: throws SOAPException;
479:
480: /**
481: * Creates the equivalent {@link SOAPMessage} from this message. It also uses
482: * transport specific headers from Packet during the SOAPMessage construction
483: * so that {@link SOAPMessage#getMimeHeaders()} gives meaningful transport
484: * headers.
485: *
486: * This consumes the message.
487: *
488: * @throws SOAPException
489: * if there's any error while creating a {@link SOAPMessage}.
490: */
491: public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound)
492: throws SOAPException {
493: return readAsSOAPMessage();
494: }
495:
496: /**
497: * Reads the payload as a JAXB object by using the given unmarshaller.
498: *
499: * This consumes the message.
500: *
501: * @throws JAXBException
502: * If JAXB reports an error during the processing.
503: */
504: public abstract <T> T readPayloadAsJAXB(Unmarshaller unmarshaller)
505: throws JAXBException;
506:
507: /**
508: * Reads the payload as a JAXB object according to the given {@link Bridge}.
509: *
510: * This consumes the message.
511: *
512: * @return null
513: * if there's no payload.
514: * @throws JAXBException
515: * If JAXB reports an error during the processing.
516: */
517: public abstract <T> T readPayloadAsJAXB(Bridge<T> bridge)
518: throws JAXBException;
519:
520: /**
521: * Reads the payload as a {@link XMLStreamReader}
522: *
523: * This consumes the message. The caller is encouraged to call
524: * {@link XMLStreamReaderFactory#recycle(XMLStreamReader)} when finished using
525: * the instance.
526: *
527: * @return
528: * If there's no payload, this method returns null.
529: * Otherwise always non-null valid {@link XMLStreamReader} that points to
530: * the payload tag name.
531: */
532: public abstract XMLStreamReader readPayload()
533: throws XMLStreamException;
534:
535: /**
536: * Marks the message as consumed, without actually reading the contents.
537: *
538: * <p>
539: * This method provides an opportunity for implementations to reuse
540: * any reusable resources needed for representing the payload.
541: */
542: public void consume() {
543: }
544:
545: /**
546: * Writes the payload to StAX.
547: *
548: * This method writes just the payload of the message to the writer.
549: * This consumes the message.
550: * The implementation will not write
551: * {@link XMLStreamWriter#writeStartDocument()}
552: * nor
553: * {@link XMLStreamWriter#writeEndDocument()}
554: *
555: * <p>
556: * If there's no payload, this method is no-op.
557: *
558: * @throws XMLStreamException
559: * If the {@link XMLStreamWriter} reports an error,
560: * or some other errors happen during the processing.
561: */
562: public abstract void writePayloadTo(XMLStreamWriter sw)
563: throws XMLStreamException;
564:
565: /**
566: * Writes the whole SOAP message (but not attachments)
567: * to the given writer.
568: *
569: * This consumes the message.
570: *
571: * @throws XMLStreamException
572: * If the {@link XMLStreamWriter} reports an error,
573: * or some other errors happen during the processing.
574: */
575: public abstract void writeTo(XMLStreamWriter sw)
576: throws XMLStreamException;
577:
578: /**
579: * Writes the whole SOAP envelope as SAX events.
580: *
581: * <p>
582: * This consumes the message.
583: *
584: * @param contentHandler
585: * must not be nulll.
586: * @param errorHandler
587: * must not be null.
588: * any error encountered during the SAX event production must be
589: * first reported to this error handler. Fatal errors can be then
590: * thrown as {@link SAXParseException}. {@link SAXException}s thrown
591: * from {@link ErrorHandler} should propagate directly through this method.
592: */
593: public abstract void writeTo(ContentHandler contentHandler,
594: ErrorHandler errorHandler) throws SAXException;
595:
596: // TODO: do we need a method that reads payload as a fault?
597: // do we want a separte streaming representation of fault?
598: // or would SOAPFault in SAAJ do?
599:
600: /**
601: * Creates a copy of a {@link Message}.
602: *
603: * <p>
604: * This method creates a new {@link Message} whose header/payload/attachments/properties
605: * are identical to this {@link Message}. Once created, the created {@link Message}
606: * and the original {@link Message} behaves independently --- adding header/
607: * attachment to one {@link Message} doesn't affect another {@link Message}
608: * at all.
609: *
610: * <p>
611: * This method does <b>NOT</b> consume a message.
612: *
613: * <p>
614: * To enable efficient copy operations, there's a few restrictions on
615: * how copied message can be used.
616: *
617: * <ol>
618: * <li>The original and the copy may not be
619: * used concurrently by two threads (this allows two {@link Message}s
620: * to share some internal resources, such as JAXB marshallers.)
621: * Note that it's OK for the original and the copy to be processed
622: * by two threads, as long as they are not concurrent.
623: *
624: * <li>The copy has the same 'life scope'
625: * as the original (this allows shallower copy, such as
626: * JAXB beans wrapped in {@link JAXBMessage}.)
627: * </ol>
628: *
629: * <p>
630: * A 'life scope' of a message created during a message processing
631: * in a pipeline is until a pipeline processes the next message.
632: * A message cannot be kept beyond its life scope.
633: *
634: * (This experimental design is to allow message objects to be reused
635: * --- feedback appreciated.)
636: *
637: *
638: *
639: * <h3>Design Rationale</h3>
640: * <p>
641: * Since a {@link Message} body is read-once, sometimes
642: * (such as when you do fail-over, or WS-RM) you need to
643: * create an idential copy of a {@link Message}.
644: *
645: * <p>
646: * The actual copy operation depends on the layout
647: * of the data in memory, hence it's best to be done by
648: * the {@link Message} implementation itself.
649: *
650: * <p>
651: * The restrictions placed on the use of copied {@link Message} can be
652: * relaxed if necessary, but it will make the copy method more expensive.
653: */
654: // TODO: update the class javadoc with 'lifescope'
655: // and move the discussion about life scope there.
656: public abstract Message copy();
657:
658: private String uuid;
659:
660: /**
661: * Retuns a unique id for the message. The id can be used for various things,
662: * like debug assistance, logging, and MIME encoding(say for boundary).
663: *
664: * <p>
665: * This method will check the existence of the addressing <MessageID> header,
666: * and if present uses that value. Otherwise it generates one from UUID.random(),
667: * and return it without adding a new header. But it doesn't add a <MessageID>
668: * to the header list since we expect them to be added before calling this
669: * method.
670: *
671: * <p>
672: * Addressing tube will go do a separate verification on inbound
673: * headers to make sure that <MessageID> header is present when it's
674: * supposed to be.
675: *
676: * @param binding object created by {@link BindingID#createBinding()}
677: *
678: * @return unique id for the message
679: */
680: public @NotNull
681: String getID(@NotNull
682: WSBinding binding) {
683: return getID(binding.getAddressingVersion(), binding
684: .getSOAPVersion());
685: }
686:
687: /**
688: * Retuns a unique id for the message.
689: * <p><p>
690: * @see {@link #getID(com.sun.xml.ws.api.WSBinding)} for detailed description.
691: * @param av WS-Addressing version
692: * @param sv SOAP version
693: * @return unique id for the message
694: */
695: public @NotNull
696: String getID(AddressingVersion av, SOAPVersion sv) {
697: if (uuid == null) {
698: if (av != null) {
699: uuid = getHeaders().getMessageID(av, sv);
700: }
701: if (uuid == null) {
702: uuid = "uuid:" + UUID.randomUUID().toString();
703: }
704: }
705: return uuid;
706: }
707: }
|