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.ws.addressing.WsaTubeHelper; 31 import com.sun.xml.internal.ws.api.SOAPVersion; 32 import com.sun.xml.internal.ws.api.WSBinding; 33 import com.sun.xml.internal.ws.api.addressing.AddressingVersion; 34 import com.sun.xml.internal.ws.api.addressing.OneWayFeature; 35 import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; 36 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation; 37 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; 38 import com.sun.xml.internal.ws.api.pipe.Codec; 39 import com.sun.xml.internal.ws.api.pipe.Pipe; 40 import com.sun.xml.internal.ws.message.RelatesToHeader; 41 import com.sun.xml.internal.ws.message.StringHeader; 42 import com.sun.xml.internal.ws.protocol.soap.ClientMUTube; 43 import com.sun.xml.internal.ws.protocol.soap.ServerMUTube; 44 import com.sun.xml.internal.ws.resources.AddressingMessages; 45 import com.sun.xml.internal.ws.resources.ClientMessages; 46 47 import javax.xml.namespace.QName; 48 import javax.xml.stream.XMLStreamException; 49 import javax.xml.ws.WebServiceException; 50 import javax.xml.ws.soap.SOAPBinding; 51 import java.util.ArrayList; 52 import java.util.BitSet; 53 import java.util.Iterator; 54 import java.util.NoSuchElementException; 55 56 /** 57 * A list of {@link Header}s on a {@link Message}. 58 * 59 * <p> 60 * This list can be modified to add headers 61 * from outside a {@link Message}, this is necessary 62 * since intermediate processing layers often need to 63 * put additional headers. 64 * 65 * <p> 66 * Following the SOAP convention, the order among headers 67 * are not significant. However, {@link Codec}s are 68 * expected to preserve the order of headers in the input 69 * message as much as possible. 70 * 71 * 72 * <a name="MU"></a> 73 * <h3>MustUnderstand Processing</h3> 74 * <p> 96 * as long as it's present, so this is the easiest and thus the preferred way. 97 * 98 * For example, if JAX-WSA looks for <wsa:To>, then it can set 99 * <tt>markAsUnderstand</tt> to true, to do the obtaining of a header 100 * and marking at the same time. 101 * 102 * <li>Call {@link #understood(int)}. 103 * If under a rare circumstance, a pipe cannot determine whether 104 * it can understand it or not when you are fetching a header, then 105 * you can use this method afterward to mark it as understood. 106 * </ol> 107 * 108 * <p> 109 * Intuitively speaking, at the end of the day, if a header is not 110 * understood but {@link Header#isIgnorable(SOAPVersion, java.util.Set)} is false, a bad thing 111 * will happen. The actual implementation of the checking is more complicated, 112 * for that see {@link ClientMUTube}/{@link ServerMUTube}. 113 * 114 * @see Message#getHeaders() 115 */ 116 public class HeaderList extends ArrayList<Header> { 117 118 private static final long serialVersionUID = -6358045781349627237L; 119 /** 120 * Bit set to keep track of which headers are understood. 121 * <p> 122 * The first 32 headers use this field, and the rest will use 123 * {@link #moreUnderstoodBits}. The expectation is that 124 * most of the time a SOAP message will only have up to 32 headers, 125 * so we can avoid allocating separate objects for {@link BitSet}. 126 */ 127 private int understoodBits; 128 /** 129 * If there are more than 32 headers, we use this {@link BitSet} 130 * to keep track of whether those headers are understood. 131 * Lazily allocated. 132 */ 133 private BitSet moreUnderstoodBits = null; 134 135 /** 136 * Creates an empty {@link HeaderList}. 137 */ 138 public HeaderList() { 139 } 140 141 /** 142 * Copy constructor. 143 */ 144 public HeaderList(HeaderList that) { 145 super(that); 146 this.understoodBits = that.understoodBits; 147 if (that.moreUnderstoodBits != null) { 148 this.moreUnderstoodBits = (BitSet) that.moreUnderstoodBits.clone(); 149 } 150 } 151 152 /** 153 * The total number of headers. 154 */ 155 @Override 156 public int size() { 157 return super.size(); 158 } 159 160 /** 161 * Adds all the headers. 162 */ 163 public void addAll(Header... headers) { 164 for (Header header : headers) { 165 add(header); 166 } 167 } 168 169 /** 170 * Gets the {@link Header} at the specified index. 171 * 172 * <p> 173 * This method does not mark the returned {@link Header} as understood. 174 * 175 * @see #understood(int) 176 */ 177 @Override 178 public Header get(int index) { 179 return super.get(index); 180 } 181 182 /** 183 * Marks the {@link Header} at the specified index as 184 * <a href="#MU">"understood"</a>. 185 */ 186 public void understood(int index) { 216 return false; 217 } 218 return moreUnderstoodBits.get(index - 32); 219 } 220 } 221 222 /** 223 * Marks the specified {@link Header} as <a href="#MU">"understood"</a>. 224 * 225 * @deprecated 226 * By the definition of {@link ArrayList}, this operation requires 227 * O(n) search of the array, and thus inherently inefficient. 228 * 229 * Because of this, if you are developing a {@link Pipe} for 230 * a performance sensitive environment, do not use this method. 231 * 232 * @throws IllegalArgumentException 233 * if the given header is not {@link #contains(Object) contained} 234 * in this header. 235 */ 236 public void understood(@NotNull Header header) { 237 int sz = size(); 238 for (int i = 0; i < sz; i++) { 239 if (get(i) == header) { 240 understood(i); 241 return; 242 } 243 } 244 throw new IllegalArgumentException(); 245 } 246 247 /** 248 * Gets the first {@link Header} of the specified name. 249 * 250 * @param markAsUnderstood 251 * If this parameter is true, the returned header will 252 * be marked as <a href="#MU">"understood"</a>. 253 * @return null if not found. 254 */ 255 public 256 @Nullable 257 Header get(@NotNull String nsUri, @NotNull String localName, boolean markAsUnderstood) { 258 int len = size(); 259 for (int i = 0; i < len; i++) { 260 Header h = get(i); 261 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 262 if (markAsUnderstood) { 263 understood(i); 264 } 265 return h; 266 } 267 } 268 return null; 269 } 270 271 /** 272 * @deprecated 273 * Use {@link #get(String, String, boolean)} 274 */ 275 public Header get(String nsUri, String localName) { 276 return get(nsUri, localName, true); 277 } 278 279 /** 280 * Gets the first {@link Header} of the specified name. 281 * 282 * @param markAsUnderstood 283 * If this parameter is true, the returned header will 284 * be marked as <a href="#MU">"understood"</a>. 285 * @return null 286 * if not found. 287 */ 288 public 289 @Nullable 290 Header get(@NotNull QName name, boolean markAsUnderstood) { 291 return get(name.getNamespaceURI(), name.getLocalPart(), markAsUnderstood); 292 } 293 294 /** 295 * @deprecated 296 * Use {@link #get(QName)} 297 */ 298 public 299 @Nullable 300 Header get(@NotNull QName name) { 301 return get(name, true); 302 } 303 304 /** 305 * @deprecated 306 * Use {@link #getHeaders(String, String, boolean)} 307 */ 308 public Iterator<Header> getHeaders(final String nsUri, final String localName) { 309 return getHeaders(nsUri, localName, true); 310 } 311 312 /** 313 * Gets all the {@link Header}s of the specified name, 314 * including duplicates (if any.) 315 * 316 * @param markAsUnderstood 317 * If this parameter is true, the returned headers will 318 * be marked as <a href="#MU">"understood"</a> when they are returned 319 * from {@link Iterator#next()}. 320 * @return empty iterator if not found. 321 */ 322 public 323 @NotNull 324 Iterator<Header> getHeaders(@NotNull final String nsUri, @NotNull final String localName, final boolean markAsUnderstood) { 325 return new Iterator<Header>() { 326 327 int idx = 0; 328 Header next; 329 330 public boolean hasNext() { 331 if (next == null) { 332 fetch(); 333 } 334 return next != null; 335 } 336 337 public Header next() { 338 if (next == null) { 339 fetch(); 340 if (next == null) { 341 throw new NoSuchElementException(); 342 } 343 } 344 345 if (markAsUnderstood) { 346 assert get(idx - 1) == next; 347 understood(idx - 1); 348 } 349 350 Header r = next; 351 next = null; 352 return r; 353 } 354 355 private void fetch() { 356 while (idx < size()) { 357 Header h = get(idx++); 358 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 359 next = h; 360 break; 361 } 362 } 363 } 364 365 public void remove() { 366 throw new UnsupportedOperationException(); 367 } 368 }; 369 } 370 371 /** 372 * @see #getHeaders(String, String, boolean) 373 */ 374 public 375 @NotNull 376 Iterator<Header> getHeaders(@NotNull QName headerName, final boolean markAsUnderstood) { 377 return getHeaders(headerName.getNamespaceURI(), headerName.getLocalPart(), markAsUnderstood); 378 } 379 380 /** 381 * @deprecated 382 * use {@link #getHeaders(String, boolean)}. 383 */ 384 public 385 @NotNull 386 Iterator<Header> getHeaders(@NotNull final String nsUri) { 387 return getHeaders(nsUri, true); 388 } 389 390 /** 391 * Gets an iteration of headers {@link Header} in the specified namespace, 392 * including duplicates (if any.) 393 * 394 * @param markAsUnderstood 395 * If this parameter is true, the returned headers will 396 * be marked as <a href="#MU">"understood"</a> when they are returned 397 * from {@link Iterator#next()}. 398 * @return 399 * empty iterator if not found. 400 */ 401 public 402 @NotNull 403 Iterator<Header> getHeaders(@NotNull final String nsUri, final boolean markAsUnderstood) { 404 return new Iterator<Header>() { 405 406 int idx = 0; 407 Header next; 408 409 public boolean hasNext() { 410 if (next == null) { 411 fetch(); 412 } 413 return next != null; 414 } 415 416 public Header next() { 417 if (next == null) { 418 fetch(); 419 if (next == null) { 420 throw new NoSuchElementException(); 421 } 422 } 423 424 if (markAsUnderstood) { 425 assert get(idx - 1) == next; 426 understood(idx - 1); 427 } 428 429 Header r = next; 430 next = null; 431 return r; 432 } 433 434 private void fetch() { 435 while (idx < size()) { 436 Header h = get(idx++); 437 if (h.getNamespaceURI().equals(nsUri)) { 438 next = h; 439 break; 440 } 441 } 442 } 443 444 public void remove() { 445 throw new UnsupportedOperationException(); 446 } 447 }; 448 } 449 450 /** 451 * Gets the first {@link Header} of the specified name targeted at the 452 * current implicit role. 453 * 454 * @param name name of the header 455 * @param markUnderstood 456 * If this parameter is true, the returned headers will 457 * be marked as <a href="#MU">"understood"</a> when they are returned 458 * from {@link Iterator#next()}. 459 * @return null if header not found 460 */ 461 private Header getFirstHeader(QName name, boolean markUnderstood, SOAPVersion sv) { 462 if (sv == null) { 463 throw new IllegalArgumentException(AddressingMessages.NULL_SOAP_VERSION()); 464 } 465 466 Iterator<Header> iter = getHeaders(name.getNamespaceURI(), name.getLocalPart(), markUnderstood); 467 while (iter.hasNext()) { 468 Header h = iter.next(); 469 if (h.getRole(sv).equals(sv.implicitRole)) { 470 return h; 471 } 472 } 473 474 return null; 475 } 476 477 /** 478 * Returns the value of WS-Addressing <code>To</code> header. The <code>version</code> 479 * identifies the WS-Addressing version and the header returned is targeted at 480 * the current implicit role. Caches the value for subsequent invocation. 481 * Duplicate <code>To</code> headers are detected earlier. 482 * 483 * @param av WS-Addressing version 484 * @param sv SOAP version 485 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 486 * @return Value of WS-Addressing To header, anonymous URI if no header is present 487 */ 488 public String getTo(AddressingVersion av, SOAPVersion sv) { 489 if (av == null) { 490 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 491 } 492 493 Header h = getFirstHeader(av.toTag, true, sv); 494 String to; 495 if (h != null) { 496 to = h.getStringContent(); 497 } else { 498 to = av.anonymousUri; 499 } 500 501 return to; 502 } 503 504 /** 505 * Returns the value of WS-Addressing <code>Action</code> header. The <code>version</code> 506 * identifies the WS-Addressing version and the header returned is targeted at 507 * the current implicit role. Caches the value for subsequent invocation. 508 * Duplicate <code>Action</code> headers are detected earlier. 509 * 510 * @param av WS-Addressing version 511 * @param sv SOAP version 512 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 513 * @return Value of WS-Addressing Action header, null if no header is present 514 */ 515 public String getAction(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 516 if (av == null) { 517 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 518 } 519 520 String action = null; 521 Header h = getFirstHeader(av.actionTag, true, sv); 522 if (h != null) { 523 action = h.getStringContent(); 524 } 525 526 return action; 527 } 528 529 /** 530 * Returns the value of WS-Addressing <code>ReplyTo</code> header. The <code>version</code> 531 * identifies the WS-Addressing version and the header returned is targeted at 532 * the current implicit role. Caches the value for subsequent invocation. 533 * Duplicate <code>ReplyTo</code> headers are detected earlier. 534 * 535 * @param av WS-Addressing version 536 * @param sv SOAP version 537 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 538 * @return Value of WS-Addressing ReplyTo header, null if no header is present 539 */ 540 public WSEndpointReference getReplyTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 541 if (av == null) { 542 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 543 } 544 545 Header h = getFirstHeader(av.replyToTag, true, sv); 546 WSEndpointReference replyTo; 547 if (h != null) { 548 try { 549 replyTo = h.readAsEPR(av); 550 } catch (XMLStreamException e) { 551 throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e); 552 } 553 } else { 554 replyTo = av.anonymousEpr; 555 } 556 557 return replyTo; 558 } 559 560 /** 561 * Returns the value of WS-Addressing <code>FaultTo</code> header. The <code>version</code> 562 * identifies the WS-Addressing version and the header returned is targeted at 563 * the current implicit role. Caches the value for subsequent invocation. 564 * Duplicate <code>FaultTo</code> headers are detected earlier. 565 * 566 * @param av WS-Addressing version 567 * @param sv SOAP version 568 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 569 * @return Value of WS-Addressing FaultTo header, null if no header is present 570 */ 571 public WSEndpointReference getFaultTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 572 if (av == null) { 573 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 574 } 575 576 Header h = getFirstHeader(av.faultToTag, true, sv); 577 WSEndpointReference faultTo = null; 578 if (h != null) { 579 try { 580 faultTo = h.readAsEPR(av); 581 } catch (XMLStreamException e) { 582 throw new WebServiceException(AddressingMessages.FAULT_TO_CANNOT_PARSE(), e); 583 } 584 } 585 586 return faultTo; 587 } 588 589 /** 590 * Returns the value of WS-Addressing <code>MessageID</code> header. The <code>version</code> 591 * identifies the WS-Addressing version and the header returned is targeted at 592 * the current implicit role. Caches the value for subsequent invocation. 593 * Duplicate <code>MessageID</code> headers are detected earlier. 594 * 595 * @param av WS-Addressing version 596 * @param sv SOAP version 597 * @throws WebServiceException if either <code>av</code> or <code>sv</code> is null. 598 * @return Value of WS-Addressing MessageID header, null if no header is present 599 */ 600 public String getMessageID(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 601 if (av == null) { 602 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 603 } 604 605 Header h = getFirstHeader(av.messageIDTag, true, sv); 606 String messageId = null; 607 if (h != null) { 608 messageId = h.getStringContent(); 609 } 610 611 return messageId; 612 } 613 614 /** 615 * Returns the value of WS-Addressing <code>RelatesTo</code> header. The <code>version</code> 616 * identifies the WS-Addressing version and the header returned is targeted at 617 * the current implicit role. Caches the value for subsequent invocation. 618 * Duplicate <code>RelatesTo</code> headers are detected earlier. 619 * 620 * @param av WS-Addressing version 621 * @param sv SOAP version 622 * @throws WebServiceException if either <code>av</code> or <code>sv</code> is null. 623 * @return Value of WS-Addressing RelatesTo header, null if no header is present 624 */ 625 public String getRelatesTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 626 if (av == null) { 627 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 628 } 629 630 Header h = getFirstHeader(av.relatesToTag, true, sv); 631 String relatesTo = null; 632 if (h != null) { 633 relatesTo = h.getStringContent(); 634 } 635 636 return relatesTo; 637 } 638 639 /** 640 * Creates a set of outbound WS-Addressing headers on the client with the 641 * specified Action Message Addressing Property value. 642 * <p><p> 643 * This method needs to be invoked right after such a Message is 644 * created which is error prone but so far only MEX, RM and JAX-WS 645 * creates a request so this ugliness is acceptable. This method is also used 646 * to create protocol messages that are not associated with any {@link WSBinding} 647 * and {@link WSDLPort}. 648 * 649 * @param packet request packet 650 * @param av WS-Addressing version 651 * @param sv SOAP version 652 * @param oneway Indicates if the message exchange pattern is oneway 653 * @param action Action Message Addressing Property value 654 * @param mustUnderstand to indicate if the addressing headers are set with mustUnderstand attribute 655 */ 656 public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action, boolean mustUnderstand) { 657 fillCommonAddressingHeaders(packet, av, sv, action, mustUnderstand); 658 659 // wsa:ReplyTo 660 // null or "true" is equivalent to request/response MEP 661 if (!oneway) { 662 WSEndpointReference epr = av.anonymousEpr; 663 if (get(av.replyToTag, false) == null) { 664 add(epr.createHeader(av.replyToTag)); 665 } 666 667 // wsa:FaultTo 668 if (get(av.faultToTag, false) == null) { 669 add(epr.createHeader(av.faultToTag)); 670 } 671 672 // wsa:MessageID 673 if (packet.getMessage().getHeaders().get(av.messageIDTag, false) == null) { 674 if (get(av.messageIDTag, false) == null) { 675 Header h = new StringHeader(av.messageIDTag, Message.generateMessageID()); 676 add(h); 677 } 678 } 679 } 680 } 681 682 public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action) { 683 fillRequestAddressingHeaders(packet, av, sv, oneway, action, false); 684 } 685 686 /** 687 * Creates a set of outbound WS-Addressing headers on the client with the 688 * default Action Message Addressing Property value. 689 * <p><p> 690 * This method needs to be invoked right after such a Message is 691 * created which is error prone but so far only MEX, RM and JAX-WS 692 * creates a request so this ugliness is acceptable. If more components 693 * are identified using this, then we may revisit this. 694 * <p><p> 695 * This method is used if default Action Message Addressing Property is to 696 * be used. See 697 * {@link #fillRequestAddressingHeaders(Packet, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, boolean, String)} 698 * if non-default Action is to be used, for example when creating a protocol message not 699 * associated with {@link WSBinding} and {@link WSDLPort}. 700 * This method uses SOAPAction as the Action unless set expplicitly in the wsdl. 701 * @param wsdlPort request WSDL port 702 * @param binding request WSBinding 703 * @param packet request packet 704 */ 705 public void fillRequestAddressingHeaders(WSDLPort wsdlPort, @NotNull WSBinding binding, Packet packet) { 706 if (binding == null) { 707 throw new IllegalArgumentException(AddressingMessages.NULL_BINDING()); 708 } 709 710 if (binding.isFeatureEnabled(SuppressAutomaticWSARequestHeadersFeature.class)) 711 return; 712 713 //See if WSA headers are already set by the user. 714 HeaderList hl = packet.getMessage().getHeaders(); 715 String action = hl.getAction(binding.getAddressingVersion(), binding.getSOAPVersion()); 716 if (action != null) { 717 //assume that all the WSA headers are set by the user 718 return; 719 } 720 AddressingVersion addressingVersion = binding.getAddressingVersion(); 721 //seiModel is passed as null as it is not needed. 722 WsaTubeHelper wsaHelper = addressingVersion.getWsaHelper(wsdlPort, null, binding); 723 724 // wsa:Action 725 String effectiveInputAction = wsaHelper.getEffectiveInputAction(packet); 726 if (effectiveInputAction == null || effectiveInputAction.equals("") && binding.getSOAPVersion() == SOAPVersion.SOAP_11) { 727 throw new WebServiceException(ClientMessages.INVALID_SOAP_ACTION()); 728 } 729 boolean oneway = !packet.expectReply; 730 if (wsdlPort != null) { 731 // if WSDL has <wsaw:Anonymous>prohibited</wsaw:Anonymous>, then throw an error 732 // as anonymous ReplyTo MUST NOT be added in that case. BindingProvider need to 733 // disable AddressingFeature and MemberSubmissionAddressingFeature and hand-craft 734 // the SOAP message with non-anonymous ReplyTo/FaultTo. 735 if (!oneway && packet.getMessage() != null && packet.getWSDLOperation() != null) { 736 WSDLBoundOperation wbo = wsdlPort.getBinding().get(packet.getWSDLOperation()); 737 if (wbo != null && wbo.getAnonymous() == WSDLBoundOperation.ANONYMOUS.prohibited) { 738 throw new WebServiceException(AddressingMessages.WSAW_ANONYMOUS_PROHIBITED()); 739 } 740 } 741 } 742 if (!binding.isFeatureEnabled(OneWayFeature.class)) { 743 // standard oneway 744 fillRequestAddressingHeaders(packet, addressingVersion, binding.getSOAPVersion(), oneway, effectiveInputAction, addressingVersion.isRequired(binding)); 745 } else { 746 // custom oneway 747 fillRequestAddressingHeaders(packet, addressingVersion, binding.getSOAPVersion(), binding.getFeature(OneWayFeature.class), oneway, effectiveInputAction); 748 } 749 } 750 751 private void fillRequestAddressingHeaders(@NotNull Packet packet, @NotNull AddressingVersion av, @NotNull SOAPVersion sv, @NotNull OneWayFeature of, boolean oneway, @NotNull String action) { 752 if (!oneway&&!of.isUseAsyncWithSyncInvoke() && Boolean.TRUE.equals(packet.isSynchronousMEP)) 753 fillRequestAddressingHeaders(packet, av, sv, oneway, action); 754 else { 755 fillCommonAddressingHeaders(packet, av, sv, action, false); 756 757 // wsa:ReplyTo 758 // wsa:ReplyTo (add it if it doesn't already exist and OnewayFeature 759 // requests a specific ReplyTo) 760 if (get(av.replyToTag, false) == null) { 761 WSEndpointReference replyToEpr = of.getReplyTo(); 762 if (replyToEpr != null) { 763 add(replyToEpr.createHeader(av.replyToTag)); 764 // add wsa:MessageID only for non-null ReplyTo 765 if (packet.getMessage().getHeaders().get(av.messageIDTag, false) == null) { 766 // if header doesn't exist, method getID creates a new random id 767 String newID = Message.generateMessageID(); 768 add(new StringHeader(av.messageIDTag, newID)); 769 } 770 } 771 } 772 773 // wsa:FaultTo 774 // wsa:FaultTo (add it if it doesn't already exist and OnewayFeature 775 // requests a specific FaultTo) 776 if (get(av.faultToTag, false) == null) { 777 WSEndpointReference faultToEpr = of.getFaultTo(); 778 if (faultToEpr != null) { 779 add(faultToEpr.createHeader(av.faultToTag)); 780 // add wsa:MessageID only for non-null FaultTo 781 if (get(av.messageIDTag, false) == null) { 782 add(new StringHeader(av.messageIDTag, Message.generateMessageID())); 783 } 784 } 785 } 786 787 // wsa:From 788 if (of.getFrom() != null) { 789 addOrReplace(of.getFrom().createHeader(av.fromTag)); 790 } 791 792 // wsa:RelatesTo 793 if (of.getRelatesToID() != null) { 794 addOrReplace(new RelatesToHeader(av.relatesToTag, of.getRelatesToID())); 795 } 796 } 797 } 798 799 /** 800 * Creates wsa:To, wsa:Action and wsa:MessageID header on the client 801 * 802 * @param packet request packet 803 * @param av WS-Addressing version 804 * @param sv SOAP version 805 * @param action Action Message Addressing Property value 806 * @throws IllegalArgumentException if any of the parameters is null. 807 */ 808 private void fillCommonAddressingHeaders(Packet packet, @NotNull AddressingVersion av, @NotNull SOAPVersion sv, @NotNull String action, boolean mustUnderstand) { 809 if (packet == null) { 810 throw new IllegalArgumentException(AddressingMessages.NULL_PACKET()); 811 } 812 813 if (av == null) { 814 throw new IllegalArgumentException(AddressingMessages.NULL_ADDRESSING_VERSION()); 815 } 816 817 if (sv == null) { 818 throw new IllegalArgumentException(AddressingMessages.NULL_SOAP_VERSION()); 819 } 820 821 if (action == null && !sv.httpBindingId.equals(SOAPBinding.SOAP12HTTP_BINDING)) { 822 throw new IllegalArgumentException(AddressingMessages.NULL_ACTION()); 823 } 824 825 // wsa:To 826 if (get(av.toTag, false) == null) { 827 StringHeader h = new StringHeader(av.toTag, packet.endpointAddress.toString()); 828 add(h); 829 } 830 831 // wsa:Action 832 if (action != null) { 833 packet.soapAction = action; 834 if (get(av.actionTag, false) == null) { 835 //As per WS-I BP 1.2/2.0, if one of the WSA headers is MU, then all WSA headers should be treated as MU., 836 // so just set MU on action header 837 StringHeader h = new StringHeader(av.actionTag, action, sv, mustUnderstand); 838 add(h); 839 } 840 } 841 } 842 843 /** 844 * Adds a new {@link Header}. 845 * 846 * <p> 847 * Order doesn't matter in headers, so this method 848 * does not make any guarantee as to where the new header 849 * is inserted. 850 * 851 * @return 852 * always true. Don't use the return value. 853 */ 854 @Override 855 public boolean add(Header header) { 856 return super.add(header); 857 } 858 859 /** 860 * Removes the first {@link Header} of the specified name. 861 * @param nsUri namespace URI of the header to remove 862 * @param localName local part of the FQN of the header to remove 863 * 864 * @return null if not found. 865 */ 866 public 867 @Nullable 868 Header remove(@NotNull String nsUri, @NotNull String localName) { 869 int len = size(); 870 for (int i = 0; i < len; i++) { 871 Header h = get(i); 872 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 873 return remove(i); 874 } 875 } 876 return null; 877 } 878 879 /** 880 * Replaces an existing {@link Header} or adds a new {@link Header}. 881 * 882 * <p> 883 * Order doesn't matter in headers, so this method 884 * does not make any guarantee as to where the new header 885 * is inserted. 886 * 887 * @return 888 * always true. Don't use the return value. 889 */ 890 public boolean addOrReplace(Header header) { 891 for (int i=0; i < size(); i++) { 892 Header hdr = get(i); 893 if (hdr.getNamespaceURI().equals(header.getNamespaceURI()) && 894 hdr.getLocalPart().equals(header.getLocalPart())) { 895 // Put the new header in the old position. Call super versions 896 // internally to avoid UnsupportedOperationException 897 removeInternal(i); 898 addInternal(i, header); 899 return true; 900 } 901 } 902 return add(header); 903 } 904 905 protected void addInternal(int index, Header header) { 906 super.add(index, header); 907 } 908 909 protected Header removeInternal(int index) { 910 return super.remove(index); 911 } 912 913 /** 914 * Removes the first {@link Header} of the specified name. 915 * 916 * @param name fully qualified name of the header to remove 917 * 918 * @return null if not found. 919 */ 920 public 921 @Nullable 922 Header remove(@NotNull QName name) { 923 return remove(name.getNamespaceURI(), name.getLocalPart()); 924 } 925 926 /** 927 * Removes the first {@link Header} of the specified name. 928 * 929 * @param index index of the header to remove 930 * 931 * @return removed header 932 */ 933 @Override 934 public Header remove(int index) { 935 removeUnderstoodBit(index); 936 return super.remove(index); 937 } 938 939 /** 940 * Removes the "understood" bit for header on the position specified by {@code index} parameter 941 * from the set of understood header bits. 993 moreUnderstoodBits = null; 994 } 995 } 996 997 /** 998 * Removes a single instance of the specified element from this 999 * header list, if it is present. More formally, 1000 * removes a header <tt>h</tt> such that <tt>(o==null ? h==null : 1001 * o.equals(h))</tt>, if the header list contains one or more such 1002 * headers. Returns <tt>true</tt> if the list contained the 1003 * specified element (or equivalently, if the list changed as a 1004 * result of the call).<p> 1005 * 1006 * @param o element to be removed from this list, if present. 1007 * @return <tt>true</tt> if the list contained the specified element. 1008 * @see #remove(javax.xml.namespace.QName) 1009 */ 1010 @Override 1011 public boolean remove(Object o) { 1012 if (o != null) { 1013 for (int index = 0; index < this.size(); index++) 1014 if (o.equals(this.get(index))) { 1015 remove(index); 1016 return true; 1017 } 1018 } 1019 1020 return false; 1021 } 1022 1023 /** 1024 * Creates a copy. 1025 * 1026 * This handles null {@link HeaderList} correctly. 1027 * 1028 * @param original 1029 * Can be null, in which case null will be returned. 1030 */ 1031 public static HeaderList copy(HeaderList original) { 1032 if (original == null) { 1033 return null; 1034 } else { 1035 return new HeaderList(original); 1036 } 1037 } 1038 1039 public void readResponseAddressingHeaders(WSDLPort wsdlPort, WSBinding binding) { 1040 // read Action 1041 String wsaAction = getAction(binding.getAddressingVersion(), binding.getSOAPVersion()); 1042 // TODO: validate client-inbound Action 1043 } 1044 } | 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 java.util.ArrayList; 29 import java.util.BitSet; 30 import java.util.HashSet; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.NoSuchElementException; 34 import java.util.Set; 35 36 import javax.xml.namespace.QName; 37 import javax.xml.ws.WebServiceException; 38 39 import com.sun.istack.internal.NotNull; 40 import com.sun.istack.internal.Nullable; 41 import com.sun.xml.internal.ws.api.SOAPVersion; 42 import com.sun.xml.internal.ws.api.WSBinding; 43 import com.sun.xml.internal.ws.api.addressing.AddressingVersion; 44 import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; 45 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; 46 import com.sun.xml.internal.ws.api.pipe.Codec; 47 import com.sun.xml.internal.ws.api.pipe.Pipe; 48 import com.sun.xml.internal.ws.binding.SOAPBindingImpl; 49 import com.sun.xml.internal.ws.protocol.soap.ClientMUTube; 50 import com.sun.xml.internal.ws.protocol.soap.ServerMUTube; 51 import java.util.Arrays; 52 53 /** 54 * A list of {@link Header}s on a {@link Message}. 55 * 56 * <p> 57 * This list can be modified to add headers 58 * from outside a {@link Message}, this is necessary 59 * since intermediate processing layers often need to 60 * put additional headers. 61 * 62 * <p> 63 * Following the SOAP convention, the order among headers 64 * are not significant. However, {@link Codec}s are 65 * expected to preserve the order of headers in the input 66 * message as much as possible. 67 * 68 * 69 * <a name="MU"></a> 70 * <h3>MustUnderstand Processing</h3> 71 * <p> 93 * as long as it's present, so this is the easiest and thus the preferred way. 94 * 95 * For example, if JAX-WSA looks for <wsa:To>, then it can set 96 * <tt>markAsUnderstand</tt> to true, to do the obtaining of a header 97 * and marking at the same time. 98 * 99 * <li>Call {@link #understood(int)}. 100 * If under a rare circumstance, a pipe cannot determine whether 101 * it can understand it or not when you are fetching a header, then 102 * you can use this method afterward to mark it as understood. 103 * </ol> 104 * 105 * <p> 106 * Intuitively speaking, at the end of the day, if a header is not 107 * understood but {@link Header#isIgnorable(SOAPVersion, java.util.Set)} is false, a bad thing 108 * will happen. The actual implementation of the checking is more complicated, 109 * for that see {@link ClientMUTube}/{@link ServerMUTube}. 110 * 111 * @see Message#getHeaders() 112 */ 113 public class HeaderList extends ArrayList<Header> implements MessageHeaders { 114 115 private static final long serialVersionUID = -6358045781349627237L; 116 /** 117 * Bit set to keep track of which headers are understood. 118 * <p> 119 * The first 32 headers use this field, and the rest will use 120 * {@link #moreUnderstoodBits}. The expectation is that 121 * most of the time a SOAP message will only have up to 32 headers, 122 * so we can avoid allocating separate objects for {@link BitSet}. 123 */ 124 private int understoodBits; 125 /** 126 * If there are more than 32 headers, we use this {@link BitSet} 127 * to keep track of whether those headers are understood. 128 * Lazily allocated. 129 */ 130 private BitSet moreUnderstoodBits = null; 131 132 private SOAPVersion soapVersion; 133 134 /** 135 * This method is deprecated - instead use this one: 136 * public HeaderList(SOAPVersion) 137 * Creates an empty {@link HeaderList}. 138 */ 139 @Deprecated 140 public HeaderList() { 141 } 142 143 /** 144 * Creates an empty {@link HeaderList} with the given soap version 145 * @param soapVersion 146 */ 147 public HeaderList(SOAPVersion soapVersion) { 148 this.soapVersion = soapVersion; 149 } 150 151 /** 152 * Copy constructor. 153 */ 154 public HeaderList(HeaderList that) { 155 super(that); 156 this.understoodBits = that.understoodBits; 157 if (that.moreUnderstoodBits != null) { 158 this.moreUnderstoodBits = (BitSet) that.moreUnderstoodBits.clone(); 159 } 160 } 161 162 public HeaderList(MessageHeaders that) { 163 super(that.asList()); 164 if (that instanceof HeaderList) { 165 HeaderList hThat = (HeaderList) that; 166 this.understoodBits = hThat.understoodBits; 167 if (hThat.moreUnderstoodBits != null) { 168 this.moreUnderstoodBits = (BitSet) hThat.moreUnderstoodBits.clone(); 169 } 170 } else { 171 Set<QName> understood = that.getUnderstoodHeaders(); 172 if (understood != null) { 173 for (QName qname : understood) { 174 understood(qname); 175 } 176 } 177 } 178 } 179 180 /** 181 * The total number of headers. 182 */ 183 @Override 184 public int size() { 185 return super.size(); 186 } 187 188 @Override 189 public boolean hasHeaders() { 190 return !isEmpty(); 191 } 192 193 /** 194 * Adds all the headers. 195 * @deprecated throws UnsupportedOperationException from some HeaderList implementations - better iterate over items one by one 196 */ 197 @Deprecated 198 public void addAll(Header... headers) { 199 addAll(Arrays.asList(headers)); 200 } 201 202 /** 203 * Gets the {@link Header} at the specified index. 204 * 205 * <p> 206 * This method does not mark the returned {@link Header} as understood. 207 * 208 * @see #understood(int) 209 */ 210 @Override 211 public Header get(int index) { 212 return super.get(index); 213 } 214 215 /** 216 * Marks the {@link Header} at the specified index as 217 * <a href="#MU">"understood"</a>. 218 */ 219 public void understood(int index) { 249 return false; 250 } 251 return moreUnderstoodBits.get(index - 32); 252 } 253 } 254 255 /** 256 * Marks the specified {@link Header} as <a href="#MU">"understood"</a>. 257 * 258 * @deprecated 259 * By the definition of {@link ArrayList}, this operation requires 260 * O(n) search of the array, and thus inherently inefficient. 261 * 262 * Because of this, if you are developing a {@link Pipe} for 263 * a performance sensitive environment, do not use this method. 264 * 265 * @throws IllegalArgumentException 266 * if the given header is not {@link #contains(Object) contained} 267 * in this header. 268 */ 269 @Override 270 public void understood(@NotNull Header header) { 271 int sz = size(); 272 for (int i = 0; i < sz; i++) { 273 if (get(i) == header) { 274 understood(i); 275 return; 276 } 277 } 278 throw new IllegalArgumentException(); 279 } 280 281 /** 282 * Gets the first {@link Header} of the specified name. 283 * 284 * @param markAsUnderstood 285 * If this parameter is true, the returned header will 286 * be marked as <a href="#MU">"understood"</a>. 287 * @return null if not found. 288 */ 289 @Override 290 public @Nullable Header get(@NotNull String nsUri, @NotNull String localName, boolean markAsUnderstood) { 291 int len = size(); 292 for (int i = 0; i < len; i++) { 293 Header h = get(i); 294 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 295 if (markAsUnderstood) { 296 understood(i); 297 } 298 return h; 299 } 300 } 301 return null; 302 } 303 304 /** 305 * @deprecated 306 * Use {@link #get(String, String, boolean)} 307 */ 308 public Header get(String nsUri, String localName) { 309 return get(nsUri, localName, true); 310 } 311 312 /** 313 * Gets the first {@link Header} of the specified name. 314 * 315 * @param markAsUnderstood 316 * If this parameter is true, the returned header will 317 * be marked as <a href="#MU">"understood"</a>. 318 * @return null 319 * if not found. 320 */ 321 @Override 322 public @Nullable Header get(@NotNull QName name, boolean markAsUnderstood) { 323 return get(name.getNamespaceURI(), name.getLocalPart(), markAsUnderstood); 324 } 325 326 /** 327 * @deprecated 328 * Use {@link #get(QName)} 329 */ 330 public 331 @Nullable 332 Header get(@NotNull QName name) { 333 return get(name, true); 334 } 335 336 /** 337 * @deprecated 338 * Use {@link #getHeaders(String, String, boolean)} 339 */ 340 public Iterator<Header> getHeaders(final String nsUri, final String localName) { 341 return getHeaders(nsUri, localName, true); 342 } 343 344 /** 345 * Gets all the {@link Header}s of the specified name, 346 * including duplicates (if any.) 347 * 348 * @param markAsUnderstood 349 * If this parameter is true, the returned headers will 350 * be marked as <a href="#MU">"understood"</a> when they are returned 351 * from {@link Iterator#next()}. 352 * @return empty iterator if not found. 353 */ 354 public 355 @NotNull 356 @Override 357 Iterator<Header> getHeaders(@NotNull final String nsUri, @NotNull final String localName, final boolean markAsUnderstood) { 358 return new Iterator<Header>() { 359 360 int idx = 0; 361 Header next; 362 363 @Override 364 public boolean hasNext() { 365 if (next == null) { 366 fetch(); 367 } 368 return next != null; 369 } 370 371 @Override 372 public Header next() { 373 if (next == null) { 374 fetch(); 375 if (next == null) { 376 throw new NoSuchElementException(); 377 } 378 } 379 380 if (markAsUnderstood) { 381 assert get(idx - 1) == next; 382 understood(idx - 1); 383 } 384 385 Header r = next; 386 next = null; 387 return r; 388 } 389 390 private void fetch() { 391 while (idx < size()) { 392 Header h = get(idx++); 393 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 394 next = h; 395 break; 396 } 397 } 398 } 399 400 @Override 401 public void remove() { 402 throw new UnsupportedOperationException(); 403 } 404 }; 405 } 406 407 /** 408 * @see #getHeaders(String, String, boolean) 409 */ 410 public 411 @NotNull 412 @Override 413 Iterator<Header> getHeaders(@NotNull QName headerName, final boolean markAsUnderstood) { 414 return getHeaders(headerName.getNamespaceURI(), headerName.getLocalPart(), markAsUnderstood); 415 } 416 417 /** 418 * @deprecated 419 * use {@link #getHeaders(String, boolean)}. 420 */ 421 public 422 @NotNull 423 Iterator<Header> getHeaders(@NotNull final String nsUri) { 424 return getHeaders(nsUri, true); 425 } 426 427 /** 428 * Gets an iteration of headers {@link Header} in the specified namespace, 429 * including duplicates (if any.) 430 * 431 * @param markAsUnderstood 432 * If this parameter is true, the returned headers will 433 * be marked as <a href="#MU">"understood"</a> when they are returned 434 * from {@link Iterator#next()}. 435 * @return 436 * empty iterator if not found. 437 */ 438 public 439 @NotNull 440 @Override 441 Iterator<Header> getHeaders(@NotNull final String nsUri, final boolean markAsUnderstood) { 442 return new Iterator<Header>() { 443 444 int idx = 0; 445 Header next; 446 447 @Override 448 public boolean hasNext() { 449 if (next == null) { 450 fetch(); 451 } 452 return next != null; 453 } 454 455 @Override 456 public Header next() { 457 if (next == null) { 458 fetch(); 459 if (next == null) { 460 throw new NoSuchElementException(); 461 } 462 } 463 464 if (markAsUnderstood) { 465 assert get(idx - 1) == next; 466 understood(idx - 1); 467 } 468 469 Header r = next; 470 next = null; 471 return r; 472 } 473 474 private void fetch() { 475 while (idx < size()) { 476 Header h = get(idx++); 477 if (h.getNamespaceURI().equals(nsUri)) { 478 next = h; 479 break; 480 } 481 } 482 } 483 484 @Override 485 public void remove() { 486 throw new UnsupportedOperationException(); 487 } 488 }; 489 } 490 491 /** 492 * Returns the value of WS-Addressing <code>To</code> header. The <code>version</code> 493 * identifies the WS-Addressing version and the header returned is targeted at 494 * the current implicit role. Caches the value for subsequent invocation. 495 * Duplicate <code>To</code> headers are detected earlier. 496 * 497 * @param av WS-Addressing version 498 * @param sv SOAP version 499 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 500 * @return Value of WS-Addressing To header, anonymous URI if no header is present 501 */ 502 public String getTo(AddressingVersion av, SOAPVersion sv) { 503 return AddressingUtils.getTo(this, av, sv); 504 } 505 506 /** 507 * Returns the value of WS-Addressing <code>Action</code> header. The <code>version</code> 508 * identifies the WS-Addressing version and the header returned is targeted at 509 * the current implicit role. Caches the value for subsequent invocation. 510 * Duplicate <code>Action</code> headers are detected earlier. 511 * 512 * @param av WS-Addressing version 513 * @param sv SOAP version 514 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 515 * @return Value of WS-Addressing Action header, null if no header is present 516 */ 517 public String getAction(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 518 return AddressingUtils.getAction(this, av, sv); 519 } 520 521 /** 522 * Returns the value of WS-Addressing <code>ReplyTo</code> header. The <code>version</code> 523 * identifies the WS-Addressing version and the header returned is targeted at 524 * the current implicit role. Caches the value for subsequent invocation. 525 * Duplicate <code>ReplyTo</code> headers are detected earlier. 526 * 527 * @param av WS-Addressing version 528 * @param sv SOAP version 529 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 530 * @return Value of WS-Addressing ReplyTo header, null if no header is present 531 */ 532 public WSEndpointReference getReplyTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 533 return AddressingUtils.getReplyTo(this, av, sv); 534 } 535 536 /** 537 * Returns the value of WS-Addressing <code>FaultTo</code> header. The <code>version</code> 538 * identifies the WS-Addressing version and the header returned is targeted at 539 * the current implicit role. Caches the value for subsequent invocation. 540 * Duplicate <code>FaultTo</code> headers are detected earlier. 541 * 542 * @param av WS-Addressing version 543 * @param sv SOAP version 544 * @throws IllegalArgumentException if either <code>av</code> or <code>sv</code> is null. 545 * @return Value of WS-Addressing FaultTo header, null if no header is present 546 */ 547 public WSEndpointReference getFaultTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 548 return AddressingUtils.getFaultTo(this, av, sv); 549 } 550 551 /** 552 * Returns the value of WS-Addressing <code>MessageID</code> header. The <code>version</code> 553 * identifies the WS-Addressing version and the header returned is targeted at 554 * the current implicit role. Caches the value for subsequent invocation. 555 * Duplicate <code>MessageID</code> headers are detected earlier. 556 * 557 * @param av WS-Addressing version 558 * @param sv SOAP version 559 * @throws WebServiceException if either <code>av</code> or <code>sv</code> is null. 560 * @return Value of WS-Addressing MessageID header, null if no header is present 561 */ 562 public String getMessageID(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 563 return AddressingUtils.getMessageID(this, av, sv); 564 } 565 566 /** 567 * Returns the value of WS-Addressing <code>RelatesTo</code> header. The <code>version</code> 568 * identifies the WS-Addressing version and the header returned is targeted at 569 * the current implicit role. Caches the value for subsequent invocation. 570 * Duplicate <code>RelatesTo</code> headers are detected earlier. 571 * 572 * @param av WS-Addressing version 573 * @param sv SOAP version 574 * @throws WebServiceException if either <code>av</code> or <code>sv</code> is null. 575 * @return Value of WS-Addressing RelatesTo header, null if no header is present 576 */ 577 public String getRelatesTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { 578 return AddressingUtils.getRelatesTo(this, av, sv); 579 } 580 581 /** 582 * Creates a set of outbound WS-Addressing headers on the client with the 583 * specified Action Message Addressing Property value. 584 * <p><p> 585 * This method needs to be invoked right after such a Message is 586 * created which is error prone but so far only MEX, RM and JAX-WS 587 * creates a request so this ugliness is acceptable. This method is also used 588 * to create protocol messages that are not associated with any {@link WSBinding} 589 * and {@link WSDLPort}. 590 * 591 * @param packet request packet 592 * @param av WS-Addressing version 593 * @param sv SOAP version 594 * @param oneway Indicates if the message exchange pattern is oneway 595 * @param action Action Message Addressing Property value 596 * @param mustUnderstand to indicate if the addressing headers are set with mustUnderstand attribute 597 */ 598 public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action, boolean mustUnderstand) { 599 AddressingUtils.fillRequestAddressingHeaders(this, packet, av, sv, oneway, action, mustUnderstand); 600 } 601 602 public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action) { 603 AddressingUtils.fillRequestAddressingHeaders(this, packet, av, sv, oneway, action); 604 } 605 606 /** 607 * Creates a set of outbound WS-Addressing headers on the client with the 608 * default Action Message Addressing Property value. 609 * <p><p> 610 * This method needs to be invoked right after such a Message is 611 * created which is error prone but so far only MEX, RM and JAX-WS 612 * creates a request so this ugliness is acceptable. If more components 613 * are identified using this, then we may revisit this. 614 * <p><p> 615 * This method is used if default Action Message Addressing Property is to 616 * be used. See 617 * {@link #fillRequestAddressingHeaders(Packet, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, boolean, String)} 618 * if non-default Action is to be used, for example when creating a protocol message not 619 * associated with {@link WSBinding} and {@link WSDLPort}. 620 * This method uses SOAPAction as the Action unless set expplicitly in the wsdl. 621 * @param wsdlPort request WSDL port 622 * @param binding request WSBinding 623 * @param packet request packet 624 */ 625 public void fillRequestAddressingHeaders(WSDLPort wsdlPort, @NotNull WSBinding binding, Packet packet) { 626 AddressingUtils.fillRequestAddressingHeaders(this, wsdlPort, binding, packet); 627 } 628 629 /** 630 * Adds a new {@link Header}. 631 * 632 * <p> 633 * Order doesn't matter in headers, so this method 634 * does not make any guarantee as to where the new header 635 * is inserted. 636 * 637 * @return 638 * always true. Don't use the return value. 639 */ 640 @Override 641 public boolean add(Header header) { 642 return super.add(header); 643 } 644 645 /** 646 * Removes the first {@link Header} of the specified name. 647 * @param nsUri namespace URI of the header to remove 648 * @param localName local part of the FQN of the header to remove 649 * 650 * @return null if not found. 651 */ 652 public 653 @Nullable 654 @Override 655 Header remove(@NotNull String nsUri, @NotNull String localName) { 656 int len = size(); 657 for (int i = 0; i < len; i++) { 658 Header h = get(i); 659 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 660 return remove(i); 661 } 662 } 663 return null; 664 } 665 666 /** 667 * Replaces an existing {@link Header} or adds a new {@link Header}. 668 * 669 * <p> 670 * Order doesn't matter in headers, so this method 671 * does not make any guarantee as to where the new header 672 * is inserted. 673 * 674 * @return 675 * always true. Don't use the return value. 676 */ 677 @Override 678 public boolean addOrReplace(Header header) { 679 for (int i=0; i < size(); i++) { 680 Header hdr = get(i); 681 if (hdr.getNamespaceURI().equals(header.getNamespaceURI()) && 682 hdr.getLocalPart().equals(header.getLocalPart())) { 683 // Put the new header in the old position. Call super versions 684 // internally to avoid UnsupportedOperationException 685 removeInternal(i); 686 addInternal(i, header); 687 return true; 688 } 689 } 690 return add(header); 691 } 692 693 @Override 694 public void replace(Header old, Header header) { 695 for (int i=0; i < size(); i++) { 696 Header hdr = get(i); 697 if (hdr.getNamespaceURI().equals(header.getNamespaceURI()) && 698 hdr.getLocalPart().equals(header.getLocalPart())) { 699 // Put the new header in the old position. Call super versions 700 // internally to avoid UnsupportedOperationException 701 removeInternal(i); 702 addInternal(i, header); 703 return; 704 } 705 } 706 707 throw new IllegalArgumentException(); 708 } 709 710 protected void addInternal(int index, Header header) { 711 super.add(index, header); 712 } 713 714 protected Header removeInternal(int index) { 715 return super.remove(index); 716 } 717 718 /** 719 * Removes the first {@link Header} of the specified name. 720 * 721 * @param name fully qualified name of the header to remove 722 * 723 * @return null if not found. 724 */ 725 public 726 @Nullable 727 @Override 728 Header remove(@NotNull QName name) { 729 return remove(name.getNamespaceURI(), name.getLocalPart()); 730 } 731 732 /** 733 * Removes the first {@link Header} of the specified name. 734 * 735 * @param index index of the header to remove 736 * 737 * @return removed header 738 */ 739 @Override 740 public Header remove(int index) { 741 removeUnderstoodBit(index); 742 return super.remove(index); 743 } 744 745 /** 746 * Removes the "understood" bit for header on the position specified by {@code index} parameter 747 * from the set of understood header bits. 799 moreUnderstoodBits = null; 800 } 801 } 802 803 /** 804 * Removes a single instance of the specified element from this 805 * header list, if it is present. More formally, 806 * removes a header <tt>h</tt> such that <tt>(o==null ? h==null : 807 * o.equals(h))</tt>, if the header list contains one or more such 808 * headers. Returns <tt>true</tt> if the list contained the 809 * specified element (or equivalently, if the list changed as a 810 * result of the call).<p> 811 * 812 * @param o element to be removed from this list, if present. 813 * @return <tt>true</tt> if the list contained the specified element. 814 * @see #remove(javax.xml.namespace.QName) 815 */ 816 @Override 817 public boolean remove(Object o) { 818 if (o != null) { 819 for (int index = 0; index < this.size(); index++) { 820 if (o.equals(this.get(index))) { 821 remove(index); 822 return true; 823 } 824 } 825 } 826 827 return false; 828 } 829 830 public Header remove(Header h) { 831 if (remove((Object) h)) { 832 return h; 833 } else { 834 return null; 835 } 836 } 837 838 /** 839 * Creates a copy. 840 * 841 * This handles null {@link HeaderList} correctly. 842 * 843 * @param original 844 * Can be null, in which case null will be returned. 845 */ 846 public static HeaderList copy(MessageHeaders original) { 847 if (original == null) { 848 return null; 849 } else { 850 return new HeaderList(original); 851 } 852 } 853 854 /** 855 * Creates a copy. 856 * 857 * This handles null {@link HeaderList} correctly. 858 * 859 * @param original 860 * Can be null, in which case null will be returned. 861 */ 862 public static HeaderList copy(HeaderList original) { 863 return copy((MessageHeaders) original); 864 } 865 866 public void readResponseAddressingHeaders(WSDLPort wsdlPort, WSBinding binding) { 867 // read Action 868 // String wsaAction = getAction(binding.getAddressingVersion(), binding.getSOAPVersion()); 869 // TODO: validate client-inbound Action 870 } 871 872 @Override 873 public void understood(QName name) { 874 get(name, true); 875 } 876 877 @Override 878 public void understood(String nsUri, String localName) { 879 get(nsUri, localName, true); 880 } 881 882 @Override 883 public Set<QName> getUnderstoodHeaders() { 884 Set<QName> understoodHdrs = new HashSet<QName>(); 885 for (int i = 0; i < size(); i++) { 886 if (isUnderstood(i)) { 887 Header header = get(i); 888 understoodHdrs.add(new QName(header.getNamespaceURI(), header.getLocalPart())); 889 } 890 } 891 return understoodHdrs; 892 // throw new UnsupportedOperationException("getUnderstoodHeaders() is not implemented by HeaderList"); 893 } 894 895 @Override 896 public boolean isUnderstood(Header header) { 897 return isUnderstood(header.getNamespaceURI(), header.getLocalPart()); 898 } 899 900 @Override 901 public boolean isUnderstood(String nsUri, String localName) { 902 for (int i = 0; i < size(); i++) { 903 Header h = get(i); 904 if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { 905 return isUnderstood(i); 906 } 907 } 908 return false; 909 } 910 911 @Override 912 public boolean isUnderstood(QName name) { 913 return isUnderstood(name.getNamespaceURI(), name.getLocalPart()); 914 } 915 916 @Override 917 public Set<QName> getNotUnderstoodHeaders(Set<String> roles, Set<QName> knownHeaders, WSBinding binding) { 918 Set<QName> notUnderstoodHeaders = null; 919 if (roles == null) { 920 roles = new HashSet<String>(); 921 } 922 SOAPVersion effectiveSoapVersion = getEffectiveSOAPVersion(binding); 923 roles.add(effectiveSoapVersion.implicitRole); 924 for (int i = 0; i < size(); i++) { 925 if (!isUnderstood(i)) { 926 Header header = get(i); 927 if (!header.isIgnorable(effectiveSoapVersion, roles)) { 928 QName qName = new QName(header.getNamespaceURI(), header.getLocalPart()); 929 if (binding == null) { 930 //if binding is null, no further checks needed...we already 931 //know this header is not understood from the isUnderstood 932 //check above 933 if (notUnderstoodHeaders == null) { 934 notUnderstoodHeaders = new HashSet<QName>(); 935 } 936 notUnderstoodHeaders.add(qName); 937 } else { 938 // if the binding is not null, see if the binding can understand it 939 if (binding instanceof SOAPBindingImpl && !((SOAPBindingImpl) binding).understandsHeader(qName)) { 940 if (!knownHeaders.contains(qName)) { 941 //logger.info("Element not understood=" + qName); 942 if (notUnderstoodHeaders == null) { 943 notUnderstoodHeaders = new HashSet<QName>(); 944 } 945 notUnderstoodHeaders.add(qName); 946 } 947 } 948 } 949 } 950 } 951 } 952 return notUnderstoodHeaders; 953 } 954 955 private SOAPVersion getEffectiveSOAPVersion(WSBinding binding) { 956 SOAPVersion mySOAPVersion = (soapVersion != null) ? soapVersion : binding.getSOAPVersion(); 957 if (mySOAPVersion == null) { 958 mySOAPVersion = SOAPVersion.SOAP_11; 959 } 960 return mySOAPVersion; 961 } 962 963 public void setSoapVersion(SOAPVersion soapVersion) { 964 this.soapVersion = soapVersion; 965 } 966 967 @Override 968 public Iterator<Header> getHeaders() { 969 return iterator(); 970 } 971 972 @Override 973 public List<Header> asList() { 974 return this; 975 } 976 } |