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> 75 * To perform SOAP mustUnderstang processing correctly, we need to keep 76 * track of headers that are understood and headers that are not. 77 * This is a collaborative process among {@link Pipe}s, thus it's something 78 * a {@link Pipe} author needs to keep in mind. 79 * 80 * <p> 81 * Specifically, when a {@link Pipe} sees a header and processes it 82 * (that is, if it did enough computing with the header to claim that 83 * the header is understood), then it should mark the corresponding 84 * header as "understood". For example, when a pipe that handles JAX-WSA 85 * examins the <wsa:To> header, it can claim that it understood the header. 86 * But for example, if a pipe that does the signature verification checks 87 * <wsa:To> for a signature, that would not be considered as "understood". 88 * 89 * <p> 90 * There are two ways to mark a header as understood: 91 * 92 * <ol> 93 * <li>Use one of the <tt>getXXX</tt> methods that take a 94 * boolean <tt>markAsUnderstood</tt> parameter. 95 * Most often, a {@link Pipe} knows it's going to understand a header 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) { 187 // check that index is in range 188 if (index >= size()) { 189 throw new ArrayIndexOutOfBoundsException(index); 190 } 191 192 if (index < 32) { 193 understoodBits |= 1 << index; 194 } else { 195 if (moreUnderstoodBits == null) { 196 moreUnderstoodBits = new BitSet(); 197 } 198 moreUnderstoodBits.set(index - 32); 199 } 200 } 201 202 /** 203 * Returns true if a {@link Header} at the given index 204 * was <a href="#MU">"understood"</a>. 205 */ 206 public boolean isUnderstood(int index) { 207 // check that index is in range 208 if (index >= size()) { 209 throw new ArrayIndexOutOfBoundsException(index); 210 } 211 212 if (index < 32) { 213 return understoodBits == (understoodBits | (1 << index)); 214 } else { 215 if (moreUnderstoodBits == null) { 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. 942 * 943 * @param index position of the bit to remove 944 */ 945 private void removeUnderstoodBit(int index) { 946 assert index < size(); 947 948 if (index < 32) { 949 /** 950 * Let 951 * R be the bit to be removed 952 * M be a more significant "upper" bit than bit R 953 * L be a less significant "lower" bit than bit R 954 * 955 * Then following 3 lines of code produce these results: 956 * 957 * old understoodBits = MMMMMMMMMMMMRLLLLLLLLLLLLLLLLLLL 958 * 959 * shiftedUpperBits = 0MMMMMMMMMMMM0000000000000000000 960 * 961 * lowerBits = 0000000000000LLLLLLLLLLLLLLLLLLL 962 * 963 * new understoodBits = 0MMMMMMMMMMMMLLLLLLLLLLLLLLLLLLL 964 * 965 * The R bit is removed and all the upper bits are shifted right (unsigned) 966 */ 967 int shiftedUpperBits = understoodBits >>> -31 + index << index; 968 int lowerBits = understoodBits << -index >>> 31 - index >>> 1; 969 understoodBits = shiftedUpperBits | lowerBits; 970 971 if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { 972 if (moreUnderstoodBits.get(0)) { 973 understoodBits |= 0x80000000; 974 } 975 976 moreUnderstoodBits.clear(0); 977 for (int i = moreUnderstoodBits.nextSetBit(1); i > 0; i = moreUnderstoodBits.nextSetBit(i + 1)) { 978 moreUnderstoodBits.set(i - 1); 979 moreUnderstoodBits.clear(i); 980 } 981 } 982 } else if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { 983 index -= 32; 984 moreUnderstoodBits.clear(index); 985 for (int i = moreUnderstoodBits.nextSetBit(index); i >= 1; i = moreUnderstoodBits.nextSetBit(i + 1)) { 986 moreUnderstoodBits.set(i - 1); 987 moreUnderstoodBits.clear(i); 988 } 989 } 990 991 // remove bit set if the new size will be < 33 => we fit all bits into int 992 if (size() - 1 <= 33 && moreUnderstoodBits != null) { 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 }