/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.axis2.handlers.addressing;

import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.util.AttributeHelper;
import org.apache.axiom.om.util.ElementHelper;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axiom.util.UIDGenerator;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.AddressingConstants;
import org.apache.axis2.addressing.AddressingFaultsHelper;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.addressing.EndpointReferenceHelper;
import org.apache.axis2.addressing.RelatesTo;
import org.apache.axis2.addressing.i18n.AddressingMessages;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisEndpoint;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.axis2.handlers.AbstractTemplatedHandler;
import org.apache.axis2.util.JavaUtils;
import org.apache.axis2.util.LoggingControl;
import org.apache.axis2.util.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class AddressingOutHandler extends AbstractTemplatedHandler implements AddressingConstants {

    private static final Log log = LogFactory.getLog(AddressingOutHandler.class);

    // TODO: This is required for MessageContext#getModuleParameter.
    //       Not clear why there is no way to automatically determine this!
    private static final String MODULE_NAME = "addressing";


    public boolean shouldInvoke(MessageContext msgContext) throws AxisFault {
        Parameter param = null;
        boolean disableAddressing = false;
        
        Object o = msgContext.getProperty(DISABLE_ADDRESSING_FOR_OUT_MESSAGES);
        if (o == null || !(o instanceof Boolean)) {
            //determine whether outbound addressing has been disabled or not.
            // Get default value from module.xml or axis2.xml files
            param = msgContext.getModuleParameter(DISABLE_ADDRESSING_FOR_OUT_MESSAGES, MODULE_NAME, handlerDesc);
            disableAddressing =
                msgContext.isPropertyTrue(DISABLE_ADDRESSING_FOR_OUT_MESSAGES,
                    JavaUtils.isTrueExplicitly(Utils.getParameterValue(param)));
        } else {
            disableAddressing = (Boolean) o;
        }

        if (disableAddressing) {
            if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                log.trace(msgContext.getLogIDString() +
                        " Addressing is disabled. Not adding WS-Addressing headers.");
            }
            return false;
        }
        return true;
    }
    
    public InvocationResponse doInvoke(MessageContext msgContext) throws AxisFault {
        

        // Determine the addressin namespace in effect.
        Object addressingVersionFromCurrentMsgCtxt = msgContext.getProperty(WS_ADDRESSING_VERSION);
        if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
            log.trace("Addressing version string from messageContext=" +
                    addressingVersionFromCurrentMsgCtxt);
        }
        boolean isSubmissionNamespace =
                Submission.WSA_NAMESPACE.equals(addressingVersionFromCurrentMsgCtxt);

        // Determine whether to include optional addressing headers in the output.
        // Get default value from module.xml or axis2.xml files
        Parameter param = msgContext.getModuleParameter(
                INCLUDE_OPTIONAL_HEADERS, MODULE_NAME, handlerDesc);
        boolean includeOptionalHeaders =
            msgContext.isPropertyTrue(INCLUDE_OPTIONAL_HEADERS,
                    JavaUtils.isTrueExplicitly(Utils.getParameterValue(param)));

        if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
            log.debug("includeOptionalHeaders=" + includeOptionalHeaders);
        }

        // Determine if a MustUnderstand attribute will be added to all headers in the
        // addressing namespace.
        boolean addMustUnderstandAttribute =
                msgContext.isPropertyTrue(ADD_MUST_UNDERSTAND_TO_ADDRESSING_HEADERS);

        // what if there are addressing headers already in the message. Do you replace that or not?
        // Lets have a parameter to control that. The default behavior is you won't replace addressing
        // headers if there are any (this was the case so far).
        boolean replaceHeaders = msgContext.isPropertyTrue(REPLACE_ADDRESSING_HEADERS);
        
        // Allow the user to specify the role these WS-Addressing headers should be targetted at.
        String role = (String) msgContext.getProperty(SOAP_ROLE_FOR_ADDRESSING_HEADERS);

        WSAHeaderWriter writer = new WSAHeaderWriter(msgContext, isSubmissionNamespace,
                                                     addMustUnderstandAttribute, replaceHeaders,
                                                     includeOptionalHeaders, role);
        writer.writeHeaders();

        return InvocationResponse.CONTINUE;
    }

    private class WSAHeaderWriter {

        private MessageContext messageContext;
        private SOAPEnvelope envelope;
        private SOAPHeader header;
        private SOAPFactory factory;
        private Options messageContextOptions;
        private OMNamespace addressingNamespaceObject;
        private String addressingNamespace;
        private String addressingRole;

        private boolean isFinalAddressingNamespace;
        private boolean addMustUnderstandAttribute;
        private boolean replaceHeaders;  // determines whether we replace the existing headers or not, if they present
        private boolean includeOptionalHeaders;

        private ArrayList existingWSAHeaders = null;
        
        public WSAHeaderWriter(MessageContext mc, boolean isSubmissionNamespace, boolean addMU,
                               boolean replace, boolean includeOptional, String role) {
            if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
                log.debug("WSAHeaderWriter: isFinal=" + !isSubmissionNamespace + " addMU=" + addMU +
                        " replace=" + replace + " includeOptional=" + includeOptional+" role="+role);
            }

            messageContext = mc;
            envelope = mc.getEnvelope();
            factory = (SOAPFactory)envelope.getOMFactory();

            messageContextOptions = messageContext.getOptions();

            addressingNamespace =
                    isSubmissionNamespace ? Submission.WSA_NAMESPACE : Final.WSA_NAMESPACE;

            header = envelope.getHeader();
            // if there is no soap header in the envelope being processed, add one.
            if (header == null) {
            	header = factory.createSOAPHeader(envelope);
            }else{
            	ArrayList addressingHeaders = header.getHeaderBlocksWithNSURI(addressingNamespace);
            	if(addressingHeaders!=null && !addressingHeaders.isEmpty()){
            		existingWSAHeaders = new ArrayList(addressingHeaders.size());
            		for(Iterator iter=addressingHeaders.iterator();iter.hasNext();){
            			SOAPHeaderBlock oe = (SOAPHeaderBlock)iter.next();
            			if(addressingRole == null || addressingRole.length() ==0 || addressingRole.equals(oe.getRole())){
            				existingWSAHeaders.add(oe.getLocalName());
            			}
            		}
            	}
            	if(addressingHeaders != null && addressingHeaders.size() ==0){
            		addressingHeaders = null;
            	}
            }
            
            isFinalAddressingNamespace = !isSubmissionNamespace;
            addMustUnderstandAttribute = addMU;
            replaceHeaders = replace;
            includeOptionalHeaders = includeOptional;
            addressingRole = role;
            
            if(!isFinalAddressingNamespace && mc.getTo() == null){
            	mc.setTo(new EndpointReference(AddressingConstants.Submission.WSA_ANONYMOUS_URL));
            }
        }

        public void writeHeaders() throws AxisFault {

            // by this time, we definitely have some addressing information to be sent. This is because,
            // we have tested at the start of this whether messageInformationHeaders are null or not.
            // So rather than declaring addressing namespace in each and every addressing header, lets
            // define that in the Header itself.
        	addressingNamespaceObject = header.declareNamespace(addressingNamespace, WSA_DEFAULT_PREFIX);

            // processing WSA To
            processToEPR();

            // processing WSA replyTo
            processReplyTo();

            // processing WSA From
            processFromEPR();

            // processing WSA FaultTo
            processFaultToEPR();

            // processing WSA MessageID
            processMessageID();

            // processing WSA Action
            processWSAAction();

            // processing WSA RelatesTo
            processRelatesTo();

            // process fault headers, if present
            processFaultsInfoIfPresent();

            // process mustUnderstand attribute, if required.
            processMustUnderstandProperty();
        }


        private void processMessageID() {
            String messageID = messageContextOptions.getMessageId();
            
            //Check whether we want to force a message id to be sent.
            if (messageID == null && includeOptionalHeaders) {
                messageID = UIDGenerator.generateURNString();
                messageContextOptions.setMessageId(messageID);
            }
            
            if (messageID != null && !isAddressingHeaderAlreadyAvailable(WSA_MESSAGE_ID, false))
            {//optional
            	ArrayList attributes = (ArrayList)messageContext.getLocalProperty(
                        AddressingConstants.MESSAGEID_ATTRIBUTES);
                createSOAPHeaderBlock(messageID, WSA_MESSAGE_ID, attributes);
            }
        }

        private void processWSAAction() throws AxisFault {
            String action = messageContextOptions.getAction();

            if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                log.trace(messageContext.getLogIDString() +
                        " processWSAAction: action from messageContext: " + action);
            }
            if (action == null || action.length()==0) {
                if (messageContext.getAxisOperation() != null) {
                    action = messageContext.getAxisOperation().getOutputAction();
                    if(action!=null){
                    	// Set this action back to obviate possible action mismatch problems
                    	messageContext.setWSAAction(action);
                    }
                    if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                        log.trace(messageContext.getLogIDString() +
                                " processWSAAction: action from AxisOperation: " + action);
                    }
                }
            }else{
	            // Use the correct fault action for the selected namespace
	            if(isFinalAddressingNamespace){
	            	if(Submission.WSA_FAULT_ACTION.equals(action)){
	            		action = Final.WSA_FAULT_ACTION;
	            		messageContextOptions.setAction(action);
	            	}
	            }else{
	            	if(Final.WSA_FAULT_ACTION.equals(action)){
	            		action = Submission.WSA_FAULT_ACTION;
	            		messageContextOptions.setAction(action);
	            	}else if(Final.WSA_SOAP_FAULT_ACTION.equals(action)){
	                    action = Submission.WSA_FAULT_ACTION;
	                    messageContextOptions.setAction(action);
	            	}
	            }
            }

            // If we need to add a wsa:Action header
            if (!isAddressingHeaderAlreadyAvailable(WSA_ACTION, false)) {
                if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                    log.trace(messageContext.getLogIDString() +
                            " processWSAAction: No existing wsa:Action header found");
                }
                // If we don't have an action to add,
                if (action == null || action.length()==0) {
                    if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                        log.trace(messageContext.getLogIDString() +
                                " processWSAAction: No action to add to header");
                    }
                    // Fault unless validation has been explictily turned off
                    if (!messageContext.isPropertyTrue(
                            AddressingConstants.DISABLE_OUTBOUND_ADDRESSING_VALIDATION))
                    {
                        throw new AxisFault(AddressingMessages.getMessage("outboundNoAction"));
                    }
                } else {
                    if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                        log.trace(messageContext.getLogIDString() +
                                " processWSAAction: Adding action to header: " + action);
                    }
                    // Otherwise just add the header
                    ArrayList attributes = (ArrayList)messageContext.getLocalProperty(
                            AddressingConstants.ACTION_ATTRIBUTES);
                    createSOAPHeaderBlock(action, WSA_ACTION, attributes);
                }
            }
        }

        private void processFaultsInfoIfPresent() {
            OMElement detailElement = AddressingFaultsHelper
                    .getDetailElementForAddressingFault(messageContext, addressingNamespaceObject);
            if (detailElement != null) {
                //The difference between SOAP 1.1 and SOAP 1.2 fault messages is explained in the WS-Addressing Specs.
                if (isFinalAddressingNamespace && messageContext.isSOAP11()) {
                    // Add detail as a wsa:FaultDetail header
                    if (!isAddressingHeaderAlreadyAvailable(Final.FAULT_HEADER_DETAIL, false)) {
                        SOAPHeaderBlock faultDetail = header.addHeaderBlock(
                                Final.FAULT_HEADER_DETAIL, addressingNamespaceObject);
                        faultDetail.addChild(ElementHelper.importOMElement(detailElement, factory));
                    }
                } else if (!messageContext.isSOAP11()) {
                    // Add detail to the Fault in the SOAP Body
                    SOAPFault fault = envelope.getBody().getFault();
                    if (fault != null && fault.getDetail() != null) {
                        fault.getDetail().addDetailEntry(
                                ElementHelper.importOMElement(detailElement, factory));
                    }
                }
            }
        }

        private void processRelatesTo() {
            if (!isAddressingHeaderAlreadyAvailable(WSA_RELATES_TO, true)) {
                RelatesTo[] relatesTo = messageContextOptions.getRelationships();

                if (relatesTo != null) {
                    for (int i = 0, length = relatesTo.length; i < length; i++) {
                        OMElement relatesToHeader = createSOAPHeaderBlock(relatesTo[i].getValue(),
                        		WSA_RELATES_TO, relatesTo[i].getExtensibilityAttributes());
                        String relationshipType = relatesTo[i].getRelationshipType();
                        if (relatesToHeader != null) {
                        	
                        	if(!includeOptionalHeaders){
                        		if (Final.WSA_DEFAULT_RELATIONSHIP_TYPE.equals(relationshipType) ||
                                        Submission.WSA_DEFAULT_RELATIONSHIP_TYPE
                                                .equals(relationshipType)) {
                        			relationshipType = null; //Omit the attribute.
                        		}
                        	}
                        	
                            if(relationshipType != null){
	                            relatesToHeader.addAttribute(WSA_RELATES_TO_RELATIONSHIP_TYPE,
	                                                         relationshipType,
	                                                         null);
                            }
                        }
                    }
                }
            }
        }

        private void processFaultToEPR() throws AxisFault {
            EndpointReference epr = messageContextOptions.getFaultTo();
            String headerName = AddressingConstants.WSA_FAULT_TO;

            //Omit the header if the epr is null.
            if (epr != null && !isAddressingHeaderAlreadyAvailable(headerName, false)) {
                addToSOAPHeader(epr, headerName);
            }
        }

        private void processFromEPR() throws AxisFault {
            EndpointReference epr = messageContextOptions.getFrom();
            String headerName = AddressingConstants.WSA_FROM;

            //Omit the header if the epr is null.
            if (epr != null && !isAddressingHeaderAlreadyAvailable(headerName, false)) {
                addToSOAPHeader(epr, headerName);
            }
        }

        private void processReplyTo() throws AxisFault {
            EndpointReference epr = messageContextOptions.getReplyTo();
            String headerName = AddressingConstants.WSA_REPLY_TO;

            //Don't check epr for null here as addToSOAPHeader() will provide an appropriate default.
            //This default is especially useful for client side outbound processing.
            if (!isAddressingHeaderAlreadyAvailable(headerName, false)) {
                addToSOAPHeader(epr, headerName);
            }
        }

        private void processToEPR() throws AxisFault {
            EndpointReference epr = messageContextOptions.getTo();
            if (epr != null && !isAddressingHeaderAlreadyAvailable(WSA_TO, false)) {
                try {
                    processToEPRReferenceInformation(epr.getAllReferenceParameters());
                }
                catch (Exception e) {
                    throw new AxisFault(AddressingMessages.getMessage("referenceParameterError"), e);
                }
                String address = epr.getAddress();
                if (address != null && address.length()!=0) {
                    if (!includeOptionalHeaders && isFinalAddressingNamespace &&
                            epr.isWSAddressingAnonymous())
                    {
                        return; //Omit the header.
                    }
                    createSOAPHeaderBlock(address, WSA_TO, epr.getAddressAttributes());
                }
            }
        }

        private OMElement createSOAPHeaderBlock(String value, String headerName, ArrayList attributes) {
            if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                log.trace("createSOAPHeaderBlock: value=" + value + " headerName=" + headerName);
            }
            if (value != null && value.length()!=0) {
                SOAPHeaderBlock soapHeaderBlock =
                        header.addHeaderBlock(headerName, addressingNamespaceObject);
                soapHeaderBlock.addChild(factory.createOMText(value));
                if (attributes != null && !attributes.isEmpty()) {
                    Iterator attrIterator = attributes.iterator();
                    while (attrIterator.hasNext()) {
                        AttributeHelper
                                .importOMAttribute((OMAttribute)attrIterator.next(), soapHeaderBlock);
                    }
                }
                addRoleToHeader(soapHeaderBlock);
                return soapHeaderBlock;
            }
            return null;
        }

        private void addToSOAPHeader(EndpointReference epr, String headerName) throws AxisFault {
            String prefix = addressingNamespaceObject.getPrefix();
            String anonymous = isFinalAddressingNamespace ?
                    Final.WSA_ANONYMOUS_URL : Submission.WSA_ANONYMOUS_URL;

            if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                log.trace("addToSOAPHeader: epr=" + epr + " headerName=" + headerName);
            }

            if (epr == null) {
                if (!includeOptionalHeaders && isFinalAddressingNamespace &&
                        AddressingConstants.WSA_REPLY_TO.equals(headerName)) {
                    return; //Omit the header.
                } else {
                    epr = new EndpointReference(anonymous);
                }
            }
            else if (!isFinalAddressingNamespace && epr.hasNoneAddress()) {
                return; //Omit the header.
            }
            else if (epr.isWSAddressingAnonymous())
            {
                if (!includeOptionalHeaders && isFinalAddressingNamespace &&
                        AddressingConstants.WSA_REPLY_TO.equals(headerName)) {
                    return; //Omit the header.
                } else {
                    epr.setAddress(anonymous);
                }
            }

            OMElement soapHeaderBlock = EndpointReferenceHelper.toOM(factory,
                                                                     epr,
                                                                     new QName(addressingNamespace,
                                                                               headerName, prefix),
                                                                     addressingNamespace);
            addRoleToHeader((SOAPHeaderBlock) soapHeaderBlock);
            header.addChild(soapHeaderBlock);
        }

        /**
         * This will add reference parameters and/or reference properties in to the message
         *
         * @param referenceInformation a Map from QName -> OMElement
         * @param parent               is the element to which the referenceparameters should be
         *                             attached
         */
        private void processToEPRReferenceInformation(Map referenceInformation) throws Exception {
            if (referenceInformation != null) {
                if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                    log.trace("processToEPRReferenceInformation: " + referenceInformation);
                }
                Iterator iterator = referenceInformation.values().iterator();
                while (iterator.hasNext()) {
                    OMElement omElement = (OMElement)iterator.next();
                    SOAPHeaderBlock newElement = ElementHelper.toSOAPHeaderBlock(omElement, factory);
                    if (isFinalAddressingNamespace) {
                        newElement.addAttribute(Final.WSA_IS_REFERENCE_PARAMETER_ATTRIBUTE,
                                               Final.WSA_TYPE_ATTRIBUTE_VALUE,
                                               addressingNamespaceObject);
                    }
                    addRoleToHeader(newElement);
                    header.addChild(newElement);
                }
            }
            // Now add reference parameters we found in the WSDL (if any)
            AxisService service = messageContext.getAxisService();
            if(service != null){
            	AxisEndpoint endpoint = service.getEndpoint(service.getEndpointName());
            	if(endpoint != null){
            		ArrayList referenceparameters = (ArrayList) endpoint.getParameterValue(REFERENCE_PARAMETER_PARAMETER);
            		if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
            			log.trace("processToEPRReferenceInformation: Reference Parameters from WSDL:" + referenceparameters);
            		}
            		if(referenceparameters!=null){
            			Iterator iterator = referenceparameters.iterator();
            			while (iterator.hasNext()) {
            				OMElement omElement = (OMElement)iterator.next();
                            // Only add the reference parameter from the WSDL if it does not already exist.
                            // This allows clients to override the values before invoking the service.
                            if (referenceInformation == null || !referenceInformation.containsKey(omElement.getQName())) {
                                SOAPHeaderBlock newElement = ElementHelper.toSOAPHeaderBlock(omElement, factory);
                                if (isFinalAddressingNamespace) {
                                    newElement.addAttribute(Final.WSA_IS_REFERENCE_PARAMETER_ATTRIBUTE,
                                                            Final.WSA_TYPE_ATTRIBUTE_VALUE,
                                                            addressingNamespaceObject);
                                }
                                addRoleToHeader(newElement);
                                header.addChild(newElement);
                            } else {
                                if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                                    log.trace("processToEPRReferenceInformation: Reference parameter already exists so ignoring value from WSDL: " + omElement);
                                }
                            }
            			}
            		}
            	}
            }
            
            if (log.isTraceEnabled()) {
                log.trace("processToEPRReferenceInformation: Header after adding reference parameters: "+header);
            }
        }

        /**
         * This will check for the existence of message information headers already in the message.
         * If there are already headers, then replacing them or not depends on the replaceHeaders
         * property.
         *
         * @param name            - Name of the message information header
         * @param multipleHeaders - determines whether to search for multiple headers, or not.
         * @return false - if one can add new headers (always the case if multipleHeaders is true),
         *         true - if new headers can't be added.
         */
        private boolean isAddressingHeaderAlreadyAvailable(String name, boolean multipleHeaders) {
        	boolean status = false;

        	if (multipleHeaders) {
        		if (replaceHeaders) {
        			QName qname = new QName(addressingNamespace, name, WSA_DEFAULT_PREFIX);
        			Iterator iterator = header.getChildrenWithName(qname);
        			while (iterator.hasNext()) {
        				iterator.next();
        				iterator.remove();
        			}
        		}
        	} else {
        		boolean exists = didAddressingHeaderExist(name);
        		if (exists && replaceHeaders) {
        			QName qname = new QName(addressingNamespace, name, WSA_DEFAULT_PREFIX);
        			OMElement addressingHeader = header.getFirstChildWithName(qname);
        			if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
        				log.trace("isAddressingHeaderAlreadyAvailable: Removing existing header:" +
        						addressingHeader.getLocalName());
        			}
        			addressingHeader.detach();
        		} else {
        			status = exists;
        		}
        	}

        	if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
        		log.trace("isAddressingHeaderAlreadyAvailable: name=" + name + " status=" + status);
        	}
        	return status;
        }

        private boolean didAddressingHeaderExist(String headerName){
        	if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
        		log.trace("didAddressingHeaderExist: headerName=" + headerName);
        	}
        	boolean result = false;
        	if(existingWSAHeaders != null){
        		result = existingWSAHeaders.contains(headerName);
        		if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
        			log.trace("didAddressingHeaderExist: existingWSAHeaders=" + existingWSAHeaders+" result="+result);
        		}
        	}
        	return result;
        }

        /**
         * Sets a mustUnderstand attribute on all headers that are found with the appropriate
         * addressing namespace.
         */
        private void processMustUnderstandProperty() {
            if (addMustUnderstandAttribute) {
                List headers = header.getHeaderBlocksWithNSURI(addressingNamespace);

                for (int i = 0, size = headers.size(); i < size; i++) {
                    SOAPHeaderBlock soapHeaderBlock = (SOAPHeaderBlock)headers.get(i);
                    soapHeaderBlock.setMustUnderstand(true);
                    if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
                        log.trace(
                                "processMustUnderstandProperty: Setting mustUnderstand=true on: " +
                                        soapHeaderBlock.getLocalName());
                    }
                }
            }
        }
        
        private void addRoleToHeader(SOAPHeaderBlock header){
        	if(addressingRole == null || addressingRole.length()==0){
        		return;
        	}
        	header.setRole(addressingRole);
        }
    }
}

