/*
 * 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.jaxws.description.impl;

import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.ClientConfigurationFactory;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.catalog.JAXWSCatalogManager;
import org.apache.axis2.jaxws.catalog.impl.OASISCatalogManager;
import org.apache.axis2.jaxws.description.DescriptionFactory;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
import org.apache.axis2.jaxws.description.ResolvedHandlersDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.description.ServiceDescriptionJava;
import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
import org.apache.axis2.jaxws.description.ServiceRuntimeDescription;
import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
import org.apache.axis2.jaxws.description.builder.MDQConstants;
import org.apache.axis2.jaxws.description.builder.PortComposite;

import static org.apache.axis2.jaxws.description.builder.MDQConstants.RETURN_TYPE_FUTURE;
import static org.apache.axis2.jaxws.description.builder.MDQConstants.RETURN_TYPE_RESPONSE;
import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;
import org.apache.axis2.jaxws.description.builder.ParameterDescriptionComposite;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.util.WSDL4JWrapper;
import org.apache.axis2.jaxws.util.WSDLWrapper;
import org.apache.axis2.AxisFault;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.resolver.Catalog;

import javax.jws.HandlerChain;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.PortInfo;
import javax.xml.ws.soap.SOAPBinding;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.net.ConnectException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;


/** @see ../ServiceDescription */
class ServiceDescriptionImpl
        implements ServiceDescription, ServiceDescriptionWSDL, ServiceDescriptionJava {
    private ClientConfigurationFactory clientConfigFactory;
    private ConfigurationContext configContext;

    // Number of times this instance is being used.  For service-providers, this is not decremented
    // since the serice isn't un-used once it is created.  For service-requesters, the service
    // is used by ServiceDelegates, and becomes un-used when the ServiceDelegate is released.
    private int useCount = 0;
    private String wsdlURL;
    private QName serviceQName;

    private WSDLWrapper wsdlWrapper;
    private WSDLWrapper generatedWsdlWrapper;
    
    //ANNOTATION: @HandlerChain
    private HandlerChain handlerChainAnnotation;
    private HandlerChainsType handlerChainsType;

    // EndpointDescriptions from annotations and wsdl
    private Map<QName, EndpointDescription> definedEndpointDescriptions =
                new HashMap<QName, EndpointDescription>();

    // Endpoints for dynamic ports
    // This needs to be a strong reference (not Weak or Soft) so that the resource release logic
    // in the finalizer still has a reference to the EndpointDescriptions so they can be
    // released.  Otherwise, this collection will be null when the finalizer is called.
    private Map<Object, Map<QName, EndpointDescriptionImpl>> dynamicEndpointDescriptions =
                new HashMap<Object, Map<QName, EndpointDescriptionImpl>>();

    // Cache classes for the info for resolved handlers
    private SoftReference<Map<PortInfo, ResolvedHandlersDescription>> resolvedHandlersDescription =
            new SoftReference<Map<PortInfo, ResolvedHandlersDescription>>
        (new ConcurrentHashMap<PortInfo, ResolvedHandlersDescription>());
    
    private static final Log log = LogFactory.getLog(ServiceDescriptionImpl.class);

    private HashMap<String, DescriptionBuilderComposite> dbcMap = null;

    private DescriptionBuilderComposite composite = null;
    private boolean isServerSide = false;

    // Allow a unique XML CatalogManager per service description.
    private JAXWSCatalogManager catalogManager = null;
    
    // RUNTIME INFORMATION
    Map<String, ServiceRuntimeDescription> runtimeDescMap =
            new ConcurrentHashMap<String, ServiceRuntimeDescription>();
    private static final String JAXWS_DYNAMIC_ENDPOINTS = "jaxws.dynamic.endpoints";

    /**
     * Create a service-requester side (aka client-side) service description.
     * Construct a service description hierachy
     * based on WSDL (may be null), the Service class, and a service QName.
     *
     * @param wsdlURL      The WSDL file (this may be null).
     * @param serviceQName The name of the service in the WSDL.  This can not be null since a
     *                     javax.xml.ws.Service can not be created with a null service QName.
     * @param serviceClass The JAX-WS service class.  This could be an instance of
     *                     javax.xml.ws.Service or a generated service subclass thereof.  This will
     *                     not be null.
     */
    ServiceDescriptionImpl(URL wsdlURL, QName serviceQName, Class serviceClass) {
        this(wsdlURL, serviceQName, serviceClass, null, null);
    }
    
    /**
     * Create a service-requester side service description.  Same as constructor above with an 
     * additonal composite paramater.  Note that the composite is "sparse" in that any values it
     * contains overrides any values on the corresponding class annotation HOWEVER if the composite
     * doesn't specify a value, it is gotten from the class annotation. 
     * @param wsdlURL
     * @param serviceQName
     * @param serviceClass
     * @param sparseComposite a composite with any annotation overrides such as from a client deployment
     *     descriptor.  CAN NOT BE NULL, but it can be an object created with a default constructor
     */
    ServiceDescriptionImpl(URL wsdlURL, QName serviceQName, Class serviceClass, 
                           DescriptionBuilderComposite sparseComposite,
                           Object sparseCompositeKey) {
        if (log.isDebugEnabled()) {
            log.debug("ServiceDescriptionImpl(URL,QName,Class,DescriptionBuilderComposite,Object)");
        }
    	
    	if (sparseComposite != null) {
    	    catalogManager = sparseComposite.getCatalogManager();
        }
    	if (catalogManager == null) {
    		catalogManager = new OASISCatalogManager();
        }
    	
        if (serviceQName == null) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDescErr0"));
        }
        if (serviceClass == null) {
            throw ExceptionFactory
                    .makeWebServiceException(Messages.getMessage("serviceDescErr1", "null"));
        }
        if (!javax.xml.ws.Service.class.isAssignableFrom(serviceClass)) {
            throw ExceptionFactory.makeWebServiceException(
                    Messages.getMessage("serviceDescErr1", serviceClass.getName()));
        }

        composite = new DescriptionBuilderComposite();
        composite.setIsServiceProvider(false);
        composite.setCorrespondingClass(serviceClass);
        // The classloader was originally gotten off this class, but it seems more logical to 
        // get it off the application service class.
//        composite.setClassLoader(this.getClass().getClassLoader());
        composite.setClassLoader(getClassLoader(serviceClass));
        composite.setSparseComposite(sparseCompositeKey, sparseComposite);
        
        // If there's a WSDL URL specified in the sparse composite, that is a override, for example
        // from a JSR-109 deployment descriptor, and that's the one to use.
        URL sparseCompositeWsdlURL = getSparseCompositeWsdlURL(sparseComposite);
        if (sparseCompositeWsdlURL != null) {
            if (log.isDebugEnabled()) {
                log.debug("Wsdl location overriden by sparse composite; overriden value: " + this.wsdlURL);
            }
            this.wsdlURL = sparseCompositeWsdlURL.toString();
        } else {
            this.wsdlURL = wsdlURL == null ? null : wsdlURL.toString();
        }
        if (log.isDebugEnabled()) {
            log.debug("Wsdl Location value used: " + this.wsdlURL);
        }
        // TODO: On the client side, we should not support partial WSDL; i.e. if the WSDL is specified it must be
        //       complete and must contain the ServiceQName.  This is how the Sun RI behaves on the client.
        //       When this is fixed, the check in ServiceDelegate(URL, QName, Class) should be removed
        
        // TODO: The serviceQName needs to be verified between the argument/WSDL/Annotation
        this.serviceQName = serviceQName;

        setupWsdlDefinition();
    }
    
    URL getSparseCompositeWsdlURL(DescriptionBuilderComposite sparseComposite) {
        // Use the WSDL file if it is specified in the composite
        URL url = null;
        if (sparseComposite != null) {
            WebServiceClient wsc = (WebServiceClient) sparseComposite.getWebServiceClientAnnot();
            if (wsc != null && wsc.wsdlLocation() != null) {
                String wsdlLocation = wsc.wsdlLocation();
                URL wsdlUrl = getWSDLURL(wsdlLocation);
                
                if (wsdlUrl == null) {
                    throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDescErr4", wsdlLocation));
                } else {
                    url = wsdlUrl;
                }
            }
        }
        return url;
    }

    private static URL createWsdlURL(String wsdlLocation) {
        URL theUrl = null;
        try {
            theUrl = new URL(wsdlLocation);
        } catch (Exception ex) {
            // Just return a null to indicate we couldn't create a URL from the string
            if (log.isDebugEnabled()) {
                log.debug("Unable to obtain URL for WSDL file: " + wsdlLocation
                        + " by using File reference");
            }
        }
        return theUrl;
    }

    ServiceDescriptionImpl(
                           HashMap<String, DescriptionBuilderComposite> dbcMap,
                           DescriptionBuilderComposite composite) {
        this(dbcMap, composite, null);
    }
    
    ServiceDescriptionImpl(HashMap<String, DescriptionBuilderComposite> dbcMap,
                           DescriptionBuilderComposite composite,
                           ConfigurationContext configContext) {
        this(dbcMap, composite, configContext, null);
    }
    
    /**
     * Create a service-provider side Service description hierachy.  The hierachy is created entirely
     * from composite.  All relevant classes and interfaces referenced from the class represented by
     * composite must be included in the map.
     * @param dbcMap
     * @param composite
     */
    ServiceDescriptionImpl(
            HashMap<String, DescriptionBuilderComposite> dbcMap,
            DescriptionBuilderComposite composite, 
            ConfigurationContext configContext, 
            QName serviceQName) {
        this.composite = composite;
        
        this.configContext = configContext;
        if (log.isDebugEnabled()) {
            log.debug("ServiceDescriptionImpl(HashMap<String,DescriptionBuilderComposite>,ConfigurationContext)");
        }

        String serviceImplName = this.composite.getClassName();

        this.dbcMap = dbcMap;
        this.isServerSide = true;
        this.serviceQName = serviceQName;
        this.catalogManager = this.composite.getCatalogManager();
        
        
        // if the ServiceDescriptionImpl was constructed with a specific service QName
        // we should use that to retrieve the potential list of PortComposite objects
        List<PortComposite> portComposites = null;
        
        if(this.serviceQName != null) {
            portComposites = composite.getPortComposites(this.serviceQName);
        }
        else {
            portComposites = composite.getPortComposites();
        }
        
        
        
        //capture the WSDL, if there is any...to be used for later processing
        setupWsdlDefinition();

        // Do a first pass validation for this DescriptionBuilderComposite.
        // This is not intended to be a full integrity check, but rather a fail-fast mechanism
        validateDBCLIntegrity();

        // The ServiceQName instance variable is set based on annotation or default
        // It will be set by the EndpointDescriptionImpl since it is the one that knows
        // how to process the annotations and the defaults.

        
        
        // If PortComposite instances were specified on the DBC we are currently processing
        // we want to switch the context of processing to the PortComposites
        if(portComposites != null
                &&
                !portComposites.isEmpty()) {
            if(log.isDebugEnabled()) {
                log.debug("Constructing EndpointDescription instance from PortComposites for " +
                		"implementation class: " + composite.getClassName());
            }
            int i = 0;
            for(PortComposite portComposite : portComposites) {
                
                // get the properties from the SEI and the Impl
                Map<String, Object> props = getAllProps(portComposite, dbcMap);
                
                // here we pass in an index so the EndpointDescriptionImpl instance will know
                // which PortComposite instance it is associated with and can retrieve that 
                // instance as required
                EndpointDescriptionImpl endpointDescription =
                    new EndpointDescriptionImpl(this, serviceImplName, props, i);
                addEndpointDescription(endpointDescription);
                i++;
            }
        }
        
        // If no PortComposites then we build the EndpointDescriptionImpl instance from the
        // DBC that was supplied
        else {
            if(log.isDebugEnabled()) {
                log.debug("No PortComposites found for implementation class: " + composite.getClassName());
            }
            
                // get the properties from the SEI and the Impl
                Map<String, Object> props = getAllProps(composite, dbcMap);
                
                // Create the single EndpointDescription hierachy from the service impl annotations; Since the PortQName is null, 
        	// it will be set to the annotation value.
        	EndpointDescriptionImpl endpointDescription =
                	new EndpointDescriptionImpl(this, serviceImplName, props, null);
        	addEndpointDescription(endpointDescription);
        }
    }

    /*=======================================================================*/
    /*=======================================================================*/
    // START of public accessor methods

    /**
     * Update or create an EndpointDescription. Updates to existing EndpointDescriptons will be
     * based on the SEI class and its annotations.  Both declared ports and dynamic ports can be
     * updated.  A declared port is one that is defined (e.g. in WSDL or via annotations); a dyamic
     * port is one that is not defined (e.g. not via WSDL or annotations) and has been added via
     * Serivce.addPort.
     * <p/>
     * For predefined ports, a composite of sparse metadata, such as from a deployment descriptor
     * may be supplied.  This can be used to modify the endpoint interfaceannotations such as
     * adding a handler chain.  The sparse composite is NOT supported for dynamic (i.e. ADD_PORT)
     * or dispatch clients. 
     * <p/>
     * Notes on how an EndpointDescription can be updated or created: 1) Service.createDispatch can
     * create a Dispatch client for either a declared or dynamic port 2) Note that creating a
     * Dispatch does not associate an SEI with an endpoint 3) Service.getPort will associate an SEI
     * with a port 4) A getPort on an endpoint which was originally created for a Distpatch will
     * update that EndpointDescription with the SEI provided on the getPort 5) Service.getPort can
     * not be called on a dynamic port (per the JAX-WS spec) 6) Service.addPort can not be called
     * for a declared port
     *
     * @param sei        This will be non-null if the update is of type GET_PORT; it will be null if
     *                   the update is ADD_PORT or CREATE_DISPATCH
     * @param portQName
     * @param updateType Indicates what is causing the update GET_PORT is an attempt to get a
     *                   declared SEI-based port ADD_PORT is an attempt to add a previously
     *                   non-existent dynamic port CREATE_DISPATCH is an attempt to create a
     *                   Dispatch-based client to either a declared port or a pre-existing dynamic
     *                   port.
     * @param composite  May contain sparse metadata, for example from a deployment descriptor, that
     *                   should be used in conjunction with the class annotations to update the
     *                   description hierarchy.  For example, it may contain a HandlerChain annotation
     *                   based on information in a JSR-109 deployment descriptor.                    
     */

    EndpointDescription updateEndpointDescription(Class sei, 
                                                  QName portQName,
                                                  DescriptionFactory.UpdateType updateType,
                                                  DescriptionBuilderComposite composite,
                                                  Object serviceDelegateKey,
                                                  String bindingId,
                                                  String endpointAddress) {

	
    	EndpointDescriptionImpl endpointDescription = getEndpointDescriptionImpl(portQName);
    	boolean isPortDeclared = isPortDeclared(portQName);

    	// If a defined endpointDescription is not available, try and locate a dynamic endpoint.
    	// Note that a dynamic port will only be found for the client that created it, per the
    	// serviceDelegateKey

    	if (endpointDescription == null && serviceDelegateKey != null) {
    		endpointDescription = getDynamicEndpointDescriptionImpl(portQName, serviceDelegateKey);
    	}

        // If no QName was specified in the arguments, one may have been specified in the sparse
        // composite metadata when the service was created.
        if (DescriptionUtils.isEmpty(portQName)) {
            QName preferredPortQN = getPreferredPort(serviceDelegateKey);
            if (!DescriptionUtils.isEmpty(preferredPortQN)) {
                portQName = preferredPortQN;
            }
        }

        switch (updateType) {

            case ADD_PORT:
                if (composite != null) {
                    throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDescErr5", portQName.toString()));
                }
                // Port must NOT be declared (e.g. can not already exist in WSDL)
                // If an EndpointDesc doesn't exist; create it as long as it doesn't exist in the WSDL
                if (DescriptionUtils.isEmpty(portQName)) {
                    throw ExceptionFactory
                            .makeWebServiceException(Messages.getMessage("addPortErr2"));
                }
                if (getWSDLWrapper() != null && isPortDeclared) {
                    throw ExceptionFactory.makeWebServiceException(
                            Messages.getMessage("addPortDup", portQName.toString()));
                } else if (endpointDescription == null) {
                    // Use the SEI Class and its annotations to finish creating the Description hierarchy.  Note that EndpointInterface, Operations, Parameters, etc.
                    // are not created for dynamic ports.  It would be an error to later do a getPort against a dynamic port (per the JAX-WS spec)
                    // If we can't add the dynamic port under a specific service delegate, that is an error

                    if (serviceDelegateKey == null) {
                        throw ExceptionFactory.makeWebServiceException(
                                 Messages.getMessage("serviceDescriptionImplAddPortErr"));
                    }

                    endpointDescription = createEndpointDescriptionImpl(sei, portQName, bindingId, endpointAddress);    
                    addDynamicEndpointDescriptionImpl(endpointDescription, serviceDelegateKey);

                } else {
                    // All error chJeck above passed, the EndpointDescription already exists and needs no updating
                }
                break;

            case GET_PORT:
                
                // try to find existing endpointDesc by SEI class if portQName was not specified 
                if (endpointDescription == null && portQName == null && sei != null) {
                    endpointDescription = getEndpointDescriptionImpl(sei);
                }
                
                // If an endpointDesc doesn't exist, and the port exists in the WSDL, create it
                // If an endpointDesc already exists and has an associated SEI already, make sure they match
                // If an endpointDesc already exists and was created for Dispatch (no SEI), update that with the SEI provided on the getPort

                // Port must be declared (e.g. in WSDL or via annotations)
                // TODO: Once isPortDeclared understands annotations and not just WSDL, the 2nd part of this check can possibly be removed.
                //       Although consider the check below that updates an existing EndpointDescritpion with an SEI.
                if (!isPortDeclared ||
                        (endpointDescription != null && endpointDescription.isDynamicPort())) {
                    // This guards against the case where an addPort was done previously and now a getPort is done on it.
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("updateEPDescrErr1",(portQName != null ? portQName.toString() : "not specified")));
                } else if (sei == null) {
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("updateEPDescrErr2",(portQName != null ? portQName.toString() : "not specified")));
                } else if (endpointDescription == null) {
                    // Use the SEI Class and its annotations to finish creating the Description hierachy: Endpoint, EndpointInterface, Operations, Parameters, etc.
                    endpointDescription = new EndpointDescriptionImpl(sei, portQName, this, composite, serviceDelegateKey);
                    addEndpointDescription(endpointDescription);
                    /*
                     * We must reset the service runtime description after adding a new endpoint
                     * since the runtime description information could have references to old
                     * information. See MarshalServiceRuntimeDescriptionFactory and 
                     * MarshalServiceRuntimeDescription.
                     */
                    resetServiceRuntimeDescription();
                } else
                if (getEndpointSEI(portQName) == null && !endpointDescription.isDynamicPort()) {
                    // Existing endpointDesc from a declared port needs to be updated with an SEI.
                    // This could happen if the client first did a CREATE_DISPATCH on a declared
                    // port, which would cause it to be created, and then did a GET_PORT on the 
                    // same port later, providing an SEI.
                    // Notes 
                    // 1) An EndpointDescritption created from an addPort (i.e. a dynamic port) can 
                    //    not do this.
                    // 2) A sparse composite may be specified.  We don't allow mixing JAX-WS unmanaged
                    //    apis (CREATE_DISPATCH) with JSR-109 managed apis (GET_PORT with sparse
                    //    composite metadata from a DD).  Since the sparse composite is stored by
                    //    a key AND CREATE_DISPATCH and ADD_PORT will thrown an exception of a composite
                    //    is specified, having a composite and key on the GET_PORTs shouldn't be
                    //    a problem.
                    endpointDescription.updateWithSEI(sei, composite, serviceDelegateKey);
                } else if (getEndpointSEI(portQName) != sei) {
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("updateEPDescrErr3",portQName.toString(),
                    				sei.getName(),getEndpointSEI(portQName).getName()));
                } else {
                    // All error check above passed, the EndpointDescription already exists and needs no updating
                    // Just add the sparse composite if one was specified.
                    endpointDescription.getDescriptionBuilderComposite().setSparseComposite(serviceDelegateKey, composite);
                }
                break;

            case CREATE_DISPATCH:
                if (composite != null) {
                    throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDescErr6"));
                }
                
                // Port may or may not exist in WSDL.
                // If an endpointDesc doesn't exist and it is in the WSDL, it can be created
                // Otherwise, it is an error.
                if (DescriptionUtils.isEmpty(portQName)) {
                    throw ExceptionFactory
                            .makeWebServiceException(Messages.getMessage("createDispatchFail0"));
                } else if (endpointDescription != null) {
                    // The EndpointDescription already exists; nothing needs to be done
                } else if (sei != null) {
                    // The Dispatch should not have an SEI associated with it on the update call.
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("createDispatchFail3",portQName.toString()));
                } else if (getWSDLWrapper() != null && isPortDeclared) {
                    // EndpointDescription doesn't exist and this is a declared Port, so create one
                    // Use the SEI Class and its annotations to finish creating the Description hierarchy.  
                    // Note that EndpointInterface, Operations, Parameters, etc. are not created for 
                    // Dispatch-based ports, but might be updated later if a getPort is done against 
                    // the same declared port.
                    endpointDescription = new EndpointDescriptionImpl(sei, portQName, this);
                    addEndpointDescription(endpointDescription);
                } else {
                    // The port is not a declared port and it does not have an EndpointDescription, 
                    // meaning an addPort has not been done for it
                    // This is an error.
                    throw ExceptionFactory.makeWebServiceException(
                            Messages.getMessage("createDispatchFail1", portQName.toString()));
                }
                break;
        }
        return endpointDescription;
    }

    private EndpointDescriptionImpl createEndpointDescriptionImpl(Class sei, QName portQName, String bindingId, String endpointAddress) {
        if (log.isDebugEnabled()) {
            log.debug("Calling createEndpointDescriptionImpl : ("
                      + portQName + "," + bindingId + "," + endpointAddress + ")");
        }
        EndpointDescriptionImpl endpointDescription = null;
        AxisConfiguration configuration = configContext.getAxisConfiguration();
        if (log.isDebugEnabled()) {
            log.debug("looking for " + JAXWS_DYNAMIC_ENDPOINTS + " in AxisConfiguration : " + configuration);
        }
        Parameter parameter = configuration.getParameter(JAXWS_DYNAMIC_ENDPOINTS);
        HashMap cachedDescriptions = (HashMap)
                ((parameter == null) ? null : parameter.getValue());
        if(cachedDescriptions == null) {
            cachedDescriptions = new HashMap();
            try {
                configuration.addParameter(JAXWS_DYNAMIC_ENDPOINTS, cachedDescriptions);
            } catch (AxisFault axisFault) {
                throw new RuntimeException(axisFault);
            }
            if (log.isDebugEnabled()) {
                log.debug("Added new instance of cachedDescriptions : " + cachedDescriptions);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("found old jaxws.dynamic.endpoints cache in AxisConfiguration ("  +  cachedDescriptions + ") with size : ("
                          + cachedDescriptions.size() + ")");
            }
        }

        StringBuffer key = new StringBuffer();
        key.append(portQName == null ? "NULL" : portQName.toString());
        key.append(':');
        key.append(bindingId == null ? "NULL" : bindingId);
        key.append(':');
        key.append(endpointAddress == null ? "NULL" : endpointAddress);
        synchronized(cachedDescriptions) {
            WeakReference ref = (WeakReference) cachedDescriptions.get(key.toString());
            if (ref != null) {
                endpointDescription = (EndpointDescriptionImpl) ref.get();
            }
        }
        if(endpointDescription == null) {
            endpointDescription = new EndpointDescriptionImpl(sei, portQName, true, this);
            synchronized(cachedDescriptions) {
                if (log.isDebugEnabled()) {
                    log.debug("Calling cachedDescriptions.put : ("
                              + key.toString() + ") : size - " + cachedDescriptions.size());
                }
                cachedDescriptions.put(key.toString(), new WeakReference(endpointDescription));
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("found old entry for endpointDescription in jaxws.dynamic.endpoints cache : ("
                          + cachedDescriptions.size() + ")");
            }
        }
        return endpointDescription;
    }
    
    /**
     * This method will get all properties that have been set on the DescriptionBuilderComposite
     * instance. If the DBC represents an implementation class that references an SEI, the
     * properties from the SEI will also be merged with the returned result.
     */
    private Map<String, Object> getAllProps(DescriptionBuilderComposite dbc, 
                                            Map<String, DescriptionBuilderComposite> dbcMap) {
        Map<String, Object> props = new HashMap<String, Object>();
        
        // first get the properties from the DBC
        if(dbc.getProperties() != null
                &&
                !dbc.getProperties().isEmpty()) {
            props.putAll(dbc.getProperties());
        }
        
        // no need to continue if the 'dbcMap' is null
        if(dbcMap != null) {
            
         // now if this is a web service impl with an SEI, get the SEI props
            if(dbc.getWebServiceAnnot() != null
                    &&
                    !DescriptionUtils.isEmpty(dbc.getWebServiceAnnot().endpointInterface())) {
                DescriptionBuilderComposite seiDBC = dbcMap.get(dbc.getWebServiceAnnot().endpointInterface());
                if(seiDBC != null
                        &&
                        seiDBC.getProperties() != null
                        &&
                        !seiDBC.getProperties().isEmpty()) {
                    props.putAll(seiDBC.getProperties());
                }
            }
        }
        
        return props;
    }

    private Class getEndpointSEI(QName portQName) {
        Class endpointSEI = null;
        EndpointDescription endpointDesc = getEndpointDescription(portQName);
        if (endpointDesc != null) {
            EndpointInterfaceDescription endpointInterfaceDesc = 
                endpointDesc.getEndpointInterfaceDescription();
            if (endpointInterfaceDesc != null ) {
                endpointSEI = endpointInterfaceDesc.getSEIClass();
            }
        }
        return endpointSEI;
    }

    private boolean isPortDeclared(QName portQName) {
        // TODO: This needs to account for declaration of the port via annotations in addition to just WSDL
        // TODO: Add logic to check the portQN namespace against the WSDL Definition NS
        boolean portIsDeclared = false;
        if (!DescriptionUtils.isEmpty(portQName)) {
            if (getWSDLWrapper() != null) {
                Definition wsdlDefn = getWSDLWrapper().getDefinition();
                Service wsdlService = wsdlDefn.getService(serviceQName);
                Port wsdlPort = wsdlService.getPort(portQName.getLocalPart());
                portIsDeclared = (wsdlPort != null);
            } else {
                // TODO: Add logic to determine if port is declared via annotations when no WSDL is present.  For now, we have to assume it is declared 
                // so getPort(...) and createDispatch(...) calls work when there is no WSDL.
                portIsDeclared = true;
            }
        } else {
            // PortQName is null, so the runtime gets to choose which one to use.  Since there's no WSDL
            // we'll use annotations, so it is implicitly declared
            portIsDeclared = true;
        }
        return portIsDeclared;
    }

    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescriptions()
    */
    public EndpointDescription[] getEndpointDescriptions() {
        return definedEndpointDescriptions.values().toArray(new EndpointDescriptionImpl[0]);
    }

    public Collection<EndpointDescriptionImpl> getDynamicEndpointDescriptions_AsCollection(Object serviceDelegateKey) {
        Collection <EndpointDescriptionImpl> dynamicEndpoints = null;
    	if (serviceDelegateKey != null ) {
    		if (dynamicEndpointDescriptions.get(serviceDelegateKey) != null)
    			dynamicEndpoints = dynamicEndpointDescriptions.get(serviceDelegateKey).values();
        }
    	return dynamicEndpoints;
    }

    public Collection<EndpointDescription> getEndpointDescriptions_AsCollection() {
    	return definedEndpointDescriptions.values();
    }

    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescription(javax.xml.namespace.QName)
    */
    public EndpointDescription getEndpointDescription(QName portQName) {

    	return getEndpointDescription(portQName, null);
    }

    public EndpointDescription getEndpointDescription(QName portQName, Object serviceDelegateKey) {
        EndpointDescription returnDesc = null;
        if (!DescriptionUtils.isEmpty(portQName)) {
    		returnDesc = definedEndpointDescriptions.get(portQName);

    		if (returnDesc == null && serviceDelegateKey != null) {
		           returnDesc = getDynamicEndpointDescriptionImpl(portQName, serviceDelegateKey);
    		}    		        	
        }
        return returnDesc;
    }

    EndpointDescriptionImpl getEndpointDescriptionImpl(QName portQName) {
        return (EndpointDescriptionImpl)getEndpointDescription(portQName, null);
    }
    
    EndpointDescriptionImpl getEndpointDescriptionImpl(QName portQName, Object serviceDelegateKey) {
        return (EndpointDescriptionImpl)getEndpointDescription(portQName);
    }
    
    EndpointDescriptionImpl getEndpointDescriptionImpl(Class seiClass) {
        for (EndpointDescription endpointDescription : definedEndpointDescriptions.values()) {
            EndpointInterfaceDescription endpointInterfaceDesc =
                    endpointDescription.getEndpointInterfaceDescription();
            // Note that Dispatch endpoints will not have an endpointInterface because the do not have an associated SEI
            if (endpointInterfaceDesc != null) {
                Class endpointSEIClass = endpointInterfaceDesc.getSEIClass();
                if (endpointSEIClass != null && endpointSEIClass.equals(seiClass)) {
                    return (EndpointDescriptionImpl)endpointDescription;
                }
            }
        }
        return null;
    }
    
    public DescriptionBuilderComposite getDescriptionBuilderComposite() {
        return getDescriptionBuilderComposite(null, null);
    }

    /**
     * This method provides a means for accessing a DescriptionBuilderComposite instance.
     * If the integer value passed in is an index in the list of PortComposite objects, 
     * then the indiciated PortComposite will be returned. Otherwise, the instance DBC
     * will be returned.
     */
    public DescriptionBuilderComposite getDescriptionBuilderComposite(QName serviceQName, 
                                                                      Integer portCompositeIndex) {
        
        DescriptionBuilderComposite dbc = null;
        
        // if the service QName was specified let's attempt to get the correct 
        // PortComposite list
        if(serviceQName != null
                &&
                composite.getServiceQNames() != null
                &&
                !composite.getServiceQNames().isEmpty()
                &&
                portCompositeIndex != null) {
            List<PortComposite> pcList = composite.getPortComposites(serviceQName);
            if(pcList != null) {
                dbc = pcList.get(portCompositeIndex);
            }
            else {
                dbc = composite;
            }
        }
        
        // ignore null values or values that would cause an IndexOutOfBoundsException
        else if(portCompositeIndex == null 
                || 
                composite.getPortComposites() == null
                ||
                portCompositeIndex < 0
                ||
                portCompositeIndex >= composite.getPortComposites().size()) {
            dbc = composite;  
        }
        
        // return the appropriate PortComposite instance
        else {
            if(log.isDebugEnabled()) {
                log.debug("Returning PortComposite at index: " + portCompositeIndex + 
                          " from ServiceDescriptionImpl: " + this.hashCode());
            }
            dbc = composite.getPortComposites().get(portCompositeIndex);
        }
        
        return dbc;
    }

    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescription(java.lang.Class)
     */
    public EndpointDescription[] getEndpointDescription(Class seiClass) {
        EndpointDescription[] returnEndpointDesc = null;
        ArrayList<EndpointDescriptionImpl> matchingEndpoints =
                new ArrayList<EndpointDescriptionImpl>();
        for (EndpointDescription endpointDescription : definedEndpointDescriptions.values()) {
            EndpointInterfaceDescription endpointInterfaceDesc =
                    endpointDescription.getEndpointInterfaceDescription();
            // Note that Dispatch endpoints will not have an endpointInterface because the do not have an associated SEI
            if (endpointInterfaceDesc != null) {
                Class endpointSEIClass = endpointInterfaceDesc.getSEIClass();
                if (endpointSEIClass != null && endpointSEIClass.equals(seiClass)) {
                    matchingEndpoints.add((EndpointDescriptionImpl)endpointDescription);
                }
            }
        }
        if (matchingEndpoints.size() > 0) {
            returnEndpointDesc = matchingEndpoints.toArray(new EndpointDescriptionImpl[0]);
        }
        return returnEndpointDesc;
    }

    // END of public accessor methods
    /*=======================================================================*/
    /*=======================================================================*/
    private void addEndpointDescription(EndpointDescriptionImpl endpoint) {
    	definedEndpointDescriptions.put(endpoint.getPortQName(), endpoint);
    }

    private void setupWsdlDefinition() {
        // Note that there may be no WSDL provided, for example when called from 
        // Service.create(QName serviceName).

        if (log.isDebugEnabled()) {
            log.debug("setupWsdlDefinition()");
        }
        
        if (composite.isServiceProvider()) {
            
            //  Currently, there is a bug which allows the wsdlDefinition to be placed
            //  on either the impl class composite or the sei composite, or both. We need to
            //  look in both places and find the correct one, if it exists.
            
            if(serviceQName != null
                    &&
                    composite.getWsdlDefinition(serviceQName) != null) {
                if(log.isDebugEnabled()) {
                    log.debug("Found WSDL definition by service QName");
                }
                Definition def = composite.getWsdlDefinition(serviceQName);
                URL url = composite.getWsdlURL(serviceQName);
                this.wsdlURL = url != null ? url.toString() : null;
                try {
                    if (log.isDebugEnabled() ) {
                        if (configContext != null) {
                            log.debug("new WSDL4JWrapper-ConfigContext not null1"); 
                        } else {
                            log.debug("new WSDL4JWrapper-ConfigContext null1"); 
                        }
                    }

                    this.wsdlWrapper = new WSDL4JWrapper(url,
                                                         def, 
                                                         configContext,
                                                         this.catalogManager);
                } catch (WSDLException e) {
                    throw ExceptionFactory.makeWebServiceException(
                            Messages.getMessage("wsdlException", e.getMessage()), e);
                }
            }

            else if (((composite.getWebServiceAnnot() != null) &&
                    DescriptionUtils.isEmpty(composite.getWebServiceAnnot().endpointInterface()))
                    ||
                    (!(composite.getWebServiceProviderAnnot() == null))) {
                //This is either an implicit SEI, or a WebService Provider
                if (composite.getWsdlDefinition() != null) {
                    URL url = composite.getWsdlURL();
                    this.wsdlURL = url == null ? null : url.toString();

                    try {
                        if (log.isDebugEnabled() ) {
                            if (configContext != null) {
                                log.debug("new WSDL4JWrapper-ConfigContext not null1"); 
                            } else {
                                log.debug("new WSDL4JWrapper-ConfigContext null1"); 
                            }
                        }

                        this.wsdlWrapper = new WSDL4JWrapper(url,
                                                             composite.getWsdlDefinition(), 
                                                             configContext,
                                                             this.catalogManager);
                    } catch (WSDLException e) {
                        throw ExceptionFactory.makeWebServiceException(
                                Messages.getMessage("wsdlException", e.getMessage()), e);
                    }
                } else {
                	String wsdlLocation = null;
                	wsdlLocation = composite.getWebServiceAnnot() != null ? composite.getWebServiceAnnot().wsdlLocation() :
                		composite.getWebServiceProviderAnnot().wsdlLocation();
                	if(wsdlLocation != null
                			&&
                			!"".equals(wsdlLocation)) {
                	    setWSDLDefinitionOnDBC(wsdlLocation);
                	}
                }

            } else if (composite.getWebServiceAnnot() != null) {
                //This impl class specifies an SEI...this is a special case. There is a bug
                //in the tooling that allows for the wsdllocation to be specifed on either the
                //impl. class, or the SEI, or both. So, we need to look for the wsdl as follows:
                //          1. If the Wsdl exists on the SEI, then check for it on the impl.
                //          2. If it is not found in either location, in that order, then generate

                DescriptionBuilderComposite seic =
                        getDBCMap().get(composite.getWebServiceAnnot().endpointInterface());

                try {
                    if (seic == null) {
                        if (log.isDebugEnabled()) {
                            log.debug("The SEI class " + composite.getWebServiceAnnot().endpointInterface() + " was not found.");
                        }
                    }
                    if (seic != null && seic.getWsdlDefinition() != null) {
                        // set the wsdl def from the SEI composite
                        if (log.isDebugEnabled()) {
                            log.debug("Get the wsdl definition from the SEI composite.");
                        }
                        URL url = seic.getWsdlURL(); 
                        this.wsdlURL = url.toString();
                        if (log.isDebugEnabled() ) {
                            if (configContext != null) {
                                log.debug("new WSDL4JWrapper-ConfigContext not null2"); 
                            } else {
                                log.debug("new WSDL4JWrapper-ConfigContext null2"); 
                            }
                        }
                        this.wsdlWrapper =
                                new WSDL4JWrapper(seic.getWsdlURL(), 
                                                  seic.getWsdlDefinition(), 
                                                  configContext,
                                                  this.catalogManager);
                            
                    } else if (composite.getWsdlDefinition() != null) {
                        
                        //set the wsdl def from the impl. class composite
                        if (log.isDebugEnabled()) {
                            log.debug("Get the wsdl definition from the impl class composite.");
                        }
                        if (log.isDebugEnabled() ) {
                            if (configContext != null) {
                                log.debug("new WSDL4JWrapper-ConfigContext not null3"); 
                            } else {
                                log.debug("new WSDL4JWrapper-ConfigContext null3"); 
                            }
                        }
                        URL url = composite.getWsdlURL();
                        this.wsdlURL = url == null ? null : url.toString();
                        this.wsdlWrapper = new WSDL4JWrapper(composite.getWsdlURL(),
                                                             composite.getWsdlDefinition(), 
                                                             configContext,
                                                             this.catalogManager);
                                                            
                    } else {
                    	String wsdlLocation = null;
                    	// first check to see if the wsdlLocation is on the SEI
                    	if(seic != null
                    			&&
                    			seic.getWebServiceAnnot() != null) {
                    	    if (log.isDebugEnabled()) {
                    	        log.debug("Get the wsdl location from the SEI composite.");
                    	    }
                    	    wsdlLocation = seic.getWebServiceAnnot().wsdlLocation();
                    	}
                    	                    	
                    	// now check the impl
                    	if(wsdlLocation == null
                    	        ||
                    	        "".equals(wsdlLocation)) {
                    	    if (log.isDebugEnabled()) {
                    	        log.debug("Get the wsdl location from the impl class composite.");
                            }
                    	    wsdlLocation = composite.getWebServiceAnnot().wsdlLocation();
                    	}
                    	
                    	if(wsdlLocation != null
                    	        &&
                    	        !"".equals(wsdlLocation)) {
                    	    if (log.isDebugEnabled()) {
                    	        log.debug("wsdl location =" + wsdlLocation);
                    	    }
                    	    
                    	    this.wsdlURL = wsdlLocation;
                    	    setWSDLDefinitionOnDBC(wsdlLocation);
                    	}
                    }
                } catch (WSDLException e) {
                    throw ExceptionFactory.makeWebServiceException(
                            Messages.getMessage("wsdlException", e.getMessage()), e);
                }
            }

            //Deprecate this code block when MDQ is fully integrated
        } else if (wsdlURL != null) {
            try {
                if (log.isDebugEnabled() ) {
                    if (configContext != null) {
                        log.debug("new WSDL4JWrapper-ConfigContext not null4"); 
                    } else {
                        log.debug("new WSDL4JWrapper-ConfigContext null4"); 
                    }
                }
                this.wsdlWrapper = new WSDL4JWrapper(new URL(this.wsdlURL),configContext,
                		                             this.catalogManager);
             
            }
            catch (FileNotFoundException e) {
                throw ExceptionFactory.makeWebServiceException(
                        Messages.getMessage("wsdlNotFoundErr", e.getMessage()), e);
            }
            catch (UnknownHostException e) {
                throw ExceptionFactory.makeWebServiceException(
                        Messages.getMessage("unknownHost", e.getMessage()), e);
            }
            catch (ConnectException e) {
                throw ExceptionFactory.makeWebServiceException(
                        Messages.getMessage("connectionRefused", e.getMessage()), e);
            }
            catch(IOException e) {
                throw ExceptionFactory.makeWebServiceException(Messages.getMessage("urlStream", e.getMessage()), e);
            }
            catch (WSDLException e) {
                throw ExceptionFactory.makeWebServiceException(
                        Messages.getMessage("wsdlException", e.getMessage()), e);
            }
        }
    }
    
    /**
     * This method accepts a location of a WSDL document and attempts to
     * load and set the WSDL definition on the DBC object.
     * @param wsdlLocation
     */
    private void setWSDLDefinitionOnDBC(String wsdlLocation) {
    	if(log.isDebugEnabled()) {
			log.debug("Attempting to load WSDL file from location specified in annotation: " +
					wsdlLocation);
		}
		if(composite.getClassLoader() == null) {
			if(log.isDebugEnabled()) {
				log.debug("A classloader could not be found for class: " + composite.
						getClassName() + ". The WSDL file: " + wsdlLocation + " will not be " +
								"processed, and the ServiceDescription will be built from " +
								"annotations");
			}
		}
		try {
		    if(log.isDebugEnabled()) {
		        log.debug("Attempting to read WSDL: " + wsdlLocation + " for web " +
		        		"service endpoint: " + composite.getClassName());
		    }
                    if (log.isDebugEnabled() ) {
                        if (configContext != null) {
                            log.debug("new WSDL4JWrapper-ConfigContext not null5");
                        } else {
                            log.debug("new WSDL4JWrapper-ConfigContext null5");
                        }
                    }

                    URL url = getWSDLURL(wsdlLocation);
                    ConfigurationContext cc = composite.getConfigurationContext();
                    if (cc != null) {
                        this.wsdlWrapper = new WSDL4JWrapper(url, cc, this.catalogManager);
                    } else {
                        // Probably shouldn't get here.  But if we do, use a memory sensitive
                        // wsdl wrapper
                        this.wsdlWrapper = new WSDL4JWrapper(url, this.catalogManager, true, 2);
                    }
                    composite.setWsdlDefinition(wsdlWrapper.getDefinition());
		}
		catch(Exception e) {
			if(log.isDebugEnabled()) {
				log.debug("The WSDL file: " + wsdlLocation + " for class: " + composite.getClassName() + 
						"could not be processed. The ServiceDescription will be built from " +
								"annotations");
			}
		}
    }
    
    /**
     * This method will handle obtaining a URL for the given WSDL location.  The WSDL will be
     * looked for in the following places in this order:
     * 
     * PreResolution) check for xmlcatalog resolver
     * 1) As a resource on the classpath
     * 2) As a fully specified URL
     * 3) As a file on the filesystem.  This is analagous to what the generated
     *    Service client does.  Is prepends "file:/" to whatever is specified in the
     *    @WebServiceClient.wsdlLocation element.
     * 
     * @param wsdlLocation The WSDL for which a URL is wanted
     * @return A URL if the WSDL can be located, or null
     */
    private URL getWSDLURL(String wsdlLocation) {
        // Look for the WSDL file as follows:
        // PreResolution) check for xmlcatalog resolver
        wsdlLocation = resolveWSDLLocationByCatalog(wsdlLocation);
        
        
        // 1) As a resource on the classpath

        ClassLoader loader = composite.getClassLoader();
        URL url = null;
        if (loader != null) {
            url = getResource(wsdlLocation, loader); 
        }
        
        // Try the context class loader
        if(url == null){
            ClassLoader classLoader = getContextClassLoader(null);
            if(classLoader != loader){
                url = getResource(wsdlLocation, classLoader);
            }
        }

        // 2) As a fully specified URL
        if (url == null) {
            if (log.isDebugEnabled()) {
                log.debug("URL for wsdl file: " + wsdlLocation + " could not be "
                        + "determined by classloader... looking for file reference");
            }
            url = createWsdlURL(wsdlLocation);
        }
        // 3) As a file on the filesystem.  This is analagous to what the generated
        //    Service client does.  Is prepends "file:/" to whatever is specified in the
        //    @WebServiceClient.wsdlLocation element.
        if (url == null) {
            if (log.isDebugEnabled()) {
                log.debug("URL for wsdl file: " + wsdlLocation + " could not be "
                        + "found as local file reference... prepending file: protocol");
            }
            // This check is necessary because Unix/Linux file paths begin
            // with a '/'. When adding the prefix 'jar:file:/' we may end
            // up with '//' after the 'file:' part. This causes the URL 
            // object to treat this like a remote resource
            if(wsdlLocation.indexOf("/") == 0) {
                wsdlLocation = wsdlLocation.substring(1, wsdlLocation.length());
            }
            url = createWsdlURL("file:/" + wsdlLocation);

        }
        if (url == null) {
            if (log.isDebugEnabled()) {
                log.debug("Unable to obtain URL for WSDL file: " + wsdlLocation
                        + " by using prepended file: protocol");
            }
        }
        return url;
    }

    private URL getResource(final String wsdlLocation, final ClassLoader loader) {
        return (URL) AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        return loader.getResource(wsdlLocation);
                    }
                }
        );
    }

    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescriptionWSDL#getWSDLWrapper()
     */
    public WSDLWrapper getWSDLWrapper() {
        return wsdlWrapper;
    }

    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescriptionWSDL#getWSDLLocation()
    */
    public String getWSDLLocation() {
        return wsdlURL;
    }

    public WSDLWrapper getGeneratedWsdlWrapper() {
        return this.generatedWsdlWrapper;
    }

    void setAxisConfigContext(ConfigurationContext config) {
        this.configContext = config;
    }
    
    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescription#getAxisConfigContext()
    */
    public ConfigurationContext getAxisConfigContext() {
        if (configContext == null) {
            configContext = getClientConfigurationFactory().getClientConfigurationContext();
        }
        return configContext;

    }

    ClientConfigurationFactory getClientConfigurationFactory() {

        if (clientConfigFactory == null) {
            clientConfigFactory = DescriptionFactory.createClientConfigurationFactory();
        }
        return clientConfigFactory;
    }

    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescription#getServiceClient(javax.xml.namespace.QName)
    */
    public ServiceClient getServiceClient(QName portQName, Object serviceDelegateKey) {
        ServiceClient returnServiceClient = null;
        if (!DescriptionUtils.isEmpty(portQName)) {
            EndpointDescription endpointDesc = getEndpointDescription(portQName, serviceDelegateKey);
            
            if (endpointDesc != null) {
                returnServiceClient = endpointDesc.getServiceClient();
            }
            else {
                // Couldn't find Endpoint Description for port QName
                if (log.isDebugEnabled()) {
                    log.debug("Could not find portQName: " + portQName 
                            + " under ServiceDescription: " + toString());
                }
            }
        }
        else {
            // PortQName is empty
            if (log.isDebugEnabled()) {
                log.debug("PortQName agrument is invalid; it can not be null or an empty string: " + portQName);
            }
        }
        
        return returnServiceClient;
    }

    /* (non-Javadoc)
    * @see org.apache.axis2.jaxws.description.ServiceDescription#getServiceQName()
    */
    public QName getServiceQName() {
        //It is assumed that this will always be set in the constructor rather than
        //built up from the class or DBC
        return serviceQName;
    }

    void setServiceQName(QName theName) {
        serviceQName = theName;
    }
    
    public JAXWSCatalogManager getCatalogManager() {
    	return catalogManager;
    }

    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#isMTOMEnabled(java.lang.Object)
     */
    public boolean isMTOMEnabled(Object key) {
        return getDescriptionBuilderComposite().isMTOMEnabled(key);
    }
    
    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#isMTOMEnabled(java.lang.Object, Class seiClass)
     */
    public boolean isMTOMEnabled(Object key, Class seiClass) {
        if(log.isDebugEnabled()) {
            log.debug("isMTOMEnabled, key= " + key + ", seiClass= " + seiClass);
        }
        
        boolean mtomEnabled = false;
        DescriptionBuilderComposite sparseComposite = getDescriptionBuilderComposite().getSparseComposite(key);
        if(sparseComposite != null
                &&
                seiClass != null) {
            Map<String, Boolean> seiToMTOM = (Map<String, Boolean>) 
                sparseComposite.getProperties().get(MDQConstants.SEI_MTOM_ENABLEMENT_MAP);
            if(seiToMTOM != null
                    &&
                    seiToMTOM.get(seiClass.getName()) != null) {
                mtomEnabled = seiToMTOM.get(seiClass.getName());
            }
            else {
                mtomEnabled = isMTOMEnabled(key);
            }
        }
        else {
            mtomEnabled = isMTOMEnabled(key);
        }
        
        if(log.isDebugEnabled()) {
            log.debug("isMTOMEnabled, key= " + key + ", seiClass= " + seiClass + ", isMTOMEnabled= " + mtomEnabled);
        }
        return mtomEnabled;
    }
    
    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#getBindingProperites(java.lang.Object, String key)
     */
    public Map<String, Object> getBindingProperties(Object serviceDelegateKey, String key) {
        if(log.isDebugEnabled()) {
            log.debug("getBindingProperties, serviceDelegateKey= " + serviceDelegateKey + 
                      ", key= " + key);
        }

        Map<String, Object> bindingProps = null;
        DescriptionBuilderComposite sparseComposite = getDescriptionBuilderComposite().getSparseComposite(serviceDelegateKey);
        if(sparseComposite != null) {
            Map<String, Map<String, Object>> allBindingProps = (Map<String, Map<String, Object>>) 
                sparseComposite.getProperties().get(MDQConstants.BINDING_PROPS_MAP);
            bindingProps = allBindingProps != null ? (Map<String, Object>) allBindingProps.get(key) : null;
        }

        if(log.isDebugEnabled()) {
            log.debug("getBindingProperties, serviceDelegateKey= " + serviceDelegateKey + 
                      ", key= " + key + ", propsSize= " + (bindingProps != null ? bindingProps.size() : 0));
        }    

        return bindingProps;
    }

    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#getPreferredPort(java.lang.Object)
     */
    public QName getPreferredPort(Object key) {
        return getDescriptionBuilderComposite().getPreferredPort(key);
    }
    
    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#isServerSide()
     */
    public boolean isServerSide() {
        return isServerSide;
    }

    HashMap<String, DescriptionBuilderComposite> getDBCMap() {
        return dbcMap;
    }

    void setGeneratedWsdlWrapper(WSDL4JWrapper wrapper) {
        this.generatedWsdlWrapper = wrapper;
    }

    void setWsdlWrapper(WSDL4JWrapper wrapper) {
        this.wsdlWrapper = wrapper;
    }

    private void validateDBCLIntegrity() {

        //First, check the integrity of this input composite
        //and retrieve
        //the composite that represents this impl

        try {
            validateIntegrity();
        }
        catch (Exception ex) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("dbclIntegrityErr",ex.toString(),composite.toString()), ex);
        }
    }

    /*
      * Validates the integrity of an impl. class. This should not be called directly for an SEI composite
      */
    void validateIntegrity() {
        //In General, this integrity checker should do gross level checking
        //It should not be setting spec-defined default values, but can look
        //at things like empty strings or null values

        //Verify that, if this implements a strongly typed provider interface, that it
        // also contain a WebServiceProvider annotation per JAXWS Sec. 5.1
        Iterator<String> iter =
                composite.getInterfacesList().iterator();

        // Remember if we've validated the Provider interface.  Later we'll make sure that if we have an 
        // WebServiceProvider annotation, we found a valid interface here.
        boolean providerInterfaceValid = false;
        while (iter.hasNext()) {
            String interfaceString = iter.next();
            if (interfaceString.equals(MDQConstants.PROVIDER_SOURCE)
                    || interfaceString.equals(MDQConstants.PROVIDER_SOAP)
                    || interfaceString.equals(MDQConstants.PROVIDER_DATASOURCE)
                    || interfaceString.equals(MDQConstants.PROVIDER_STRING)
                    || interfaceString.equals(MDQConstants.PROVIDER_OMELEMENT)) {
                providerInterfaceValid = true;
                //This is a provider based endpoint, make sure the annotation exists
                if (composite.getWebServiceProviderAnnot() == null) {
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("validateIntegrityErr1",composite.getClassName()));
                }
            }
        }

        //Verify that WebService and WebServiceProvider are not both specified
        //per JAXWS - Sec. 7.7
        if (composite.getWebServiceAnnot() != null &&
                composite.getWebServiceProviderAnnot() != null) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateIntegrityErr2",composite.getClassName()));
        }

        if (composite.getWebServiceProviderAnnot() != null) {
            if (!providerInterfaceValid) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateIntegrityErr3",composite.getClassName()));
            }
            // There must be a public default constructor per JAXWS - Sec 5.1
            if (!validateDefaultConstructor()) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateIntegrityErr4",composite.getClassName()));
            }
            // There must be an invoke method per JAXWS - Sec 5.1.1
            if (!validateInvokeMethod()) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateIntegrityErr5",composite.getClassName()));
            }

            //If ServiceMode annotation specifies 'payload', then make sure that it is not typed with
            // SOAPMessage or DataSource
            validateProviderInterfaces();

        } else if (composite.getWebServiceAnnot() != null) {

            if (composite.getServiceModeAnnot() != null) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateIntegrityErr6",composite.getClassName()));
            }

            if (!composite.isInterface()) {
                // TODO: Validate on the class that this.classModifiers Array does not contain the strings
                //        FINAL or ABSTRACT, but does contain PUBLIC
                // TODO: Validate on the class that a public constructor exists
                // TODO: Validate on the class that a finalize() method does not exist
                if (!DescriptionUtils.isEmpty(composite.getWebServiceAnnot().wsdlLocation())) {
                    if (composite.getWsdlDefinition(getServiceQName()) == null
                            &&
                            composite.getWsdlDefinition() == null 
                            &&
                            composite.getWsdlURL() == null) {
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validateIntegrityErr7",composite.getClassName(),
                        				composite.getWebServiceAnnot().wsdlLocation()));
                    }
                }

                //		setWebServiceAnnotDefaults(true=impl); Must happen before we start checking annot
                if (!DescriptionUtils.isEmpty(composite.getWebServiceAnnot().endpointInterface())) {

                    DescriptionBuilderComposite seic =
                            dbcMap.get(composite.getWebServiceAnnot().endpointInterface());

                    //Verify that we can find the SEI in the composite list
                    if (seic == null) {
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validateIntegrityErr8",composite.getClassName(),
                        				composite.getWebServiceAnnot().endpointInterface()));
                    }

                    // Verify that the only class annotations are WebService and HandlerChain
                    // (per JSR181 Sec. 3.1).  Note that this applies to JSR-181 annotations; the restriction
                    // does not apply to JSR-224 annotations such as BindingType
                    if (composite.getSoapBindingAnnot() != null
                            || composite.getWebFaultAnnot() != null
                            || composite.getWebServiceClientAnnot() != null
                            ) {
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validateIntegrityErr9",composite.getClassName()));
                    }

                    //Verify that WebService annotation does not contain a name attribute
                    //(per JSR181 Sec. 3.1)
                    if (!DescriptionUtils.isEmpty(composite.getWebServiceAnnot().name())) {
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validateIntegrityErr10",composite.getClassName(),
                        				composite.getWebServiceAnnot().name()));
                    }

                    validateSEI(seic);
                    //Verify that that this implementation class implements all methods in the interface
                    validateImplementation(seic);

                    //Verify that this impl. class does not contain any @WebMethod annotations
                    if (webMethodAnnotationsExist()) {
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validateIntegrityErr11",composite.getClassName()));
                    }


                } else { //this is an implicit SEI (i.e. impl w/out endpointInterface

                    
                    checkImplicitSEIAgainstWSDL();
                    //	TODO:	Call ValidateWebMethodAnnots()
                    //			- this method will check that all methods are public - ???
                    //
                }
            } else { //this is an interface...we should not be processing interfaces here
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateIntegrityErr12",composite.getClassName()));
            }

            // Verify that the SOAPBinding annotations are supported.
            if (composite.getSoapBindingAnnot() != null) {
                if (composite.getSoapBindingAnnot().use() == javax.jws.soap.SOAPBinding.Use.ENCODED) {
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("validateIntegrityErr13",composite.getClassName()));  
                }
            }
            
            checkMethodsAgainstWSDL();
        }
    }

    /**
     * Validate there is an invoke method on the composite.
     *
     * @return
     */
    private boolean validateInvokeMethod() {
        boolean validInvokeMethod = false;
        List<MethodDescriptionComposite> invokeMethodList =
                composite.getMethodDescriptionComposite("invoke");
        if (invokeMethodList != null && !invokeMethodList.isEmpty()) {
            validInvokeMethod = true;
        }
        return validInvokeMethod;
    }

    /**
     * Validate that, if using PAYLOAD mode, then interfaces list cannot contain SOAPMessage or
     * DataSource
     *
     * @return
     */
    private void validateProviderInterfaces() {

        // Default for ServiceMode is 'PAYLOAD'. So, if it is specified  (explicitly or
        // implicitly) then verify that we are not implementing improper interfaces)
        if ((composite.getServiceModeAnnot() == null)
                || composite.getServiceModeAnnot().value() == javax.xml.ws.Service.Mode.PAYLOAD) {

            Iterator<String> iter = composite.getInterfacesList().iterator();

            while (iter.hasNext()) {
                String interfaceString = iter.next();
                if (interfaceString.equals(MDQConstants.PROVIDER_SOAP)
                        || interfaceString.equals(MDQConstants.PROVIDER_DATASOURCE)) {
                	
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("validatePIsErr1",composite.getClassName()));
                }
            }

        } else {
            // We are in MESSAGE mode
            // Conformance: JAXWS Spec.- Sec. 4.3 (javax.activation.DataSource)

            // REVIEW: Should the provider interface validation be moved to post-construction validation, 
            // since it seems that the logic to understand the default values for binding type 
            // (see comment below) should be left to the creation of the Description objects.
            String bindingType = null;
            if (composite.getBindingTypeAnnot() != null) {
                bindingType = composite.getBindingTypeAnnot().value();
            }

            Iterator<String> iter = composite.getInterfacesList().iterator();

            while (iter.hasNext()) {
                String interfaceString = iter.next();

                if (interfaceString.equals(MDQConstants.PROVIDER_SOAP)) {

                    // Make sure BindingType is SOAP/HTTP with SOAPMessage
					// object, Default for Binding Type is SOAP/HTTP
					if (!DescriptionUtils.isEmpty(bindingType)
							&& !bindingType
									.equals(SOAPBinding.SOAP11HTTP_BINDING)
							&& !bindingType
									.equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING)
							&& !bindingType
									.equals(SOAPBinding.SOAP12HTTP_BINDING)
							&& !bindingType
									.equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING)
							&& !bindingType
									.equals(MDQConstants.SOAP11JMS_BINDING)
							&& !bindingType
									.equals(MDQConstants.SOAP11JMS_MTOM_BINDING)
							&& !bindingType
									.equals(MDQConstants.SOAP12JMS_BINDING)
							&& !bindingType
									.equals(MDQConstants.SOAP12JMS_MTOM_BINDING)
							&& !bindingType
							        .equals(MDQConstants.SOAP_HTTP_BINDING))

						throw ExceptionFactory.makeWebServiceException(Messages
								.getMessage("validatePIsErr2", composite
										.getClassName()));
                
                
                
                } else if (interfaceString
                        .equals(MDQConstants.PROVIDER_DATASOURCE)) {

                    // Make sure BindingType is XML/HTTP with DataSource object
                    if (DescriptionUtils.isEmpty(bindingType)
                            || !bindingType
                            .equals(javax.xml.ws.http.HTTPBinding.HTTP_BINDING))
                    	
                        throw ExceptionFactory.makeWebServiceException(
                        		Messages.getMessage("validatePIsErr3",composite.getClassName()));
                }
            }
        }
    }


    /**
     * Validate there is a default no-argument constructor on the composite.
     *
     * @return
     */
    private boolean validateDefaultConstructor() {
        boolean validDefaultCtor = false;
        List<MethodDescriptionComposite> constructorList =
                composite.getMethodDescriptionComposite("<init>");
        if (constructorList != null && !constructorList.isEmpty()) {
            // There are public constructors; make sure there is one that takes no arguments.
            for (MethodDescriptionComposite checkCtor : constructorList) {
                List<ParameterDescriptionComposite> paramList =
                        checkCtor.getParameterDescriptionCompositeList();
                if (paramList == null || paramList.isEmpty()) {
                    validDefaultCtor = true;
                    break;
                }
            }
        }

        return validDefaultCtor;
    }

    private void validateImplementation(DescriptionBuilderComposite seic) {
        /*
         *  Verify that an impl class implements all the methods of the SEI. We have to verify this 
         *  because an impl class is not required to actually use the 'implements' clause. So, if 
         *  it doesn't, the Java compiler won't catch it. Don't need to worry about chaining 
         *  because only one EndpointInterface can be specified, and the SEI cannot specify an 
         *  EndpointInterface, so the Java compiler will take care of everything else.
         *  
         *  Note, however, that we do need to take overloaded methods into a consideration.  The
         *  same method name can be specified for multiple methods, but they can have different
         *  parameters.  Note that methods which differ only in the return type or the exceptions
         *  thrown are not overloaded (and therefore would cause a compile error).
         */

        List<MethodDescriptionComposite> implMethods = composite.getMethodDescriptionsList();
        // Add methods declared in the implementation's superclass
        addSuperClassMethods(implMethods, composite);

        List<MethodDescriptionComposite> seiMethods = seic.getMethodDescriptionsList();
        // Add any methods declared in superinterfaces of the SEI
        addSuperClassMethods(seiMethods, seic);

        // Make sure all the methods in the SEI (including any inherited from superinterfaces) are
        // implemented by the bean (including inherited methods on the bean), taking into
        // account overloaded methods.
        Iterator<MethodDescriptionComposite> verifySEIIterator = seiMethods.iterator();
        while (verifySEIIterator.hasNext()) {
            MethodDescriptionComposite seiMDC = verifySEIIterator.next();
            
            // Make sure the implementation implements this SEI method.  Since we have to account
            // for method overloading, we look for ALL methods with the same name in the 
            // implementation, then from that collection of methods, we look for one that has the 
            // same parameters.  If we find one with the same parameters, then we check the return
            // and exceptions.  Note that in Java, overloaded methods are ones that have the same
            // name but different parameters; a difference in the return type or thrown exceptions
            // does not constitute overloading and is a compile error.
            Iterator<MethodDescriptionComposite> implMDCIterator = implMethods.iterator();
            boolean methodImplFound = false;
            while (implMDCIterator.hasNext()) {
                MethodDescriptionComposite implMDC = implMDCIterator.next();
                
                if (seiMDC.getMethodName().equals(implMDC.getMethodName())) {
                    // The method names match, so now check the parameters
                    try {
                        validateMethodParameters(seiMDC, implMDC, seic.getClassName());
                        methodImplFound = true;
                    }
                    catch (Exception ex) {
                        // The parameters didn't match, so we'll check the next 
                        // implemntation method on the next iteration of the inner loop.
                    }
                    
                    // If the name and the parameters matched, then we've found the method
                    // implementation, even if it was overloaded. Now check the return value and
                    // thrown exceptions.  Note these will methods throw exceptions if validation fails.
                    // If all the validation passes, we can break out of the inner loop since we 
                    // found the implementation for this sei method.
                    if (methodImplFound) {
                        validateMethodExceptions(seiMDC, implMDC, seic.getClassName());
                        validateMethodReturnValue(seiMDC, implMDC, seic.getClassName());
                        break;
                    }
                }
            }
            
            if (!methodImplFound) {
                // We didn't find the implementation for this SEI method, so throw a validation
                // exception.
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateImplErr",composite.getClassName(),
                				seiMDC.getMethodName(),seic.getClassName()));
            }
        }
    }

    private void validateMethodParameters(MethodDescriptionComposite seiMDC,
        MethodDescriptionComposite implMDC, String className) {
        List<ParameterDescriptionComposite> seiPDCList = seiMDC
            .getParameterDescriptionCompositeList();
        List<ParameterDescriptionComposite> implPDCList = implMDC
            .getParameterDescriptionCompositeList();
        if ((seiPDCList == null || seiPDCList.isEmpty())
            && (implPDCList == null || implPDCList.isEmpty())) {
            // There are no parameters on the SEI or the impl; all is well
        } else if ((seiPDCList == null || seiPDCList.isEmpty())
            && !(implPDCList == null || implPDCList.isEmpty())) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodParamErr1",implPDCList.toString(),
            				composite.getClassName(),seiMDC.getMethodName(),className));
        } else if ((seiPDCList != null && !seiPDCList.isEmpty())
            && !(implPDCList != null && !implPDCList.isEmpty())) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodParamErr2",seiPDCList.toString(),
            				composite.getClassName(),seiMDC.getMethodName(),className));
        } else if (seiPDCList.size() != implPDCList.size()) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodParamErr3",
            				new Integer(seiPDCList.size()).toString(),
            				new Integer(implPDCList.size()).toString(),composite.getClassName(),
            				seiMDC.getMethodName(),className));
        } else {
            // Make sure the order and type of parameters match
            // REVIEW: This checks for strict equality of the fully qualified
            // type. It does not
            // take into consideration object hierachy. For example foo(Animal)
            // will not equal bar(Zebra)
            boolean parametersMatch = true;
            String failingMessage = null;
            for (int paramNumber = 0; paramNumber < seiPDCList.size(); paramNumber++) {
                String seiParamType = seiPDCList.get(paramNumber).getParameterType();
                String implParamType = implPDCList.get(paramNumber).getParameterType();
                if (!seiParamType.equals(implParamType)) {
                    parametersMatch = false;
                    String[] inserts = new String[] {
                            String.valueOf(paramNumber), 
                            seiParamType,
                            implParamType,
                            composite.getClassName(),
                            seiMDC.getMethodName(),
                            className
                    };
                    
                    failingMessage = Messages.getMessage("serviceDescriptionImplValidationErr",
                                                         inserts);
                                                         
                    break;
                }
            }
            if (!parametersMatch) {
                throw ExceptionFactory.makeWebServiceException(failingMessage);
            }
        }
    }

    private void validateMethodReturnValue(MethodDescriptionComposite seiMDC,
        MethodDescriptionComposite implMDC, String className) {
        String seiReturnValue = seiMDC.getReturnType();
        String implReturnValue = implMDC.getReturnType();

        if (seiReturnValue == null && implReturnValue == null) {
            // Neither specify a return value; all is well
        } else if (seiReturnValue == null && implReturnValue != null) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodRVErr1",implReturnValue,
            				composite.getClassName(),seiMDC.getMethodName(),className));
        } else if (seiReturnValue != null && implReturnValue == null) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodRVErr2",seiReturnValue,
            				composite.getClassName(),seiMDC.getMethodName(),className));
        } else if (!seiReturnValue.equals(implReturnValue)) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodRVErr3",seiReturnValue,implReturnValue,
            				composite.getClassName(),seiMDC.getMethodName(),className));
        }

    }

    private void validateMethodExceptions ( MethodDescriptionComposite seiMDC, 
        MethodDescriptionComposite implMDC,
        String className) {

        String[] seiExceptions = seiMDC.getExceptions();
        String[] implExceptions = implMDC.getExceptions();

        // An impl can choose to throw fewer checked exceptions than declared on the SEI, but not more.
        // This is analagous to the Java rules for interfaces.
        if (seiExceptions == null) {
            if (implExceptions == null) {
                return;
            } else {
                // SEI delcares no checked exceptions, but the implementation has checked exceptions, which is an error
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("validateMethodExceptionErr1",
                				composite.getClassName(),seiMDC.getMethodName(),className));
            }
        } else if (implExceptions == null) {
            // Implementation throws fewer checked exceptions than SEI, which is OK.
            return;
        }
        
        // Check the list length; An implementation can not declare more exceptions than the SEI
        if (seiExceptions.length < implExceptions.length) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateMethodExceptionErr2",
            				new Integer(implExceptions.length).toString(),
            				new Integer(seiExceptions.length).toString(),
            				composite.getClassName(),seiMDC.getMethodName(),className));
        }
        
        // Make sure that each checked exception declared by the 
        // implementation is on the SEI also
        if (implExceptions.length > 0) {                
            for (String implException : implExceptions) {
                boolean foundIt = false;
                if (seiExceptions.length > 0) {         
                    for (String seiException : seiExceptions) {
                        if (seiException.equals(implException)) {
                            foundIt = true;
                            break;
                        }
                    }
                }
                
                if (!foundIt) {    	
                    throw ExceptionFactory.makeWebServiceException(
                    		Messages.getMessage("validateMethodExceptionErr3",implException,
                    				composite.getClassName(),seiMDC.getMethodName(),className));
                }
            }
        }

    }

    /**
     * Adds any methods declared in superclasses to the List.  The hierachy starting with the DBC
     * will be walked up recursively, adding methods from each parent DBC encountered.
     * <p/>
     * Note that this can be used for either classes or interfaces.
     *
     * @param methodList The current collection of methods, including overloaded ones
     * @param dbc The composite to be checked for methods to be added to the collection
     */
    private void addSuperClassMethods(List<MethodDescriptionComposite> methodList, DescriptionBuilderComposite dbc) {
        DescriptionBuilderComposite superDBC = dbcMap.get(dbc.getSuperClassName());
        if (superDBC != null) {
            Iterator<MethodDescriptionComposite> mIter = superDBC.getMethodDescriptionsList().iterator();
            while (mIter.hasNext()) {
                MethodDescriptionComposite mdc = mIter.next();
                methodList.add(mdc);
            }
            addSuperClassMethods(methodList, superDBC);
        }
    }


    /*
      * This method verifies that, if there are any WebMethod with exclude == false, then
      * make sure that we find all of those methods represented in the wsdl. However, if
      * there are no exclusions == false, or there are no WebMethod annotations, then verify
      * that all the public methods are in the wsdl
      */
    private void checkMethodsAgainstWSDL() {
        // Verify that, for ImplicitSEI, that all methods that should exist(if one false found, then
        // only look for WebMethods w/ False, else take all public methods but ignore those with
        // exclude == true
        if (webMethodAnnotationsExist()) {
            if (DescriptionUtils.falseExclusionsExist(composite))
                verifyFalseExclusionsWithWSDL();
            else
                verifyPublicMethodsWithWSDL();
        } else {
            verifyPublicMethodsWithWSDL();
        }
    }

    private void checkImplicitSEIAgainstWSDL() {

        //TODO: If there is a WSDL, then verify that all WebMethods on this class and in the
        //		superclasses chain are represented in the WSDL...Look at logic below to make
        //		sure this really happening


        if (webMethodAnnotationsExist()) {
            if (DescriptionUtils.falseExclusionsExist(composite))
                verifyFalseExclusionsWithWSDL();
            else
                verifyPublicMethodsWithWSDL();
        } else {
            verifyPublicMethodsWithWSDL();
        }

    }

    private void checkSEIAgainstWSDL() {
        //TODO: Place logic here to verify that each publicMethod with WebMethod annot
        //      is contained in the WSDL (If there is a WSDL) If we find
        //	    a WebMethod annotation, use its values for looking in the WSDL

    }

    private void validateSEI(DescriptionBuilderComposite seic) {

        if (seic.getWebServiceAnnot() == null) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateSEIErr1",composite.getClassName(),seic.getClassName()));
        }
        if (!seic.getWebServiceAnnot().endpointInterface().equals("")) {
            throw ExceptionFactory.makeWebServiceException(
            		Messages.getMessage("validateSEIErr2",composite.getClassName(),
            				seic.getClassName(),seic.getWebServiceAnnot().endpointInterface()));
        }
        // Verify that the SOAPBinding annotations are supported.
        if (seic.getSoapBindingAnnot() != null &&
        		seic.getSoapBindingAnnot().use() == javax.jws.soap.SOAPBinding.Use.ENCODED) {
        	throw ExceptionFactory.makeWebServiceException(Messages.getMessage("validateSEIErr3",seic.getClassName()));  
        }

        checkSEIAgainstWSDL();

        //TODO: More validation here

        //TODO: Make sure we don't find any WebMethod annotations with exclude == true
        //		anywhere in the superclasses chain

        //TODO: Check that all WebMethod annotations in the superclass chain are represented in
        //		WSDL, assuming there is WSDL

        //TODO:	Validate that the interface is public

        //		Call ValidateWebMethodAnnots()
        //

        //This will perform validation for all methods, regardless of WebMethod annotations
        //It is called for the SEI, and an impl. class that does not specify an endpointInterface
        validateMethods(seic.getMethodDescriptionsList());
    }

    /** @return Returns TRUE if we find just one WebMethod Annotation */
    private boolean webMethodAnnotationsExist() {
        MethodDescriptionComposite mdc = null;
        Iterator<MethodDescriptionComposite> iter =
                composite.getMethodDescriptionsList().iterator();

        while (iter.hasNext()) {
            mdc = iter.next();

            if (mdc.getWebMethodAnnot() != null)
                return true;
        }

        return false;
    }

    private void verifyFalseExclusionsWithWSDL() {
        //TODO: Place logic here to verify that each exclude==false WebMethod annot we find
        //      is contained in the WSDL
    }

    private void verifyPublicMethodsWithWSDL() {
        //TODO: Place logic here to verify that each publicMethod with no WebMethod annot
        //      is contained in the WSDL

    }


    private void validateMethods(List<MethodDescriptionComposite> mdcList) {
        if (mdcList != null && !mdcList.isEmpty()) {
            for (MethodDescriptionComposite mdc : mdcList) {
                String returnType = mdc.getReturnType();
                if (returnType != null
                                && (returnType.equals(RETURN_TYPE_FUTURE) || returnType
                                                .equals(RETURN_TYPE_RESPONSE))) {
                    throw ExceptionFactory.makeWebServiceException(Messages
                                    .getMessage("serverSideAsync", mdc.getDeclaringClass(), mdc
                                                    .getMethodName()));
                }
                // Verify that the SOAPBinding annotation values are supported.
                if (mdc.getSoapBindingAnnot() != null) {

                    // For this JAXWS engine, SOAPBinding.Use = ENCODED is unsupported
                    if (mdc.getSoapBindingAnnot().use() == javax.jws.soap.SOAPBinding.Use.ENCODED) {
                        throw ExceptionFactory.
                          makeWebServiceException(Messages.getMessage("soapBindingUseEncoded",
                                                                      mdc.getDeclaringClass(),
                                                                      mdc.getMethodName()));

                    }

                    // Verify that, if a SOAPBinding annotation exists, that its style be set to
                    // only DOCUMENT JSR181-Sec 4.7.1
                    if (mdc.getSoapBindingAnnot().style() == javax.jws.soap.SOAPBinding.Style.RPC) {
                        throw ExceptionFactory.
                          makeWebServiceException(Messages.getMessage("soapBindingStyle",
                                                                      mdc.getDeclaringClass(),
                                                                      mdc.getMethodName()));
                    }

                } 
            }
        }
        // TODO: Fill this out to validate all MethodDescriptionComposite (and
        // their inclusive
        //      annotations on this SEI (SEI is assumed here)
        //check oneway
        //

        //This could be an SEI, or an impl. class that doesn' specify an EndpointInterface (so, it
        //is implicitly an SEI...need to consider this
        //

        //TODO: Verify that, if this is an interface...that there are no Methods with WebMethod
        //      annotations that contain exclude == true

        //TODO: Verify that, if a SOAPBinding annotation exists, that its style be set to
        //      only DOCUMENT JSR181-Sec 4.7.1

    }

    private void validateWSDLOperations() {
        //Verifies that all operations on the wsdl are found in the impl/sei class
    }

    public boolean isWSDLSpecified() {
        boolean wsdlSpecified = false;
        if (getWSDLWrapper() != null) {
            wsdlSpecified = (getWSDLWrapper().getDefinition() != null);
        }
        return wsdlSpecified;
    }

    
    // ===========================================
    // ANNOTATION: HandlerChain
    // ===========================================

    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#getHandlerChain()
     */
    public HandlerChainsType getHandlerChain() {
        return getHandlerChain(null);
    }
    
    /* (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.ServiceDescription#getHandlerChain(java.lang.Object)
     */
    public HandlerChainsType getHandlerChain(Object sparseCompositeKey) {
        DescriptionBuilderComposite sparseComposite = null;

        // If there is a HandlerChainsType in the sparse composite for this ServiceDelegate
        // (i.e. this sparseCompositeKey), then return that.
        if (sparseCompositeKey != null) {
            sparseComposite = composite.getSparseComposite(sparseCompositeKey);
            if (sparseComposite != null && sparseComposite.getHandlerChainsType() != null) {
                return sparseComposite.getHandlerChainsType();
            }
        }
        
        // If there is no HandlerChainsType in the composite, then read in the file specified
        // on the HandlerChain annotation if it is present.
        if (handlerChainsType == null) {

            getAnnoHandlerChainAnnotation(sparseCompositeKey);
            if (handlerChainAnnotation != null) {

                String handlerFileName = handlerChainAnnotation.file();

                if (log.isDebugEnabled()) {
                    log.debug("EndpointDescriptionImpl.getHandlerChain: fileName: "
                              + handlerFileName + " className: " + composite.getClassName());
                }

                String className = composite.getClassName();

                ClassLoader classLoader = composite.getClassLoader();

                InputStream is =
                        DescriptionUtils.openHandlerConfigStream(handlerFileName,
                                                                 className,
                                                                 classLoader);
                if (is == null) {
                    // config stream is still null.  This may mean the @HandlerChain annotation is on a *driver* class
                    // next to a @WebServiceRef annotation, so the path is relative to the class declaring @HandlerChain
                    // and NOT relative to the Service or Endpoint class, which also means we should use the sparseComposite
                    // since that is where the @HandlerChain annotation info would have been loaded.
                    if (sparseComposite != null) {
                        String handlerChainDeclaringClass = (String)sparseComposite.getProperties().get(MDQConstants.HANDLER_CHAIN_DECLARING_CLASS);
                        if (handlerChainDeclaringClass != null) {
                            className = handlerChainDeclaringClass;
                            is = DescriptionUtils.openHandlerConfigStream(handlerFileName, className, classLoader);
                        }
                    }
                }

                if(is == null) {
                    throw ExceptionFactory.makeWebServiceException(Messages.getMessage("handlerChainNS",
                            handlerFileName, className));

                }
                else {
                    handlerChainsType =
                        DescriptionUtils.loadHandlerChains(is, 
                              getClassLoader(this.getClass()));
                }
            }
        }
        return handlerChainsType;
    }

    
    /*
     * This is a client side only method. The generated service class may contain 
     * handler chain annotations
     */
    public HandlerChain getAnnoHandlerChainAnnotation(Object sparseCompositeKey) {
        if (this.handlerChainAnnotation == null) {
            Class serviceClass = composite.getCorrespondingClass();
                if (serviceClass != null) {
                    handlerChainAnnotation =
                            (HandlerChain) getAnnotation(serviceClass, HandlerChain.class);
            }
        }
	if (handlerChainAnnotation == null) {
            if (sparseCompositeKey != null) {
                DescriptionBuilderComposite sparseComposite = composite.getSparseComposite(sparseCompositeKey);
                if (sparseComposite != null && sparseComposite.getHandlerChainAnnot() != null) {
                    handlerChainAnnotation = sparseComposite.getHandlerChainAnnot();
                }
            }
        }

        return handlerChainAnnotation;
    }
    
    /* Returns the WSDL definiton as specified in the metadata.  Note that this WSDL may not be
     * complete.
     */
    public Definition getWSDLDefinition() {
        Definition defn = null;
        if (getWSDLWrapper() != null) {
            defn = getWSDLWrapper().getDefinition();
        }
        return defn;
    }

    /**
     * Returns the WSDL definiton as created by calling the WSDL generator.  This will be null
     * unless the WSDL definition provided by the metadata is incomplete
     */
    public Definition getWSDLGeneratedDefinition() {
        Definition defn = null;
        if (getGeneratedWsdlWrapper() != null) {
            defn = getGeneratedWsdlWrapper().getDefinition();
        }
        return defn;
    }

    public Service getWSDLService() {
        Service returnWSDLService = null;
        Definition defn = getWSDLDefinition();
        if (defn != null) {
            returnWSDLService = defn.getService(getServiceQName());
        }
        return returnWSDLService;
    }

    public Map getWSDLPorts() {
        Service wsdlService = getWSDLService();
        if (wsdlService != null) {
            return wsdlService.getPorts();
        } else {
            return null;
        }
    }

    public List<QName> getPorts(Object serviceDelegateKey) {
        ArrayList<QName> portList = new ArrayList<QName>();
        // Note that we don't cache these results because the list of ports can be added
        // to via getPort(...) and addPort(...).

        // If the WSDL is specified, get the list of ports under this service
        Map wsdlPortsMap = getWSDLPorts();
        if (wsdlPortsMap != null) {
            Iterator wsdlPortsIterator = wsdlPortsMap.values().iterator();
            // Note that the WSDL Ports do not have a target namespace associated with them.
            // JAXWS says to use the TNS from the Service.
            String serviceTNS = getServiceQName().getNamespaceURI();
            for (Port wsdlPort = null; wsdlPortsIterator.hasNext();) {
                wsdlPort = (Port)wsdlPortsIterator.next();
                String wsdlPortLocalPart = wsdlPort.getName();
                portList.add(new QName(serviceTNS, wsdlPortLocalPart));
            }
        }

        // Go through the list of Endpoints that have been created and add any
        // not already in the list.  This will include ports added to the Service
        // via getPort(...) and addPort(...)
        Collection<EndpointDescription> endpointDescs = getEndpointDescriptions_AsCollection();
        for (EndpointDescription endpointDesc : endpointDescs) {
            QName endpointPortQName = endpointDesc.getPortQName();
            if (!portList.contains(endpointPortQName)) {
                portList.add(endpointPortQName);
            }
        }
        
        //Retrieve all the dynamic ports for this client
        if (serviceDelegateKey != null) {
			Collection<EndpointDescriptionImpl> dynamicEndpointDescs = getDynamicEndpointDescriptions_AsCollection(serviceDelegateKey);
			if (dynamicEndpointDescs != null) {
				for (EndpointDescription dynamicEndpointDesc : dynamicEndpointDescs) {
					QName endpointPortQName = dynamicEndpointDesc
							.getPortQName();
					if (!portList.contains(endpointPortQName)) {
						portList.add(endpointPortQName);
					}
				}
			}
		}
        return portList;
    }

    public List<Port> getWSDLPortsUsingPortType(QName portTypeQN) {
        ArrayList<Port> portList = new ArrayList<Port>();
        if (!DescriptionUtils.isEmpty(portTypeQN)) {
            Map wsdlPortMap = getWSDLPorts();
            if (wsdlPortMap != null && !wsdlPortMap.isEmpty()) {
                for (Object mapElement : wsdlPortMap.values()) {
                    Port wsdlPort = (Port)mapElement;
                    PortType wsdlPortType = wsdlPort.getBinding().getPortType();
                    QName wsdlPortTypeQN = wsdlPortType.getQName();
                    if (portTypeQN.equals(wsdlPortTypeQN)) {
                        portList.add(wsdlPort);
                    }
                }
            }
        }
        return portList;
    }

    public List<Port> getWSDLPortsUsingSOAPAddress(List<Port> wsdlPorts) {
        ArrayList<Port> portsUsingAddress = new ArrayList<Port>();
        if (wsdlPorts != null && !wsdlPorts.isEmpty()) {
            for (Port checkPort : wsdlPorts) {
                List extensibilityElementList = checkPort.getExtensibilityElements();
                for (Object checkElement : extensibilityElementList) {
                    if (EndpointDescriptionImpl
                            .isSOAPAddressElement((ExtensibilityElement)checkElement)) {
                        portsUsingAddress.add(checkPort);
                    }
                }
            }
        }
        return portsUsingAddress;
    }

    public ServiceRuntimeDescription getServiceRuntimeDesc(String name) {
        return runtimeDescMap.get(name);
    }

    public void setServiceRuntimeDesc(ServiceRuntimeDescription srd) {
        runtimeDescMap.put(srd.getKey(), srd);
    }
    
    private void resetServiceRuntimeDescription() {
        runtimeDescMap.clear();
    }
    
    /**
     * Return the name of the client-side service class if it exists.
     */
    protected String getServiceClassName() {
        return composite.getClassName();
    }

    private EndpointDescriptionImpl getDynamicEndpointDescriptionImpl(QName portQName, Object key) {
        Map<QName, EndpointDescriptionImpl> innerMap = null;
        synchronized(dynamicEndpointDescriptions) {
        	innerMap = dynamicEndpointDescriptions.get(key);
            if (innerMap != null) {
            	return innerMap.get(portQName);
            }
        }
        return null;
    }

    private void addDynamicEndpointDescriptionImpl(EndpointDescriptionImpl endpointDescriptionImpl, 
    												Object key) {
        Map<QName, EndpointDescriptionImpl> innerMap = null;
        synchronized(dynamicEndpointDescriptions) {
            innerMap = dynamicEndpointDescriptions.get(key);
            if (innerMap == null) {
               innerMap = new HashMap<QName, EndpointDescriptionImpl>();
               dynamicEndpointDescriptions.put(key, innerMap);
            }
            innerMap.put(endpointDescriptionImpl.getPortQName(), endpointDescriptionImpl);
        }
    }
        
    /** Return a string representing this Description object and all the objects it contains. */
    public String toString() {
        final String newline = "\n";
        final String sameline = "; ";
        // This produces a TREMENDOUS amount of output if we have the WSDL Definition objects 
        // do a toString on themselves.
        boolean dumpWSDLContents = false;
        StringBuffer string = new StringBuffer();
        try {
            // Basic information
            string.append(super.toString());
            string.append(newline);
            string.append("ServiceQName: " + getServiceQName());
            // WSDL information
            string.append(newline);
            string.append("isWSDLSpecified: " + isWSDLSpecified());
            string.append(sameline);
            string.append("WSDL Location: " + getWSDLLocation());
            string.append(newline);
            if (dumpWSDLContents) {
                string.append("WSDL Definition: " + getWSDLDefinition());
                string.append(newline);
                string.append("Generated WSDL Definition: " + getWSDLGeneratedDefinition());
            } else {
                string.append("WSDL Definition available: " + (getWSDLDefinition() != null));
                string.append(sameline);
                string.append("Generated WSDL Definition available: " +
                        (getWSDLGeneratedDefinition() != null));
            }
            // Ports
            string.append(newline);
            List<QName> ports = getPorts(null);
            string.append("Number of defined ports: " + ports.size());
            //TODO: Show the map that contains the dynamic ports
            string.append(newline);
            string.append("Port QNames: ");
            for (QName port : ports) {
                string.append(port + sameline);
            }
            // Axis Config information
            // We don't print out the config context because it will force one to be created
            // if it doesn't already exist.
            // string.append(newline);
            // string.append("ConfigurationContext: " + getAxisConfigContext());
            // EndpointDescriptions
            string.append(newline);
            Collection<EndpointDescription> endpointDescs = getEndpointDescriptions_AsCollection();
            if (endpointDescs == null) {
                string.append("EndpointDescription array is null");
            }
            else {
                string.append("Number of EndpointDescrptions: " + endpointDescs.size());
                string.append(newline);
                for (EndpointDescription endpointDesc : endpointDescs) {
                    string.append(endpointDesc.toString());
                    string.append(newline);
                }
            }
            string.append("RuntimeDescriptions:" + this.runtimeDescMap.size());
            string.append(newline);
            for (ServiceRuntimeDescription runtimeDesc : runtimeDescMap.values()) {
                string.append(runtimeDesc.toString());
                string.append(newline);
            }
        }
        catch (Throwable t) {
            string.append(newline);
            string.append("Complete debug information not currently available for " +
                    "ServiceDescription");
            return string.toString();
        }
        return string.toString();

    }
    /**
     * Get an annotation.  This is wrappered to avoid a Java2Security violation.
     * @param cls Class that contains annotation 
     * @param annotation Class of requrested Annotation
     * @return annotation or null
     */
    private static Annotation getAnnotation(final Class cls, final Class annotation) {
        return (Annotation) AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                return cls.getAnnotation(annotation);
            }
        });
    }

    private static ClassLoader getContextClassLoader(final ClassLoader classLoader) {
        ClassLoader cl;
        try {
            cl = (ClassLoader) AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws ClassNotFoundException {
                            return classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
                        }
                    }
            );
        } catch (PrivilegedActionException e) {
            if (log.isDebugEnabled()) {
                log.debug("Exception thrown from AccessController: " + e.getMessage(), e);
            }
            throw ExceptionFactory.makeWebServiceException(e.getException());
        }

        return cl;
    }
    
    private static ClassLoader getClassLoader(final Class cls) {
        ClassLoader cl = null;
        try {
            cl = (ClassLoader) AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws ClassNotFoundException {
                            return cls.getClassLoader();
                        }
                    }
            );
        } catch (PrivilegedActionException e) {
            if (log.isDebugEnabled()) {
                log.debug("Exception thrown from AccessController: " + e);
            }
            throw ExceptionFactory.makeWebServiceException(e.getException());
        }

        return cl;
    }

    public void setResolvedHandlersDescription(PortInfo portInfo, ResolvedHandlersDescription resolvedHandlersInfo) {
        // Get the cache and store the handler description
        Map<PortInfo, ResolvedHandlersDescription> cache = resolvedHandlersDescription.get();
        
        if (cache == null) {
            cache = new ConcurrentHashMap<PortInfo, ResolvedHandlersDescription>();
            resolvedHandlersDescription =
                new SoftReference<Map<PortInfo, ResolvedHandlersDescription>>(cache);
            
        }
        cache.put(portInfo, resolvedHandlersInfo);
        
    }

    
    public ResolvedHandlersDescription getResolvedHandlersDescription(PortInfo portInfo) {
        Map<PortInfo, ResolvedHandlersDescription> cache = resolvedHandlersDescription.get();

        return (cache == null) ?
                null: // No Cache
                cache.get(portInfo); 
       
    }
    
    private String resolveWSDLLocationByCatalog(String wsdlLocation) {
        if (catalogManager != null) {
            Catalog catalog = catalogManager.getCatalog();
            if (catalog != null) {
                String resolvedLocation = null;
                try {
                    resolvedLocation = catalog.resolveSystem(wsdlLocation);
                    if (resolvedLocation == null) {
                        resolvedLocation = catalog.resolveURI(wsdlLocation);
                    }
                    // normally, one might also do the following, but in this case we're looking at a top-level WSDL, so no parent
                    // resolvedLocation = catalog.resolvePublic(wsdlLocation, parent);
                    if (resolvedLocation != null) {
                        if (log.isDebugEnabled()) {
                            log.debug("XMLCatalog transformed original wsdl location \""
                                    + wsdlLocation
                                    + "\" to \""
                                    + resolvedLocation + "\"");
                        }
                        return resolvedLocation;
                    }
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug("Catalog resolution attempt caused exception: "
                                + e);
                    }
                }
            }
        }
        return wsdlLocation;
    }
    
    /**
     * Increment the use count for this ServiceDescription instance.  Note the use count is
     * only used on the client side.
     * @return
     */
    boolean isInUse() {
        return useCount > 0;
    }
    
    /**
     * Register that this ServiceDescription is being used by a service delegate instance.  Note
     * this is only used on the client side (since the service delegate is what is being 
     * registered).
     * 
     * Note that this is package protected since only the implementation classes should be calling
     * it.
     */
    void registerUse() {
        useCount++;
    }

    /**
     * Deregister that this ServiceDescription is being used by a service delegate instance.  Note
     * this is only used on the client side (since the service delegate is what is being 
     * registered).

     * Note that this is package protected since only the implementation classes should be calling
     * it.
     */
    void deregisterUse() {
        if (useCount > 0) {
            useCount--;
        }
    }

    public void releaseResources(Object delegate) {
        try {
        if (log.isDebugEnabled()) {
            log.debug("ServiceDescription release resources called with delegate " + delegate);
        }
        // If the service desc can be removed from the cache, which means no other service delegates
        // are using it, then we will release the resources associated with it.  If it can't be 
        // removed because it is still in use, then just return.
        if (!DescriptionFactoryImpl.removeFromCache(this)) {
            if (log.isDebugEnabled()) {
                log.debug("ServiceDesc was not removed from cache, so it will not be released");
            }
            return;
        }
        
        if (log.isDebugEnabled()) {
            log.debug("ServiceDesc was removed from cache, so releasing associated resources");
        }
                
        
        // Close all the endpoint descs, both declared and dynamic
        Collection<EndpointDescription> definedEndpoints = definedEndpointDescriptions.values(); 
        if (definedEndpoints.size() > 0) {
            if (log.isDebugEnabled()) {
                log.debug("Releasing defined endpoints, size: " + definedEndpoints.size());
            }
            for (EndpointDescription endpointDesc : definedEndpoints) {
                ((EndpointDescriptionImpl) endpointDesc).releaseResources(getAxisConfigContext());
            }
        }
        definedEndpointDescriptions.clear();
        
        Collection<Map<QName, EndpointDescriptionImpl>> dynamicEndpointsMap = 
            dynamicEndpointDescriptions.values();
        if (log.isDebugEnabled()) {
            log.debug("Releasing dynamic endpoints, size: " + dynamicEndpointsMap.size());
        }
        Iterator<Map<QName, EndpointDescriptionImpl>> dynamicEndpointsMapIterator = dynamicEndpointsMap.iterator();
        while (dynamicEndpointsMapIterator.hasNext()) {
            Map<QName, EndpointDescriptionImpl> mapEntry = dynamicEndpointsMapIterator.next();
            Collection<EndpointDescriptionImpl> dynamicEndpoints = mapEntry.values();
            if (dynamicEndpoints != null && dynamicEndpoints.size() > 0) {
                for (EndpointDescription endpointDesc : dynamicEndpoints) {
                    ((EndpointDescriptionImpl) endpointDesc).releaseResources(getAxisConfigContext());
                    // Remove this endpoint from the list on axis config 
                    removeFromDynamicEndpointCache(endpointDesc);
                }
            }
        }
        dynamicEndpointDescriptions.clear();
        
        } catch (Throwable t) {
            if (log.isDebugEnabled()) {
                log.debug("Release resorces in ServiceDesc caught throwable ", t);
            }
            throw ExceptionFactory.makeWebServiceException(t);
        }
    }
    
    /**
     * Remove the endpointDescription from the list of dynamic ports held on the
     * AxisConfiguration object.
     * 
     * @param endpointDesc The endpointDescription to be removed from the list.
     */
    private void removeFromDynamicEndpointCache(EndpointDescription endpointDesc) {
        AxisConfiguration configuration = configContext.getAxisConfiguration();
        Parameter parameter = configuration.getParameter(JAXWS_DYNAMIC_ENDPOINTS);
        HashMap cachedDescriptions = (HashMap)
                ((parameter == null) ? null : parameter.getValue());
        if (cachedDescriptions != null) {
            synchronized(cachedDescriptions) {
                Set cachedDescSet = cachedDescriptions.entrySet();
                Iterator cachedDescIterator = cachedDescSet.iterator();
                while (cachedDescIterator.hasNext()) {
                    Map.Entry mapEntry = (Map.Entry) cachedDescIterator.next();
                    WeakReference weakRef = (WeakReference) mapEntry.getValue();
                    if (weakRef != null) {
                        EndpointDescriptionImpl checkDynamicEndpointDesc = (EndpointDescriptionImpl) weakRef.get();
                        if (endpointDesc == checkDynamicEndpointDesc) {
                            cachedDescIterator.remove();
                            if (log.isDebugEnabled()) {
                                log.debug("Removing endpoint desc from dynamic cache on configuration");
                            }
                        }
                    }
                }
            }
        }
    }
}
