src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/HeaderList.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
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 &lt;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 * &lt;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 &lt;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 }

mercurial