ohair@286: /* alanb@368: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.api.message; ohair@286: alanb@368: import java.util.ArrayList; alanb@368: import java.util.BitSet; alanb@368: import java.util.HashSet; alanb@368: import java.util.Iterator; alanb@368: import java.util.List; alanb@368: import java.util.NoSuchElementException; alanb@368: import java.util.Set; alanb@368: alanb@368: import javax.xml.namespace.QName; alanb@368: import javax.xml.ws.WebServiceException; alanb@368: ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.istack.internal.Nullable; ohair@286: import com.sun.xml.internal.ws.api.SOAPVersion; ohair@286: import com.sun.xml.internal.ws.api.WSBinding; ohair@286: import com.sun.xml.internal.ws.api.addressing.AddressingVersion; ohair@286: import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; ohair@286: import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; ohair@286: import com.sun.xml.internal.ws.api.pipe.Codec; ohair@286: import com.sun.xml.internal.ws.api.pipe.Pipe; alanb@368: import com.sun.xml.internal.ws.binding.SOAPBindingImpl; ohair@286: import com.sun.xml.internal.ws.protocol.soap.ClientMUTube; ohair@286: import com.sun.xml.internal.ws.protocol.soap.ServerMUTube; alanb@368: import java.util.Arrays; ohair@286: ohair@286: /** ohair@286: * A list of {@link Header}s on a {@link Message}. ohair@286: * ohair@286: *

ohair@286: * This list can be modified to add headers ohair@286: * from outside a {@link Message}, this is necessary ohair@286: * since intermediate processing layers often need to ohair@286: * put additional headers. ohair@286: * ohair@286: *

ohair@286: * Following the SOAP convention, the order among headers ohair@286: * are not significant. However, {@link Codec}s are ohair@286: * expected to preserve the order of headers in the input ohair@286: * message as much as possible. ohair@286: * ohair@286: * ohair@286: * ohair@286: *

MustUnderstand Processing

ohair@286: *

ohair@286: * To perform SOAP mustUnderstang processing correctly, we need to keep ohair@286: * track of headers that are understood and headers that are not. ohair@286: * This is a collaborative process among {@link Pipe}s, thus it's something ohair@286: * a {@link Pipe} author needs to keep in mind. ohair@286: * ohair@286: *

ohair@286: * Specifically, when a {@link Pipe} sees a header and processes it ohair@286: * (that is, if it did enough computing with the header to claim that ohair@286: * the header is understood), then it should mark the corresponding ohair@286: * header as "understood". For example, when a pipe that handles JAX-WSA ohair@286: * examins the <wsa:To> header, it can claim that it understood the header. ohair@286: * But for example, if a pipe that does the signature verification checks ohair@286: * <wsa:To> for a signature, that would not be considered as "understood". ohair@286: * ohair@286: *

ohair@286: * There are two ways to mark a header as understood: ohair@286: * ohair@286: *

    ohair@286: *
  1. Use one of the getXXX methods that take a ohair@286: * boolean markAsUnderstood parameter. ohair@286: * Most often, a {@link Pipe} knows it's going to understand a header ohair@286: * as long as it's present, so this is the easiest and thus the preferred way. ohair@286: * ohair@286: * For example, if JAX-WSA looks for <wsa:To>, then it can set ohair@286: * markAsUnderstand to true, to do the obtaining of a header ohair@286: * and marking at the same time. ohair@286: * ohair@286: *
  2. Call {@link #understood(int)}. ohair@286: * If under a rare circumstance, a pipe cannot determine whether ohair@286: * it can understand it or not when you are fetching a header, then ohair@286: * you can use this method afterward to mark it as understood. ohair@286: *
ohair@286: * ohair@286: *

ohair@286: * Intuitively speaking, at the end of the day, if a header is not ohair@286: * understood but {@link Header#isIgnorable(SOAPVersion, java.util.Set)} is false, a bad thing ohair@286: * will happen. The actual implementation of the checking is more complicated, ohair@286: * for that see {@link ClientMUTube}/{@link ServerMUTube}. ohair@286: * ohair@286: * @see Message#getHeaders() ohair@286: */ alanb@368: public class HeaderList extends ArrayList

implements MessageHeaders { ohair@286: ohair@286: private static final long serialVersionUID = -6358045781349627237L; ohair@286: /** ohair@286: * Bit set to keep track of which headers are understood. ohair@286: *

ohair@286: * The first 32 headers use this field, and the rest will use ohair@286: * {@link #moreUnderstoodBits}. The expectation is that ohair@286: * most of the time a SOAP message will only have up to 32 headers, ohair@286: * so we can avoid allocating separate objects for {@link BitSet}. ohair@286: */ ohair@286: private int understoodBits; ohair@286: /** ohair@286: * If there are more than 32 headers, we use this {@link BitSet} ohair@286: * to keep track of whether those headers are understood. ohair@286: * Lazily allocated. ohair@286: */ ohair@286: private BitSet moreUnderstoodBits = null; ohair@286: alanb@368: private SOAPVersion soapVersion; alanb@368: ohair@286: /** alanb@368: * This method is deprecated - instead use this one: alanb@368: * public HeaderList(SOAPVersion) ohair@286: * Creates an empty {@link HeaderList}. ohair@286: */ alanb@368: @Deprecated ohair@286: public HeaderList() { ohair@286: } ohair@286: ohair@286: /** alanb@368: * Creates an empty {@link HeaderList} with the given soap version alanb@368: * @param soapVersion alanb@368: */ alanb@368: public HeaderList(SOAPVersion soapVersion) { alanb@368: this.soapVersion = soapVersion; alanb@368: } alanb@368: alanb@368: /** ohair@286: * Copy constructor. ohair@286: */ ohair@286: public HeaderList(HeaderList that) { ohair@286: super(that); ohair@286: this.understoodBits = that.understoodBits; ohair@286: if (that.moreUnderstoodBits != null) { ohair@286: this.moreUnderstoodBits = (BitSet) that.moreUnderstoodBits.clone(); ohair@286: } ohair@286: } ohair@286: alanb@368: public HeaderList(MessageHeaders that) { alanb@368: super(that.asList()); alanb@368: if (that instanceof HeaderList) { alanb@368: HeaderList hThat = (HeaderList) that; alanb@368: this.understoodBits = hThat.understoodBits; alanb@368: if (hThat.moreUnderstoodBits != null) { alanb@368: this.moreUnderstoodBits = (BitSet) hThat.moreUnderstoodBits.clone(); alanb@368: } alanb@368: } else { alanb@368: Set understood = that.getUnderstoodHeaders(); alanb@368: if (understood != null) { alanb@368: for (QName qname : understood) { alanb@368: understood(qname); alanb@368: } alanb@368: } alanb@368: } alanb@368: } alanb@368: ohair@286: /** ohair@286: * The total number of headers. ohair@286: */ ohair@286: @Override ohair@286: public int size() { ohair@286: return super.size(); ohair@286: } ohair@286: alanb@368: @Override alanb@368: public boolean hasHeaders() { alanb@368: return !isEmpty(); alanb@368: } alanb@368: ohair@286: /** ohair@286: * Adds all the headers. alanb@368: * @deprecated throws UnsupportedOperationException from some HeaderList implementations - better iterate over items one by one ohair@286: */ alanb@368: @Deprecated ohair@286: public void addAll(Header... headers) { alanb@368: addAll(Arrays.asList(headers)); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the {@link Header} at the specified index. ohair@286: * ohair@286: *

ohair@286: * This method does not mark the returned {@link Header} as understood. ohair@286: * ohair@286: * @see #understood(int) ohair@286: */ ohair@286: @Override ohair@286: public Header get(int index) { ohair@286: return super.get(index); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Marks the {@link Header} at the specified index as ohair@286: * "understood". ohair@286: */ ohair@286: public void understood(int index) { ohair@286: // check that index is in range ohair@286: if (index >= size()) { ohair@286: throw new ArrayIndexOutOfBoundsException(index); ohair@286: } ohair@286: ohair@286: if (index < 32) { ohair@286: understoodBits |= 1 << index; ohair@286: } else { ohair@286: if (moreUnderstoodBits == null) { ohair@286: moreUnderstoodBits = new BitSet(); ohair@286: } ohair@286: moreUnderstoodBits.set(index - 32); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns true if a {@link Header} at the given index ohair@286: * was "understood". ohair@286: */ ohair@286: public boolean isUnderstood(int index) { ohair@286: // check that index is in range ohair@286: if (index >= size()) { ohair@286: throw new ArrayIndexOutOfBoundsException(index); ohair@286: } ohair@286: ohair@286: if (index < 32) { ohair@286: return understoodBits == (understoodBits | (1 << index)); ohair@286: } else { ohair@286: if (moreUnderstoodBits == null) { ohair@286: return false; ohair@286: } ohair@286: return moreUnderstoodBits.get(index - 32); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Marks the specified {@link Header} as "understood". ohair@286: * ohair@286: * @deprecated ohair@286: * By the definition of {@link ArrayList}, this operation requires ohair@286: * O(n) search of the array, and thus inherently inefficient. ohair@286: * ohair@286: * Because of this, if you are developing a {@link Pipe} for ohair@286: * a performance sensitive environment, do not use this method. ohair@286: * ohair@286: * @throws IllegalArgumentException ohair@286: * if the given header is not {@link #contains(Object) contained} ohair@286: * in this header. ohair@286: */ alanb@368: @Override ohair@286: public void understood(@NotNull Header header) { ohair@286: int sz = size(); ohair@286: for (int i = 0; i < sz; i++) { ohair@286: if (get(i) == header) { ohair@286: understood(i); ohair@286: return; ohair@286: } ohair@286: } ohair@286: throw new IllegalArgumentException(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the first {@link Header} of the specified name. ohair@286: * ohair@286: * @param markAsUnderstood ohair@286: * If this parameter is true, the returned header will ohair@286: * be marked as "understood". ohair@286: * @return null if not found. ohair@286: */ alanb@368: @Override alanb@368: public @Nullable Header get(@NotNull String nsUri, @NotNull String localName, boolean markAsUnderstood) { ohair@286: int len = size(); ohair@286: for (int i = 0; i < len; i++) { ohair@286: Header h = get(i); ohair@286: if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { ohair@286: if (markAsUnderstood) { ohair@286: understood(i); ohair@286: } ohair@286: return h; ohair@286: } ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * @deprecated ohair@286: * Use {@link #get(String, String, boolean)} ohair@286: */ ohair@286: public Header get(String nsUri, String localName) { ohair@286: return get(nsUri, localName, true); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the first {@link Header} of the specified name. ohair@286: * ohair@286: * @param markAsUnderstood ohair@286: * If this parameter is true, the returned header will ohair@286: * be marked as "understood". ohair@286: * @return null ohair@286: * if not found. ohair@286: */ alanb@368: @Override alanb@368: public @Nullable Header get(@NotNull QName name, boolean markAsUnderstood) { ohair@286: return get(name.getNamespaceURI(), name.getLocalPart(), markAsUnderstood); ohair@286: } ohair@286: ohair@286: /** ohair@286: * @deprecated ohair@286: * Use {@link #get(QName)} ohair@286: */ ohair@286: public ohair@286: @Nullable ohair@286: Header get(@NotNull QName name) { ohair@286: return get(name, true); ohair@286: } ohair@286: ohair@286: /** ohair@286: * @deprecated ohair@286: * Use {@link #getHeaders(String, String, boolean)} ohair@286: */ ohair@286: public Iterator

getHeaders(final String nsUri, final String localName) { ohair@286: return getHeaders(nsUri, localName, true); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets all the {@link Header}s of the specified name, ohair@286: * including duplicates (if any.) ohair@286: * ohair@286: * @param markAsUnderstood ohair@286: * If this parameter is true, the returned headers will ohair@286: * be marked as "understood" when they are returned ohair@286: * from {@link Iterator#next()}. ohair@286: * @return empty iterator if not found. ohair@286: */ ohair@286: public ohair@286: @NotNull alanb@368: @Override ohair@286: Iterator
getHeaders(@NotNull final String nsUri, @NotNull final String localName, final boolean markAsUnderstood) { ohair@286: return new Iterator
() { ohair@286: ohair@286: int idx = 0; ohair@286: Header next; ohair@286: alanb@368: @Override ohair@286: public boolean hasNext() { ohair@286: if (next == null) { ohair@286: fetch(); ohair@286: } ohair@286: return next != null; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public Header next() { ohair@286: if (next == null) { ohair@286: fetch(); ohair@286: if (next == null) { ohair@286: throw new NoSuchElementException(); ohair@286: } ohair@286: } ohair@286: ohair@286: if (markAsUnderstood) { ohair@286: assert get(idx - 1) == next; ohair@286: understood(idx - 1); ohair@286: } ohair@286: ohair@286: Header r = next; ohair@286: next = null; ohair@286: return r; ohair@286: } ohair@286: ohair@286: private void fetch() { ohair@286: while (idx < size()) { ohair@286: Header h = get(idx++); ohair@286: if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { ohair@286: next = h; ohair@286: break; ohair@286: } ohair@286: } ohair@286: } ohair@286: alanb@368: @Override ohair@286: public void remove() { ohair@286: throw new UnsupportedOperationException(); ohair@286: } ohair@286: }; ohair@286: } ohair@286: ohair@286: /** ohair@286: * @see #getHeaders(String, String, boolean) ohair@286: */ ohair@286: public ohair@286: @NotNull alanb@368: @Override ohair@286: Iterator
getHeaders(@NotNull QName headerName, final boolean markAsUnderstood) { ohair@286: return getHeaders(headerName.getNamespaceURI(), headerName.getLocalPart(), markAsUnderstood); ohair@286: } ohair@286: ohair@286: /** ohair@286: * @deprecated ohair@286: * use {@link #getHeaders(String, boolean)}. ohair@286: */ ohair@286: public ohair@286: @NotNull ohair@286: Iterator
getHeaders(@NotNull final String nsUri) { ohair@286: return getHeaders(nsUri, true); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets an iteration of headers {@link Header} in the specified namespace, ohair@286: * including duplicates (if any.) ohair@286: * ohair@286: * @param markAsUnderstood ohair@286: * If this parameter is true, the returned headers will ohair@286: * be marked as "understood" when they are returned ohair@286: * from {@link Iterator#next()}. ohair@286: * @return ohair@286: * empty iterator if not found. ohair@286: */ ohair@286: public ohair@286: @NotNull alanb@368: @Override ohair@286: Iterator
getHeaders(@NotNull final String nsUri, final boolean markAsUnderstood) { ohair@286: return new Iterator
() { ohair@286: ohair@286: int idx = 0; ohair@286: Header next; ohair@286: alanb@368: @Override ohair@286: public boolean hasNext() { ohair@286: if (next == null) { ohair@286: fetch(); ohair@286: } ohair@286: return next != null; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public Header next() { ohair@286: if (next == null) { ohair@286: fetch(); ohair@286: if (next == null) { ohair@286: throw new NoSuchElementException(); ohair@286: } ohair@286: } ohair@286: ohair@286: if (markAsUnderstood) { ohair@286: assert get(idx - 1) == next; ohair@286: understood(idx - 1); ohair@286: } ohair@286: ohair@286: Header r = next; ohair@286: next = null; ohair@286: return r; ohair@286: } ohair@286: ohair@286: private void fetch() { ohair@286: while (idx < size()) { ohair@286: Header h = get(idx++); ohair@286: if (h.getNamespaceURI().equals(nsUri)) { ohair@286: next = h; ohair@286: break; ohair@286: } ohair@286: } ohair@286: } ohair@286: alanb@368: @Override ohair@286: public void remove() { ohair@286: throw new UnsupportedOperationException(); ohair@286: } ohair@286: }; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing To header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate To headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws IllegalArgumentException if either av or sv is null. ohair@286: * @return Value of WS-Addressing To header, anonymous URI if no header is present ohair@286: */ ohair@286: public String getTo(AddressingVersion av, SOAPVersion sv) { alanb@368: return AddressingUtils.getTo(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing Action header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate Action headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws IllegalArgumentException if either av or sv is null. ohair@286: * @return Value of WS-Addressing Action header, null if no header is present ohair@286: */ ohair@286: public String getAction(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { alanb@368: return AddressingUtils.getAction(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing ReplyTo header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate ReplyTo headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws IllegalArgumentException if either av or sv is null. ohair@286: * @return Value of WS-Addressing ReplyTo header, null if no header is present ohair@286: */ ohair@286: public WSEndpointReference getReplyTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { alanb@368: return AddressingUtils.getReplyTo(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing FaultTo header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate FaultTo headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws IllegalArgumentException if either av or sv is null. ohair@286: * @return Value of WS-Addressing FaultTo header, null if no header is present ohair@286: */ ohair@286: public WSEndpointReference getFaultTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { alanb@368: return AddressingUtils.getFaultTo(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing MessageID header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate MessageID headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws WebServiceException if either av or sv is null. ohair@286: * @return Value of WS-Addressing MessageID header, null if no header is present ohair@286: */ ohair@286: public String getMessageID(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { alanb@368: return AddressingUtils.getMessageID(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the value of WS-Addressing RelatesTo header. The version ohair@286: * identifies the WS-Addressing version and the header returned is targeted at ohair@286: * the current implicit role. Caches the value for subsequent invocation. ohair@286: * Duplicate RelatesTo headers are detected earlier. ohair@286: * ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @throws WebServiceException if either av or sv is null. ohair@286: * @return Value of WS-Addressing RelatesTo header, null if no header is present ohair@286: */ ohair@286: public String getRelatesTo(@NotNull AddressingVersion av, @NotNull SOAPVersion sv) { alanb@368: return AddressingUtils.getRelatesTo(this, av, sv); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates a set of outbound WS-Addressing headers on the client with the ohair@286: * specified Action Message Addressing Property value. ohair@286: *

ohair@286: * This method needs to be invoked right after such a Message is ohair@286: * created which is error prone but so far only MEX, RM and JAX-WS ohair@286: * creates a request so this ugliness is acceptable. This method is also used ohair@286: * to create protocol messages that are not associated with any {@link WSBinding} ohair@286: * and {@link WSDLPort}. ohair@286: * ohair@286: * @param packet request packet ohair@286: * @param av WS-Addressing version ohair@286: * @param sv SOAP version ohair@286: * @param oneway Indicates if the message exchange pattern is oneway ohair@286: * @param action Action Message Addressing Property value ohair@286: * @param mustUnderstand to indicate if the addressing headers are set with mustUnderstand attribute ohair@286: */ ohair@286: public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action, boolean mustUnderstand) { alanb@368: AddressingUtils.fillRequestAddressingHeaders(this, packet, av, sv, oneway, action, mustUnderstand); ohair@286: } ohair@286: ohair@286: public void fillRequestAddressingHeaders(Packet packet, AddressingVersion av, SOAPVersion sv, boolean oneway, String action) { alanb@368: AddressingUtils.fillRequestAddressingHeaders(this, packet, av, sv, oneway, action); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates a set of outbound WS-Addressing headers on the client with the ohair@286: * default Action Message Addressing Property value. ohair@286: *

ohair@286: * This method needs to be invoked right after such a Message is ohair@286: * created which is error prone but so far only MEX, RM and JAX-WS ohair@286: * creates a request so this ugliness is acceptable. If more components ohair@286: * are identified using this, then we may revisit this. ohair@286: *

ohair@286: * This method is used if default Action Message Addressing Property is to ohair@286: * be used. See ohair@286: * {@link #fillRequestAddressingHeaders(Packet, com.sun.xml.internal.ws.api.addressing.AddressingVersion, com.sun.xml.internal.ws.api.SOAPVersion, boolean, String)} ohair@286: * if non-default Action is to be used, for example when creating a protocol message not ohair@286: * associated with {@link WSBinding} and {@link WSDLPort}. ohair@286: * This method uses SOAPAction as the Action unless set expplicitly in the wsdl. ohair@286: * @param wsdlPort request WSDL port ohair@286: * @param binding request WSBinding ohair@286: * @param packet request packet ohair@286: */ ohair@286: public void fillRequestAddressingHeaders(WSDLPort wsdlPort, @NotNull WSBinding binding, Packet packet) { alanb@368: AddressingUtils.fillRequestAddressingHeaders(this, wsdlPort, binding, packet); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Adds a new {@link Header}. ohair@286: * ohair@286: *

ohair@286: * Order doesn't matter in headers, so this method ohair@286: * does not make any guarantee as to where the new header ohair@286: * is inserted. ohair@286: * ohair@286: * @return ohair@286: * always true. Don't use the return value. ohair@286: */ ohair@286: @Override ohair@286: public boolean add(Header header) { ohair@286: return super.add(header); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Removes the first {@link Header} of the specified name. ohair@286: * @param nsUri namespace URI of the header to remove ohair@286: * @param localName local part of the FQN of the header to remove ohair@286: * ohair@286: * @return null if not found. ohair@286: */ ohair@286: public ohair@286: @Nullable alanb@368: @Override ohair@286: Header remove(@NotNull String nsUri, @NotNull String localName) { ohair@286: int len = size(); ohair@286: for (int i = 0; i < len; i++) { ohair@286: Header h = get(i); ohair@286: if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { ohair@286: return remove(i); ohair@286: } ohair@286: } ohair@286: return null; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Replaces an existing {@link Header} or adds a new {@link Header}. ohair@286: * ohair@286: *

ohair@286: * Order doesn't matter in headers, so this method ohair@286: * does not make any guarantee as to where the new header ohair@286: * is inserted. ohair@286: * ohair@286: * @return ohair@286: * always true. Don't use the return value. ohair@286: */ alanb@368: @Override ohair@286: public boolean addOrReplace(Header header) { ohair@286: for (int i=0; i < size(); i++) { ohair@286: Header hdr = get(i); ohair@286: if (hdr.getNamespaceURI().equals(header.getNamespaceURI()) && ohair@286: hdr.getLocalPart().equals(header.getLocalPart())) { ohair@286: // Put the new header in the old position. Call super versions ohair@286: // internally to avoid UnsupportedOperationException ohair@286: removeInternal(i); ohair@286: addInternal(i, header); ohair@286: return true; ohair@286: } ohair@286: } ohair@286: return add(header); ohair@286: } ohair@286: alanb@368: @Override alanb@368: public void replace(Header old, Header header) { alanb@368: for (int i=0; i < size(); i++) { alanb@368: Header hdr = get(i); alanb@368: if (hdr.getNamespaceURI().equals(header.getNamespaceURI()) && alanb@368: hdr.getLocalPart().equals(header.getLocalPart())) { alanb@368: // Put the new header in the old position. Call super versions alanb@368: // internally to avoid UnsupportedOperationException alanb@368: removeInternal(i); alanb@368: addInternal(i, header); alanb@368: return; alanb@368: } alanb@368: } alanb@368: alanb@368: throw new IllegalArgumentException(); alanb@368: } alanb@368: ohair@286: protected void addInternal(int index, Header header) { ohair@286: super.add(index, header); ohair@286: } ohair@286: ohair@286: protected Header removeInternal(int index) { ohair@286: return super.remove(index); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Removes the first {@link Header} of the specified name. ohair@286: * ohair@286: * @param name fully qualified name of the header to remove ohair@286: * ohair@286: * @return null if not found. ohair@286: */ ohair@286: public ohair@286: @Nullable alanb@368: @Override ohair@286: Header remove(@NotNull QName name) { ohair@286: return remove(name.getNamespaceURI(), name.getLocalPart()); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Removes the first {@link Header} of the specified name. ohair@286: * ohair@286: * @param index index of the header to remove ohair@286: * ohair@286: * @return removed header ohair@286: */ ohair@286: @Override ohair@286: public Header remove(int index) { ohair@286: removeUnderstoodBit(index); ohair@286: return super.remove(index); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Removes the "understood" bit for header on the position specified by {@code index} parameter ohair@286: * from the set of understood header bits. ohair@286: * ohair@286: * @param index position of the bit to remove ohair@286: */ ohair@286: private void removeUnderstoodBit(int index) { ohair@286: assert index < size(); ohair@286: ohair@286: if (index < 32) { ohair@286: /** ohair@286: * Let ohair@286: * R be the bit to be removed ohair@286: * M be a more significant "upper" bit than bit R ohair@286: * L be a less significant "lower" bit than bit R ohair@286: * ohair@286: * Then following 3 lines of code produce these results: ohair@286: * ohair@286: * old understoodBits = MMMMMMMMMMMMRLLLLLLLLLLLLLLLLLLL ohair@286: * ohair@286: * shiftedUpperBits = 0MMMMMMMMMMMM0000000000000000000 ohair@286: * ohair@286: * lowerBits = 0000000000000LLLLLLLLLLLLLLLLLLL ohair@286: * ohair@286: * new understoodBits = 0MMMMMMMMMMMMLLLLLLLLLLLLLLLLLLL ohair@286: * ohair@286: * The R bit is removed and all the upper bits are shifted right (unsigned) ohair@286: */ ohair@286: int shiftedUpperBits = understoodBits >>> -31 + index << index; ohair@286: int lowerBits = understoodBits << -index >>> 31 - index >>> 1; ohair@286: understoodBits = shiftedUpperBits | lowerBits; ohair@286: ohair@286: if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { ohair@286: if (moreUnderstoodBits.get(0)) { ohair@286: understoodBits |= 0x80000000; ohair@286: } ohair@286: ohair@286: moreUnderstoodBits.clear(0); ohair@286: for (int i = moreUnderstoodBits.nextSetBit(1); i > 0; i = moreUnderstoodBits.nextSetBit(i + 1)) { ohair@286: moreUnderstoodBits.set(i - 1); ohair@286: moreUnderstoodBits.clear(i); ohair@286: } ohair@286: } ohair@286: } else if (moreUnderstoodBits != null && moreUnderstoodBits.cardinality() > 0) { ohair@286: index -= 32; ohair@286: moreUnderstoodBits.clear(index); ohair@286: for (int i = moreUnderstoodBits.nextSetBit(index); i >= 1; i = moreUnderstoodBits.nextSetBit(i + 1)) { ohair@286: moreUnderstoodBits.set(i - 1); ohair@286: moreUnderstoodBits.clear(i); ohair@286: } ohair@286: } ohair@286: ohair@286: // remove bit set if the new size will be < 33 => we fit all bits into int ohair@286: if (size() - 1 <= 33 && moreUnderstoodBits != null) { ohair@286: moreUnderstoodBits = null; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Removes a single instance of the specified element from this ohair@286: * header list, if it is present. More formally, ohair@286: * removes a header h such that (o==null ? h==null : ohair@286: * o.equals(h)), if the header list contains one or more such ohair@286: * headers. Returns true if the list contained the ohair@286: * specified element (or equivalently, if the list changed as a ohair@286: * result of the call).

ohair@286: * ohair@286: * @param o element to be removed from this list, if present. ohair@286: * @return true if the list contained the specified element. ohair@286: * @see #remove(javax.xml.namespace.QName) ohair@286: */ ohair@286: @Override ohair@286: public boolean remove(Object o) { ohair@286: if (o != null) { alanb@368: for (int index = 0; index < this.size(); index++) { ohair@286: if (o.equals(this.get(index))) { ohair@286: remove(index); ohair@286: return true; ohair@286: } alanb@368: } ohair@286: } ohair@286: ohair@286: return false; ohair@286: } ohair@286: alanb@368: public Header remove(Header h) { alanb@368: if (remove((Object) h)) { alanb@368: return h; alanb@368: } else { alanb@368: return null; alanb@368: } alanb@368: } alanb@368: alanb@368: /** alanb@368: * Creates a copy. alanb@368: * alanb@368: * This handles null {@link HeaderList} correctly. alanb@368: * alanb@368: * @param original alanb@368: * Can be null, in which case null will be returned. alanb@368: */ alanb@368: public static HeaderList copy(MessageHeaders original) { alanb@368: if (original == null) { alanb@368: return null; alanb@368: } else { alanb@368: return new HeaderList(original); alanb@368: } alanb@368: } alanb@368: ohair@286: /** ohair@286: * Creates a copy. ohair@286: * ohair@286: * This handles null {@link HeaderList} correctly. ohair@286: * ohair@286: * @param original ohair@286: * Can be null, in which case null will be returned. ohair@286: */ ohair@286: public static HeaderList copy(HeaderList original) { alanb@368: return copy((MessageHeaders) original); ohair@286: } ohair@286: ohair@286: public void readResponseAddressingHeaders(WSDLPort wsdlPort, WSBinding binding) { ohair@286: // read Action alanb@368: // String wsaAction = getAction(binding.getAddressingVersion(), binding.getSOAPVersion()); ohair@286: // TODO: validate client-inbound Action ohair@286: } alanb@368: alanb@368: @Override alanb@368: public void understood(QName name) { alanb@368: get(name, true); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public void understood(String nsUri, String localName) { alanb@368: get(nsUri, localName, true); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public Set getUnderstoodHeaders() { alanb@368: Set understoodHdrs = new HashSet(); alanb@368: for (int i = 0; i < size(); i++) { alanb@368: if (isUnderstood(i)) { alanb@368: Header header = get(i); alanb@368: understoodHdrs.add(new QName(header.getNamespaceURI(), header.getLocalPart())); alanb@368: } alanb@368: } alanb@368: return understoodHdrs; alanb@368: // throw new UnsupportedOperationException("getUnderstoodHeaders() is not implemented by HeaderList"); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public boolean isUnderstood(Header header) { alanb@368: return isUnderstood(header.getNamespaceURI(), header.getLocalPart()); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public boolean isUnderstood(String nsUri, String localName) { alanb@368: for (int i = 0; i < size(); i++) { alanb@368: Header h = get(i); alanb@368: if (h.getLocalPart().equals(localName) && h.getNamespaceURI().equals(nsUri)) { alanb@368: return isUnderstood(i); alanb@368: } alanb@368: } alanb@368: return false; alanb@368: } alanb@368: alanb@368: @Override alanb@368: public boolean isUnderstood(QName name) { alanb@368: return isUnderstood(name.getNamespaceURI(), name.getLocalPart()); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public Set getNotUnderstoodHeaders(Set roles, Set knownHeaders, WSBinding binding) { alanb@368: Set notUnderstoodHeaders = null; alanb@368: if (roles == null) { alanb@368: roles = new HashSet(); alanb@368: } alanb@368: SOAPVersion effectiveSoapVersion = getEffectiveSOAPVersion(binding); alanb@368: roles.add(effectiveSoapVersion.implicitRole); alanb@368: for (int i = 0; i < size(); i++) { alanb@368: if (!isUnderstood(i)) { alanb@368: Header header = get(i); alanb@368: if (!header.isIgnorable(effectiveSoapVersion, roles)) { alanb@368: QName qName = new QName(header.getNamespaceURI(), header.getLocalPart()); alanb@368: if (binding == null) { alanb@368: //if binding is null, no further checks needed...we already alanb@368: //know this header is not understood from the isUnderstood alanb@368: //check above alanb@368: if (notUnderstoodHeaders == null) { alanb@368: notUnderstoodHeaders = new HashSet(); alanb@368: } alanb@368: notUnderstoodHeaders.add(qName); alanb@368: } else { alanb@368: // if the binding is not null, see if the binding can understand it alanb@368: if (binding instanceof SOAPBindingImpl && !((SOAPBindingImpl) binding).understandsHeader(qName)) { alanb@368: if (!knownHeaders.contains(qName)) { alanb@368: //logger.info("Element not understood=" + qName); alanb@368: if (notUnderstoodHeaders == null) { alanb@368: notUnderstoodHeaders = new HashSet(); alanb@368: } alanb@368: notUnderstoodHeaders.add(qName); alanb@368: } alanb@368: } alanb@368: } alanb@368: } alanb@368: } alanb@368: } alanb@368: return notUnderstoodHeaders; alanb@368: } alanb@368: alanb@368: private SOAPVersion getEffectiveSOAPVersion(WSBinding binding) { alanb@368: SOAPVersion mySOAPVersion = (soapVersion != null) ? soapVersion : binding.getSOAPVersion(); alanb@368: if (mySOAPVersion == null) { alanb@368: mySOAPVersion = SOAPVersion.SOAP_11; alanb@368: } alanb@368: return mySOAPVersion; alanb@368: } alanb@368: alanb@368: public void setSoapVersion(SOAPVersion soapVersion) { alanb@368: this.soapVersion = soapVersion; alanb@368: } alanb@368: alanb@368: @Override alanb@368: public Iterator

getHeaders() { alanb@368: return iterator(); alanb@368: } alanb@368: alanb@368: @Override alanb@368: public List
asList() { alanb@368: return this; alanb@368: } ohair@286: }