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> 72 * To perform SOAP mustUnderstang processing correctly, we need to keep 73 * track of headers that are understood and headers that are not. 74 * This is a collaborative process among {@link Pipe}s, thus it's something 75 * a {@link Pipe} author needs to keep in mind. 76 * 77 * <p> 78 * Specifically, when a {@link Pipe} sees a header and processes it 79 * (that is, if it did enough computing with the header to claim that 80 * the header is understood), then it should mark the corresponding 81 * header as "understood". For example, when a pipe that handles JAX-WSA 82 * examins the <wsa:To> header, it can claim that it understood the header. 83 * But for example, if a pipe that does the signature verification checks 84 * <wsa:To> for a signature, that would not be considered as "understood". 85 * 86 * <p> 87 * There are two ways to mark a header as understood: 88 * 89 * <ol> 90 * <li>Use one of the <tt>getXXX</tt> methods that take a 91 * boolean <tt>markAsUnderstood</tt> parameter. 92 * Most often, a {@link Pipe} knows it's going to understand a header 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) { 220 // check that index is in range 221 if (index >= size()) { 222 throw new ArrayIndexOutOfBoundsException(index); 223 } 224 225 if (index < 32) { 226 understoodBits |= 1 << index; 227 } else { 228 if (moreUnderstoodBits == null) { 229 moreUnderstoodBits = new BitSet(); 230 } 231 moreUnderstoodBits.set(index - 32); 232 } 233 } 234 235 /** 236 * Returns true if a {@link Header} at the given index 237 * was <a href="#MU">"understood"</a>. 238 */ 239 public boolean isUnderstood(int index) { 240 // check that index is in range 241 if (index >= size()) { 242 throw new ArrayIndexOutOfBoundsException(index); 243 } 244 245 if (index < 32) { 246 return understoodBits == (understoodBits | (1 << index)); 247 } else { 248 if (moreUnderstoodBits == null) { 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. 748 * 749 * @param index position of the bit to remove 750 */ 751 private void removeUnderstoodBit(int index) { 752 assert index < size(); 753 754 if (index < 32) { 755 /** 756 * Let 757 * R be the bit to be removed 758 * M be a more significant "upper" bit than bit R 759 * L be a less significant "lower" bit than bit R 760 * 761 * Then following 3 lines of code produce these results: 762 * 763 * old understoodBits = MMMMMMMMMMMMRLLLLLLLLLLLLLLLLLLL 764 * 765 * shiftedUpperBits = 0MMMMMMMMMMMM0000000000000000000 766 * 767 * lowerBits = 0000000000000LLLLLLLLLLLLLLLLLLL 768 * 769 * new understoodBits = 0MMMMMMMMMMMMLLLLLLLLLLLLLLLLLLL 770 * 771 * The R bit is removed and all the upper bits are shifted right (unsigned) 772 */ 773 int shiftedUpperBits = understoodBits >>> -31 + index << index; 774 int lowerBits = understoodBits << -index >>> 31 - index >>> 1; 775 understoodBits = shiftedUpperBits | lowerBits; 776 777 if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { 778 if (moreUnderstoodBits.get(0)) { 779 understoodBits |= 0x80000000; 780 } 781 782 moreUnderstoodBits.clear(0); 783 for (int i = moreUnderstoodBits.nextSetBit(1); i > 0; i = moreUnderstoodBits.nextSetBit(i + 1)) { 784 moreUnderstoodBits.set(i - 1); 785 moreUnderstoodBits.clear(i); 786 } 787 } 788 } else if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { 789 index -= 32; 790 moreUnderstoodBits.clear(index); 791 for (int i = moreUnderstoodBits.nextSetBit(index); i >= 1; i = moreUnderstoodBits.nextSetBit(i + 1)) { 792 moreUnderstoodBits.set(i - 1); 793 moreUnderstoodBits.clear(i); 794 } 795 } 796 797 // remove bit set if the new size will be < 33 => we fit all bits into int 798 if (size() - 1 <= 33 && moreUnderstoodBits != null) { 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 }