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

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

mercurial