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 */ 25 26 package com.sun.xml.internal.ws.api.message; 27 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; 56 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; 75 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 * <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 <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 <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 { 198 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(); 206 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(); 221 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 } 232 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 } 240 241 protected AttachmentSet attachmentSet; 242 243 private WSDLBoundOperation operation = null; 244 245 private WSDLOperationMapping wsdlOperationMapping = null; 246 247 private MessageMetadata messageMetadata = null; 248 249 public void setMessageMedadata(MessageMetadata metadata) { 250 messageMetadata = metadata; 251 } 252 253 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 } 291 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 } 304 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 } 350 351 private Boolean isOneWay; 352 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 } 388 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; 416 417 isOneWay = value; 418 } 419 420 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(); 428 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 437 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(); 453 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; 468 469 String nsUri = getPayloadNamespaceURI(); 470 return nsUri.equals(SOAPVersion.SOAP_11.nsUri) || nsUri.equals(SOAPVersion.SOAP_12.nsUri); 471 } 472 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 } 493 494 /** 495 * Consumes this message including the envelope. 496 * returns it as a {@link Source} object. 497 */ 498 public abstract Source readEnvelopeAsSource(); 499 500 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(); 510 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; 520 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 } 535 536 public static Map<String, List<String>> getTransportHeaders(Packet packet) { 537 return getTransportHeaders(packet, packet.getState().isInbound()); 538 } 539 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 } 548 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; 567 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; 580 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; 592 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; 606 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() {} 619 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; 638 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; 650 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; 667 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? 671 672 673 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(); 731 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 } 756 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 } 777 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 } 785 786 public SOAPVersion getSOAPVersion() { 787 return null; 788 } 789 }