Thu, 31 Aug 2017 15:18:52 +0800
merge
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 */
26 package com.sun.xml.internal.ws.api.message;
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;
36 import javax.xml.namespace.QName;
37 import javax.xml.ws.WebServiceException;
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;
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 {
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;
132 private SOAPVersion soapVersion;
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 }
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 }
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 }
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 }
180 /**
181 * The total number of headers.
182 */
183 @Override
184 public int size() {
185 return super.size();
186 }
188 @Override
189 public boolean hasHeaders() {
190 return !isEmpty();
191 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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>() {
360 int idx = 0;
361 Header next;
363 @Override
364 public boolean hasNext() {
365 if (next == null) {
366 fetch();
367 }
368 return next != null;
369 }
371 @Override
372 public Header next() {
373 if (next == null) {
374 fetch();
375 if (next == null) {
376 throw new NoSuchElementException();
377 }
378 }
380 if (markAsUnderstood) {
381 assert get(idx - 1) == next;
382 understood(idx - 1);
383 }
385 Header r = next;
386 next = null;
387 return r;
388 }
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 }
400 @Override
401 public void remove() {
402 throw new UnsupportedOperationException();
403 }
404 };
405 }
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 }
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 }
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>() {
444 int idx = 0;
445 Header next;
447 @Override
448 public boolean hasNext() {
449 if (next == null) {
450 fetch();
451 }
452 return next != null;
453 }
455 @Override
456 public Header next() {
457 if (next == null) {
458 fetch();
459 if (next == null) {
460 throw new NoSuchElementException();
461 }
462 }
464 if (markAsUnderstood) {
465 assert get(idx - 1) == next;
466 understood(idx - 1);
467 }
469 Header r = next;
470 next = null;
471 return r;
472 }
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 }
484 @Override
485 public void remove() {
486 throw new UnsupportedOperationException();
487 }
488 };
489 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
602 public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action) {
603 AddressingUtils.fillRequestAddressingHeaders(this, packet, av, sv, oneway, action);
604 }
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 }
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 }
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 }
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 }
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 }
707 throw new IllegalArgumentException();
708 }
710 protected void addInternal(int index, Header header) {
711 super.add(index, header);
712 }
714 protected Header removeInternal(int index) {
715 return super.remove(index);
716 }
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 }
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 }
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();
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;
777 if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) {
778 if (moreUnderstoodBits.get(0)) {
779 understoodBits |= 0x80000000;
780 }
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 }
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 }
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 }
827 return false;
828 }
830 public Header remove(Header h) {
831 if (remove((Object) h)) {
832 return h;
833 } else {
834 return null;
835 }
836 }
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 }
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 }
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 }
872 @Override
873 public void understood(QName name) {
874 get(name, true);
875 }
877 @Override
878 public void understood(String nsUri, String localName) {
879 get(nsUri, localName, true);
880 }
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 }
895 @Override
896 public boolean isUnderstood(Header header) {
897 return isUnderstood(header.getNamespaceURI(), header.getLocalPart());
898 }
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 }
911 @Override
912 public boolean isUnderstood(QName name) {
913 return isUnderstood(name.getNamespaceURI(), name.getLocalPart());
914 }
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 }
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 }
963 public void setSoapVersion(SOAPVersion soapVersion) {
964 this.soapVersion = soapVersion;
965 }
967 @Override
968 public Iterator<Header> getHeaders() {
969 return iterator();
970 }
972 @Override
973 public List<Header> asList() {
974 return this;
975 }
976 }