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