|
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 } |