src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/Message.java

Wed, 12 Jun 2013 14:47:09 +0100

author
mkos
date
Wed, 12 Jun 2013 14:47:09 +0100
changeset 384
8f2986ff0235
parent 368
0989ad8c0860
child 637
9c07ef4934dd
permissions
-rw-r--r--

8013021: Rebase 8005432 & 8003542 against the latest jdk8/jaxws
8003542: Improve processing of MTOM attachments
8005432: Update access to JAX-WS
Reviewed-by: mullan

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

mercurial