| /* |
| * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| package com.sun.xml.internal.ws.api.message; |
| |
| import com.sun.istack.internal.NotNull; |
| import com.sun.istack.internal.Nullable; |
| import com.sun.xml.internal.bind.api.Bridge; |
| import com.sun.xml.internal.ws.api.BindingID; |
| import com.sun.xml.internal.ws.api.SOAPVersion; |
| import com.sun.xml.internal.ws.api.WSBinding; |
| import com.sun.xml.internal.ws.api.addressing.AddressingVersion; |
| import com.sun.xml.internal.ws.api.model.JavaMethod; |
| import com.sun.xml.internal.ws.api.model.SEIModel; |
| import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation; |
| import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundPortType; |
| import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; |
| import com.sun.xml.internal.ws.api.pipe.Codec; |
| import com.sun.xml.internal.ws.api.pipe.Pipe; |
| import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; |
| import com.sun.xml.internal.ws.client.dispatch.DispatchImpl; |
| import com.sun.xml.internal.ws.message.AttachmentSetImpl; |
| import com.sun.xml.internal.ws.message.jaxb.JAXBMessage; |
| import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx; |
| import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.namespace.QName; |
| import javax.xml.soap.SOAPException; |
| import javax.xml.soap.SOAPMessage; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.Source; |
| import javax.xml.ws.Dispatch; |
| import java.io.InputStream; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.UUID; |
| |
| /** |
| * Represents a SOAP message. |
| * |
| * |
| * <h2>What is a message?</h2> |
| * <p> |
| * A {@link Message} consists of the following: |
| * |
| * <ol> |
| * <li> |
| * Random-accessible list of headers. |
| * a header is a representation of an element inside |
| * <soap:Header>. |
| * It can be read multiple times, |
| * can be added or removed, but it is not modifiable. |
| * See {@link HeaderList} for more about headers. |
| * |
| * <li> |
| * The payload of the message, which is a representation |
| * of an element inside <soap:Body>. |
| * the payload is streamed, and therefore it can be |
| * only read once (or can be only written to something once.) |
| * once a payload is used, a message is said to be <b>consumed</b>. |
| * A message {@link #hasPayload() may not have any payload.} |
| * |
| * <li> |
| * Attachments. |
| * TODO: can attachments be streamed? I suspect so. |
| * does anyone need to read attachment twice? |
| * |
| * </ol> |
| * |
| * |
| * <h2>How does this abstraction work?</h2> |
| * <p> |
| * The basic idea behind the {@link Message} is to hide the actual |
| * data representation. For example, a {@link Message} might be |
| * constructed on top of an {@link InputStream} from the accepted HTTP connection, |
| * or it might be constructed on top of a JAXB object as a result |
| * of the method invocation through {@link Proxy}. There will be |
| * a {@link Message} implementation for each of those cases. |
| * |
| * <p> |
| * This interface provides a lot of methods that access the payload |
| * in many different forms, and implementations can implement those |
| * methods in the best possible way. |
| * |
| * <p> |
| * A particular attention is paid to make sure that a {@link Message} |
| * object can be constructed on a stream that is not fully read yet. |
| * We believe this improves the turn-around time on the server side. |
| * |
| * <p> |
| * It is often useful to wrap a {@link Message} into another {@link Message}, |
| * for example to encrypt the body, or to verify the signature as the body |
| * is read. |
| * |
| * <p> |
| * This representation is also used for a REST-ful XML message. |
| * In such case we'll construct a {@link Message} with empty |
| * attachments and headers, and when serializing all headers |
| * and attachments will be ignored. |
| * |
| * |
| * |
| * <h2>Message and XOP</h2> |
| * <p> |
| * XOP is considered as an {@link Codec}, and therefore when you are looking at |
| * {@link Message}, you'll never see <xop:Include> or any such elements |
| * (instead you'll see the base64 data inlined.) If a consumer of infoset isn't |
| * interested in handling XOP by himself, this allows him to work with XOP |
| * correctly even without noticing it. |
| * |
| * <p> |
| * For producers and consumers that are interested in accessing the binary data |
| * more efficiently, they can use {@link XMLStreamReaderEx} and |
| * {@link XMLStreamWriterEx}. |
| * |
| * |
| * |
| * <h2>Message lifespan</h2> |
| * <p> |
| * Often {@link Packet} include information local to a particular |
| * invocaion (such as {@code HttpServletRequest}, from this angle, it makes sense |
| * to tie a lifespan of a message to one pipeline invocation. |
| * <p> |
| * On the other hand, if you think about WS-RM, it often needs to hold on to |
| * a message longer than a pipeline invocation (you might get an HTTP request, |
| * get a message X, get a second HTTP request, get another message Y, and |
| * only then you might want to process X.) |
| * <p> |
| * TODO: what do we do about this? |
| * |
| * |
| * <pre> |
| * TODO: can body element have foreign attributes? maybe ID for security? |
| * Yes, when the SOAP body is signed there will be an ID attribute present |
| * But in this case any security based impl may need access |
| * to the concrete representation. |
| * TODO: HTTP headers? |
| * Yes. Abstracted as transport-based properties. |
| * TODO: who handles SOAP 1.1 and SOAP 1.2 difference? |
| * As separate channel implementations responsible for the creation of the |
| * message? |
| * TODO: session? |
| * TODO: Do we need to expose SOAPMessage explicitly? |
| * SOAPMessage could be the concrete representation but is it necessary to |
| * transform between different concrete representations? |
| * Perhaps this comes down to how use channels for creation and processing. |
| * TODO: Do we need to distinguish better between creation and processing? |
| * Do we really need the requirement that a created message can be resused |
| * for processing. Shall we bifurcate? |
| * |
| * TODO: SOAP version issue |
| * SOAP version is determined by the context, so message itself doesn't carry it around (?) |
| * |
| * TODO: wrapping message needs easier. in particular properties and attachments. |
| * </pre> |
| * |
| * @author Kohsuke Kawaguchi |
| */ |
| public abstract class Message { |
| |
| /** |
| * Returns true if headers are present in the message. |
| * |
| * @return |
| * true if headers are present. |
| */ |
| public abstract boolean hasHeaders(); |
| |
| /** |
| * Gets all the headers of this message. |
| * |
| * <h3>Implementation Note</h3> |
| * <p> |
| * {@link Message} implementation is allowed to defer |
| * the construction of {@link HeaderList} object. So |
| * if you only want to check for the existence of any header |
| * element, use {@link #hasHeaders()}. |
| * |
| * @return |
| * always return the same non-null object. |
| */ |
| public abstract @NotNull HeaderList getHeaders(); |
| |
| /** |
| * Gets the attachments of this message |
| * (attachments live outside a message.) |
| */ |
| public @NotNull AttachmentSet getAttachments() { |
| if (attachmentSet == null) { |
| attachmentSet = new AttachmentSetImpl(); |
| } |
| return attachmentSet; |
| } |
| |
| /** |
| * Optimization hint for the derived class to check |
| * if we may have some attachments. |
| */ |
| protected boolean hasAttachments() { |
| return attachmentSet!=null; |
| } |
| |
| protected AttachmentSet attachmentSet; |
| |
| private WSDLBoundOperation operation = null; |
| |
| /** |
| * Returns the operation of which this message is an instance of. |
| * |
| * <p> |
| * This method relies on {@link WSDLBoundPortType#getOperation(String, String)} but |
| * it does so in an efficient way. |
| * |
| * <p> |
| * This method works only for a request. A pipe can determine an operation for a request, |
| * and then keep it in a local variable to use it with a response, so there should be |
| * no need to find out operation from a response (besides, there might not be any response!). |
| * |
| * @param boundPortType |
| * This represents the port for which this message is used. |
| * Most {@link Pipe}s should get this information when they are created, |
| * since a pippeline always work against a particular type of {@link WSDLPort}. |
| * |
| * @return |
| * Null if the operation was not found. This is possible, for example when a protocol |
| * message is sent through a pipeline, or when we receive an invalid request on the server, |
| * or when we are on the client and the user appliation sends a random DOM through |
| * {@link Dispatch}, so this error needs to be handled gracefully. |
| */ |
| public final @Nullable WSDLBoundOperation getOperation(@NotNull WSDLBoundPortType boundPortType) { |
| if(operation==null) |
| operation = boundPortType.getOperation(getPayloadNamespaceURI(),getPayloadLocalPart()); |
| return operation; |
| } |
| |
| /** |
| * The same as {@link #getOperation(WSDLBoundPortType)} but |
| * takes {@link WSDLPort} for convenience. |
| */ |
| public final @Nullable WSDLBoundOperation getOperation(@NotNull WSDLPort port) { |
| return getOperation(port.getBinding()); |
| } |
| |
| /** |
| * Returns the java Method of which this message is an instance of. |
| * |
| * |
| * <p> |
| * This method works only for a request. A pipe can determine a {@link Method} |
| * for a request, and then keep it in a local variable to use it with a response, |
| * so there should be no need to find out operation from a response (besides, |
| * there might not be any response!). |
| * |
| * @param seiModel |
| * This represents the java model for the endpoint |
| * Some server {@link Pipe}s would get this information when they are created. |
| * |
| * @return |
| * Null if there is no corresponding Method for this message. This is |
| * possible, for example when a protocol message is sent through a |
| * pipeline, or when we receive an invalid request on the server, |
| * or when we are on the client and the user appliation sends a random |
| * DOM through {@link Dispatch}, so this error needs to be handled |
| * gracefully. |
| */ |
| public final @Nullable JavaMethod getMethod(@NotNull SEIModel seiModel) { |
| String localPart = getPayloadLocalPart(); |
| String nsUri; |
| if (localPart == null) { |
| localPart = ""; |
| nsUri = ""; |
| } else { |
| nsUri = getPayloadNamespaceURI(); |
| } |
| QName name = new QName(nsUri, localPart); |
| return seiModel.getJavaMethod(name); |
| } |
| |
| private Boolean isOneWay; |
| |
| /** |
| * Returns true if this message is a request message for a |
| * one way operation according to the given WSDL. False otherwise. |
| * |
| * <p> |
| * This method is functionally equivalent as doing |
| * {@code getOperation(port).getOperation().isOneWay()} |
| * (with proper null check and all.) But this method |
| * can sometimes work faster than that (for example, |
| * on the client side when used with SEI.) |
| * |
| * @param port |
| * {@link Message}s are always created under the context of |
| * one {@link WSDLPort} and they never go outside that context. |
| * Pass in that "governing" {@link WSDLPort} object here. |
| * We chose to receive this as a parameter instead of |
| * keeping {@link WSDLPort} in a message, just to save the storage. |
| * |
| * <p> |
| * The implementation of this method involves caching the return |
| * value, so the behavior is undefined if multiple callers provide |
| * different {@link WSDLPort} objects, which is a bug of the caller. |
| */ |
| public boolean isOneWay(@NotNull WSDLPort port) { |
| if(isOneWay==null) { |
| // we don't know, so compute. |
| WSDLBoundOperation op = getOperation(port); |
| if(op!=null) |
| isOneWay = op.getOperation().isOneWay(); |
| else |
| // the contract is to return true only when it's known to be one way. |
| isOneWay = false; |
| } |
| return isOneWay; |
| } |
| |
| /** |
| * Makes an assertion that this {@link Message} is |
| * a request message for an one-way operation according |
| * to the context WSDL. |
| * |
| * <p> |
| * This method is really only intended to be invoked from within |
| * the JAX-WS runtime, and not by any code building on top of it. |
| * |
| * <p> |
| * This method can be invoked only when the caller "knows" what |
| * WSDL says. Also, there's no point in invoking this method if the caller |
| * is doing {@code getOperation(port).getOperation().isOneWay()}, |
| * or sniffing the payload tag name. |
| * In particular, this includes {@link DispatchImpl}. |
| * |
| * <p> |
| * Once called, this allows {@link #isOneWay(WSDLPort)} method |
| * to return a value quickly. |
| * |
| * @see #isOneWay(WSDLPort) |
| */ |
| public final void assertOneWay(boolean value) { |
| // if two callers make different assertions, that's a bug. |
| // this is an assertion, not a runtime check because |
| // nobody outside JAX-WS should be using this. |
| assert isOneWay==null || isOneWay==value; |
| |
| isOneWay = value; |
| } |
| |
| |
| /** |
| * Gets the local name of the payload element. |
| * |
| * @return |
| * null if a {@link Message} doesn't have any payload. |
| */ |
| public abstract @Nullable String getPayloadLocalPart(); |
| |
| /** |
| * Gets the namespace URI of the payload element. |
| * |
| * @return |
| * null if a {@link Message} doesn't have any payload. |
| */ |
| public abstract String getPayloadNamespaceURI(); |
| // I'm not putting @Nullable on it because doing null check on getPayloadLocalPart() should be suffice |
| |
| /** |
| * Returns true if a {@link Message} has a payload. |
| * |
| * <p> |
| * A message without a payload is a SOAP message that looks like: |
| * <pre><xmp> |
| * <S:Envelope> |
| * <S:Header> |
| * ... |
| * </S:Header> |
| * <S:Body /> |
| * </S:Envelope> |
| * </xmp></pre> |
| */ |
| public abstract boolean hasPayload(); |
| |
| /** |
| * Returns true if this message is a fault. |
| * |
| * <p> |
| * Just a convenience method built on {@link #getPayloadNamespaceURI()} |
| * and {@link #getPayloadLocalPart()}. |
| */ |
| public boolean isFault() { |
| // TODO: is SOAP version a property of a Message? |
| // or is it defined by external factors? |
| // how do I compare? |
| String localPart = getPayloadLocalPart(); |
| if(localPart==null || !localPart.equals("Fault")) |
| return false; |
| |
| String nsUri = getPayloadNamespaceURI(); |
| return nsUri.equals(SOAPVersion.SOAP_11.nsUri) || nsUri.equals(SOAPVersion.SOAP_12.nsUri); |
| } |
| |
| /** |
| * Consumes this message including the envelope. |
| * returns it as a {@link Source} object. |
| */ |
| public abstract Source readEnvelopeAsSource(); |
| |
| |
| /** |
| * Returns the payload as a {@link Source} object. |
| * |
| * This consumes the message. |
| * |
| * @return |
| * if there's no payload, this method returns null. |
| */ |
| public abstract Source readPayloadAsSource(); |
| |
| /** |
| * Creates the equivalent {@link SOAPMessage} from this message. |
| * |
| * This consumes the message. |
| * |
| * @throws SOAPException |
| * if there's any error while creating a {@link SOAPMessage}. |
| */ |
| public abstract SOAPMessage readAsSOAPMessage() throws SOAPException ; |
| |
| /** |
| * Creates the equivalent {@link SOAPMessage} from this message. It also uses |
| * transport specific headers from Packet during the SOAPMessage construction |
| * so that {@link SOAPMessage#getMimeHeaders()} gives meaningful transport |
| * headers. |
| * |
| * This consumes the message. |
| * |
| * @throws SOAPException |
| * if there's any error while creating a {@link SOAPMessage}. |
| */ |
| public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException { |
| return readAsSOAPMessage(); |
| } |
| |
| /** |
| * Reads the payload as a JAXB object by using the given unmarshaller. |
| * |
| * This consumes the message. |
| * |
| * @throws JAXBException |
| * If JAXB reports an error during the processing. |
| */ |
| public abstract <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException; |
| |
| /** |
| * Reads the payload as a JAXB object according to the given {@link Bridge}. |
| * |
| * This consumes the message. |
| * |
| * @throws JAXBException |
| * If JAXB reports an error during the processing. |
| */ |
| public abstract <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException; |
| |
| /** |
| * Reads the payload as a {@link XMLStreamReader} |
| * |
| * This consumes the message. The caller is encouraged to call |
| * {@link XMLStreamReaderFactory#recycle(XMLStreamReader)} when finished using |
| * the instance. |
| * |
| * @return |
| * If there's no payload, this method returns null. |
| * Otherwise always non-null valid {@link XMLStreamReader} that points to |
| * the payload tag name. |
| */ |
| public abstract XMLStreamReader readPayload() throws XMLStreamException; |
| |
| /** |
| * Marks the message as consumed, without actually reading the contents. |
| * |
| * <p> |
| * This method provides an opportunity for implementations to reuse |
| * any reusable resources needed for representing the payload. |
| */ |
| public void consume() {} |
| |
| /** |
| * Writes the payload to StAX. |
| * |
| * This method writes just the payload of the message to the writer. |
| * This consumes the message. |
| * The implementation will not write |
| * {@link XMLStreamWriter#writeStartDocument()} |
| * nor |
| * {@link XMLStreamWriter#writeEndDocument()} |
| * |
| * <p> |
| * If there's no payload, this method is no-op. |
| * |
| * @throws XMLStreamException |
| * If the {@link XMLStreamWriter} reports an error, |
| * or some other errors happen during the processing. |
| */ |
| public abstract void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException; |
| |
| /** |
| * Writes the whole SOAP message (but not attachments) |
| * to the given writer. |
| * |
| * This consumes the message. |
| * |
| * @throws XMLStreamException |
| * If the {@link XMLStreamWriter} reports an error, |
| * or some other errors happen during the processing. |
| */ |
| public abstract void writeTo(XMLStreamWriter sw) throws XMLStreamException; |
| |
| /** |
| * Writes the whole SOAP envelope as SAX events. |
| * |
| * <p> |
| * This consumes the message. |
| * |
| * @param contentHandler |
| * must not be nulll. |
| * @param errorHandler |
| * must not be null. |
| * any error encountered during the SAX event production must be |
| * first reported to this error handler. Fatal errors can be then |
| * thrown as {@link SAXParseException}. {@link SAXException}s thrown |
| * from {@link ErrorHandler} should propagate directly through this method. |
| */ |
| public abstract void writeTo( ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException; |
| |
| // TODO: do we need a method that reads payload as a fault? |
| // do we want a separte streaming representation of fault? |
| // or would SOAPFault in SAAJ do? |
| |
| |
| |
| /** |
| * Creates a copy of a {@link Message}. |
| * |
| * <p> |
| * This method creates a new {@link Message} whose header/payload/attachments/properties |
| * are identical to this {@link Message}. Once created, the created {@link Message} |
| * and the original {@link Message} behaves independently --- adding header/ |
| * attachment to one {@link Message} doesn't affect another {@link Message} |
| * at all. |
| * |
| * <p> |
| * This method does <b>NOT</b> consume a message. |
| * |
| * <p> |
| * To enable efficient copy operations, there's a few restrictions on |
| * how copied message can be used. |
| * |
| * <ol> |
| * <li>The original and the copy may not be |
| * used concurrently by two threads (this allows two {@link Message}s |
| * to share some internal resources, such as JAXB marshallers.) |
| * Note that it's OK for the original and the copy to be processed |
| * by two threads, as long as they are not concurrent. |
| * |
| * <li>The copy has the same 'life scope' |
| * as the original (this allows shallower copy, such as |
| * JAXB beans wrapped in {@link JAXBMessage}.) |
| * </ol> |
| * |
| * <p> |
| * A 'life scope' of a message created during a message processing |
| * in a pipeline is until a pipeline processes the next message. |
| * A message cannot be kept beyond its life scope. |
| * |
| * (This experimental design is to allow message objects to be reused |
| * --- feedback appreciated.) |
| * |
| * |
| * |
| * <h3>Design Rationale</h3> |
| * <p> |
| * Since a {@link Message} body is read-once, sometimes |
| * (such as when you do fail-over, or WS-RM) you need to |
| * create an idential copy of a {@link Message}. |
| * |
| * <p> |
| * The actual copy operation depends on the layout |
| * of the data in memory, hence it's best to be done by |
| * the {@link Message} implementation itself. |
| * |
| * <p> |
| * The restrictions placed on the use of copied {@link Message} can be |
| * relaxed if necessary, but it will make the copy method more expensive. |
| */ |
| // TODO: update the class javadoc with 'lifescope' |
| // and move the discussion about life scope there. |
| public abstract Message copy(); |
| |
| private String uuid; |
| |
| /** |
| * Retuns a unique id for the message. The id can be used for various things, |
| * like debug assistance, logging, and MIME encoding(say for boundary). |
| * |
| * <p> |
| * This method will check the existence of the addressing <MessageID> header, |
| * and if present uses that value. Otherwise it generates one from UUID.random(), |
| * and return it without adding a new header. But it doesn't add a <MessageID> |
| * to the header list since we expect them to be added before calling this |
| * method. |
| * |
| * <p> |
| * Addressing tube will go do a separate verification on inbound |
| * headers to make sure that <MessageID> header is present when it's |
| * supposed to be. |
| * |
| * @param binding object created by {@link BindingID#createBinding()} |
| * |
| * @return unique id for the message |
| */ |
| public @NotNull String getID(@NotNull WSBinding binding) { |
| return getID(binding.getAddressingVersion(), binding.getSOAPVersion()); |
| } |
| |
| /** |
| * Retuns a unique id for the message. |
| * <p><p> |
| * @see {@link #getID(com.sun.xml.internal.ws.api.WSBinding)} for detailed description. |
| * @param av WS-Addressing version |
| * @param sv SOAP version |
| * @return unique id for the message |
| */ |
| public @NotNull String getID(AddressingVersion av, SOAPVersion sv) { |
| if (uuid == null) { |
| if (av != null) { |
| uuid = getHeaders().getMessageID(av, sv); |
| } |
| if (uuid == null) { |
| uuid = "uuid:" + UUID.randomUUID().toString(); |
| } |
| } |
| return uuid; |
| } |
| } |