/*
 * 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.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.Constants.Configuration;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.AxisServiceGroup;
import org.apache.axis2.description.OutInAxisOperation;
import org.apache.axis2.description.OutOnlyAxisOperation;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.WSDL11ToAllAxisServicesBuilder;
import org.apache.axis2.description.WSDL11ToAxisServiceBuilder;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.catalog.JAXWSCatalogManager;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.EndpointDescriptionJava;
import org.apache.axis2.jaxws.description.EndpointDescriptionWSDL;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
import org.apache.axis2.jaxws.description.builder.CustomAnnotationInstance;
import org.apache.axis2.jaxws.description.builder.CustomAnnotationProcessor;
import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
import org.apache.axis2.jaxws.description.builder.MDQConstants;
import org.apache.axis2.jaxws.description.builder.WsdlComposite;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.axis2.jaxws.feature.ServerConfigurator;
import org.apache.axis2.jaxws.feature.ServerFramework;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.registry.ServerConfiguratorRegistry;
import org.apache.axis2.jaxws.util.CatalogURIResolver;
import org.apache.axis2.jaxws.util.WSDL4JWrapper;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.wsdl.util.WSDLDefinitionWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.jws.HandlerChain;
import javax.jws.WebService;
import javax.wsdl.Binding;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.http.HTTPBinding;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.wsdl.extensions.soap12.SOAP12Binding;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingType;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.PortInfo;
import javax.xml.ws.soap.MTOM;
import javax.xml.ws.soap.MTOMFeature;
import javax.xml.ws.soap.SOAPBinding;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/** @see ../EndpointDescription */
/*
 * IMPORTANT NOTE: Axis2 currently only supports 1 service and 1 port under that service.  When that is
 * fixed, that will probably have an impact on this class.  In particular, I think this should be created 
 * somehow from an AxisService/AxisPort combination, and not directly from the WSDL.
 */
class EndpointDescriptionImpl
        implements EndpointDescription, EndpointDescriptionJava, EndpointDescriptionWSDL {

    private ServiceDescriptionImpl parentServiceDescription;
    private AxisService axisService;
    // In some environments some of the resources on an AxisService can lead to OOMs.
    // However, releasing these resources can have other implications.  
    private boolean releaseAxisServiceResources = false;

    private QName portQName;
    private QName serviceQName;

    // Corresponds to a port that was added dynamically via addPort and is not declared (either in WSDL or annotations)
    private boolean isDynamicPort;

    // If the WSDL is fully specified, we could build the AxisService from the WSDL
    private boolean isAxisServiceBuiltFromWSDL;

    private String serviceImplName;    //class name of the service impl or SEI

    // Note that an EndpointInterfaceDescription will ONLY be set for an Endpoint-based implementation;
    // it will NOT be set for a Provider-based implementation
    private EndpointInterfaceDescription endpointInterfaceDescription;

    //On Client side, there should be One ServiceClient instance per AxisSerivce
    private ServiceClient serviceClient = null;

    //This is the base WebService or WebServiceProvider that we are processing
    DescriptionBuilderComposite composite = null;

    // Set of packages that are needed to marshal/unmashal data (used to set JAXBContext)
    TreeSet<String> packages = null;

    // The JAX-WS Handler port information corresponding to this endpoint
    private PortInfo portInfo;

    private String clientBindingID;
    // The effective endpoint address.  It could be set by the client or come from the WSDL SOAP address
    private String endpointAddress;
    // The endpoint address from the WSDL soap:address extensibility element if present.
    private String wsdlSOAPAddress;

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

    // ===========================================
    // ANNOTATION related information
    // ===========================================

    // ANNOTATION: @WebService and @WebServiceProvider
    // Only one of these two annotations will be set; they are mutually exclusive
    private WebService webServiceAnnotation;
    private WebServiceProvider webServiceProviderAnnotation;

    //ANNOTATION: @HandlerChain
    private HandlerChain handlerChainAnnotation;
    private HandlerChainsType handlerChainsType;

    // Information common to both WebService and WebServiceProvider annotations
    private String annotation_WsdlLocation;
    private String annotation_ServiceName;
    private String annotation_PortName;
    private String annotation_TargetNamespace;

    // Information only set on WebService annotation
    // ANNOTATION: @WebService
    private String webService_EndpointInterface;
    private String webService_Name;

    // ANNOTATION: @ServiceMode
    // Note this is only valid on a Provider-based endpoint
    private ServiceMode serviceModeAnnotation;
    private Service.Mode serviceModeValue;
    // Default ServiceMode.value per JAXWS Spec 7.1 "javax.xml.ServiceMode" pg 79
    public static final javax.xml.ws.Service.Mode ServiceMode_DEFAULT =
            javax.xml.ws.Service.Mode.PAYLOAD;

    // ANNOTATION: @BindingType
    private BindingType bindingTypeAnnotation;
    private String bindingTypeValue;
    // Default BindingType.value per JAXWS Spec Sec 7.8 "javax.xml.ws.BindingType" pg 83 
    // and Sec 1.4 "SOAP Transport and Transfer Bindings" pg 119
    public static final String BindingType_DEFAULT =
            javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
    
    // ANNOTATION: @RespectBinding
    private Boolean respectBinding = false;
    private List requiredBindings;
    
    private Integer portCompositeIndex = null;
    
    private List<CustomAnnotationInstance> customAnnotations;
    
    private Map<String, CustomAnnotationProcessor> customAnnotationProcessors;

    // Supports WebServiceFeatureAnnotations
    private ServerFramework framework = new ServerFramework();
    
    private Map<String, Object> properties;
    
    // Remembers if this endpoint description is MTOMEnabled
    private Boolean isMTOMEnabledCache = null;

    
    /**
     * Create a service-requester side EndpointDescription based on the WSDL port.  
     * Note that per the JAX-WS Spec (Final
     * Release, 4/19/2006 Section 4.2.3 Proxies, page 55)the "namespace component of the port is the
     * target namespace of the WSDL definition document". Note this is currently only used on the
     * client-side (this may change).
     *
     * @param theClass The SEI or Impl class.  This will be NULL for Dispatch clients since they
     *                 don't use an SEI
     */
    EndpointDescriptionImpl(Class theClass, QName portName, ServiceDescriptionImpl parent) {
        this(theClass, portName, parent, null, null);
    }
    EndpointDescriptionImpl(Class theClass, QName portName, ServiceDescriptionImpl parent, 
                            DescriptionBuilderComposite dbc, Object compositeKey ) {
        this(theClass, portName, false, parent, dbc, compositeKey);
    }
    EndpointDescriptionImpl(Class theClass, QName portName, boolean dynamicPort,
                            ServiceDescriptionImpl parent) {
        this(theClass, portName, dynamicPort, parent, null, null);
    }
    EndpointDescriptionImpl(Class theClass, QName portName, boolean dynamicPort,
                            ServiceDescriptionImpl parent, 
                            DescriptionBuilderComposite sparseComposite,
                            Object sparseCompositeKey) {

        this.parentServiceDescription = parent;
        composite = new DescriptionBuilderComposite();
        composite.setSparseComposite(sparseCompositeKey, sparseComposite);
        composite.setCorrespondingClass(theClass);
        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        return this.getClass().getClassLoader();
                    }
                }
        );
        composite.setClassLoader(loader);
        composite.setIsServiceProvider(false);

        webServiceAnnotation = composite.getWebServiceAnnot();
        
        this.isDynamicPort = dynamicPort;
        if (DescriptionUtils.isEmpty(portName)) {
            // If the port name is null, then per JAX-WS 2.0 spec p. 55, the runtime is responsible for selecting the port.
            this.portQName = selectPortToUse();
        } else {
            this.portQName = portName;
        }
        // At this point, there must be a port QName set, either as passed in, or determined from the WSDL and/or annotations.
        // If not, that is an error.
        if (this.portQName == null) {
            String msg = Messages.getMessage("endpointDescriptionErr1",theClass.getName(),parent.getClass().getName());
            throw ExceptionFactory.makeWebServiceException(msg);
        }

        setupAxisService();
        addToAxisService();
        setupReleaseResources(getServiceDescription().getAxisConfigContext());

        buildDescriptionHierachy();
        addAnonymousAxisOperations();

        // This will set the serviceClient field after adding the AxisService to the AxisConfig
        getServiceClient();
        // Give the configuration builder a chance to finalize configuration for this service
        try {
            getServiceDescriptionImpl().getClientConfigurationFactory()
                    .completeAxis2Configuration(axisService);
        } catch (Exception e) {
            String msg = Messages.getMessage("endpointDescriptionErr2",e.getClass().getName(),parent.getClass().getName());
            throw ExceptionFactory.makeWebServiceException(msg, e);
        } finally {
            releaseAxisServiceResources();
        }
        
    }
    
    private void setupReleaseResources(ConfigurationContext configurationContext) {
        if (configurationContext != null) {
            AxisConfiguration axisConfiguration = configurationContext.getAxisConfiguration();
            if (axisConfiguration != null) {
                Parameter param = 
                    axisConfiguration.getParameter(Constants.Configuration.REDUCE_WSDL_MEMORY_CACHE);
                if (param != null) {
                    releaseAxisServiceResources = ((String) param.getValue()).equalsIgnoreCase("true");
                    if (log.isDebugEnabled()) {
                        log.debug("EndpointDescription configured to release AxisService resources via "
                                  + Constants.Configuration.REDUCE_WSDL_MEMORY_CACHE);
                    }
                }
            }
        }
        else if(composite != null) {
            Boolean reduceCache = (Boolean) composite.getProperties().get(Constants.Configuration.REDUCE_WSDL_MEMORY_CACHE);
            if(reduceCache != null) {
                if(log.isDebugEnabled()) {
                    log.debug("Retrieved the following reduce WSDL cache value: " + reduceCache + 
                              " from the composite: " + composite.getClassName());
                }
                releaseAxisServiceResources = reduceCache;
            }
        }
        
    }
    EndpointDescriptionImpl(ServiceDescriptionImpl parent, String serviceImplName) {
        this(parent, serviceImplName, null, null);
    }

    /**
     * Create a service-provider side EndpointDescription based on the DescriptionBuilderComposite. 
     * Note that per the
     * JAX-WS Spec (Final Release, 4/19/2006 Section 4.2.3 Proxies, page 55)the "namespace component
     * of the port is the target namespace of the WSDL definition document".
     *
     * @param theClass The SEI or Impl class.  This will be NULL for Dispatch clients since they
     *                 don't use an SEI
     */
    EndpointDescriptionImpl(ServiceDescriptionImpl parent, String serviceImplName, Map<String, Object>
        properties, Integer portCompositeIndex) {
        
        // initialize CustomAnnotationIntance list and CustomAnnotationProcessor map
        customAnnotations = new ArrayList<CustomAnnotationInstance>();
        customAnnotationProcessors = new HashMap<String, CustomAnnotationProcessor>();
        this.portCompositeIndex = portCompositeIndex;
        
        // set properties map
        this.properties = properties;
        
        this.parentServiceDescription = parent;
        this.serviceImplName = serviceImplName;

        // if the ServiceDescription's service QName is specified, let's use that to get the
        // correct DescriptionBuilderComposite
        if(parent.getServiceQName() != null) {
            composite = getServiceDescriptionImpl().getDescriptionBuilderComposite(parent.getServiceQName(),
                                                                                   portCompositeIndex); 
        }
        
        // otherwise we will get the DescriptionBuilderComposite by the current index
        else {
            composite = getServiceDescriptionImpl().getDescriptionBuilderComposite(null,
                                                                                   portCompositeIndex);
        }
        
        if (composite == null) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("endpointDescriptionErr3"));
        }
        
        if(composite.getHandlerChainAnnot() != null && composite.getHandlerChainsType() != null) {
        	throw ExceptionFactory.makeWebServiceException(
            Messages.getMessage("handlerSourceFail", composite.getClassName()));
        }
        
        handlerChainsType = composite.getHandlerChainsType();

        //Set the base level of annotation that we are processing...currently
        // a 'WebService' or a 'WebServiceProvider'
        if (composite.getWebServiceAnnot() != null)
            webServiceAnnotation = composite.getWebServiceAnnot();
        else
            webServiceProviderAnnotation = composite.getWebServiceProviderAnnot();

        
        // now get the custom annotation and process information from the DBC
        customAnnotations.addAll(composite.getCustomAnnotationInstances());
        customAnnotationProcessors.putAll(composite.getCustomAnnotationProcessors());
        
        // Note that on the client side, the service QN should be set; on the server side it will not be.
        if (DescriptionUtils.isEmpty(getServiceDescription().getServiceQName())) {
            getServiceDescriptionImpl().setServiceQName(getServiceQName());
        }
        //Call the getter to insure the qualified port name is set. 
        getPortQName();

        setupAxisServiceFromDBL();
        addToAxisService();    //Add a reference to this EndpointDescription to the AxisService

        buildDescriptionHierachy();

        WsdlComposite wsdlComposite = null;
        
        String bindingType = getBindingType();

        boolean isSOAP11 =
                (bindingType.equals(javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING) || 
                        bindingType.equals(javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING))
                        ? true : false;


        // Determine if we need to generate WSDL
        // First, make sure that this is only a SOAP 1.1 based binding, per JAXWS spec. we cannot 
        // generate WSDL if the binding type is not SOAP 1.1 based.
        // Then, assuming the composite does not contain a 
        // Wsdl Definition, go ahead and generate it
        if (isSOAP11){
            if (
                    (isEndpointBased() &&
                            DescriptionUtils.isEmpty(getAnnoWebServiceEndpointInterface()))
                            ||
                            (!isEndpointBased())
                    ) {
                //This is either an implicit SEI, or a WebService Provider
    
                wsdlComposite = generateWSDL(composite);
    
            } else if (isEndpointBased()) {
                //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 =
                        getServiceDescriptionImpl().getDBCMap()
                                .get(composite.getWebServiceAnnot().endpointInterface());
    
                //Only generate WSDL if a definition doesn't already exist
                if (seic.getWsdlDefinition() == null)
                    wsdlComposite = generateWSDL(composite);
            }

        } else if (composite.getWsdlDefinition() == null) {
            //This is a SOAP12 binding that does not contain a WSDL definition, log a WARNING
            log.warn("This implementation does not contain a WSDL definition and is not a SOAP 1.1 based binding. "
                    + "Per JAXWS spec. - a WSDL definition cannot be generated for this implementation. Name: "
                    + composite.getClassName());
        }

        if (isSOAP11){
    
            //Save the WSDL Location and the WsdlDefinition, value depends on whether wsdl was generated
            Parameter wsdlLocationParameter = new Parameter();
            wsdlLocationParameter.setName(MDQConstants.WSDL_LOCATION);
    
            Parameter wsdlDefParameter = new Parameter();
            wsdlDefParameter.setName(MDQConstants.WSDL_DEFINITION);
    
            Parameter wsdlCompositeParameter = new Parameter();
            wsdlCompositeParameter.setName(MDQConstants.WSDL_COMPOSITE);
    
            if (wsdlComposite != null) {
    
                //We have a wsdl composite, so set these values for the generated wsdl
                wsdlCompositeParameter.setValue(wsdlComposite);
                wsdlLocationParameter.setValue(wsdlComposite.getWsdlFileName());

                Definition def =
                        getServiceDescriptionImpl().getGeneratedWsdlWrapper().getDefinition();
                URL wsdlUrl = getServiceDescriptionImpl().getGeneratedWsdlWrapper().getWSDLLocation();
                if (def instanceof WSDLDefinitionWrapper) {
                    wsdlDefParameter.setValue(def);
                } else {
                    // Create WSDLDefinitionWrapper
                    WSDLDefinitionWrapper wrap = null;
                    ConfigurationContext cc = composite.getConfigurationContext();
                    if (cc != null && cc.getAxisConfiguration() != null) {
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, 
                                                         cc.getAxisConfiguration());
                    } else {
                        // Probably shouldn't get here.  But if we do, use
                        // a memory sensitve wsdl wrapper
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, true, 2);
                    }
                    wsdlDefParameter.setValue(wrap);
                }

            } else if (getServiceDescriptionImpl().getWSDLWrapper() != null) {
                //No wsdl composite because wsdl already exists

                wsdlLocationParameter.setValue(getAnnoWebServiceWSDLLocation());

                Definition def = getServiceDescriptionImpl().getWSDLWrapper().getDefinition();
                URL wsdlUrl = getServiceDescriptionImpl().getWSDLWrapper().getWSDLLocation();
                if (def instanceof WSDLDefinitionWrapper) {
                    wsdlDefParameter.setValue(def);
                } else {
                    // Create WSDLDefinitionWrapper
                    WSDLDefinitionWrapper wrap = null;
                    ConfigurationContext cc = composite.getConfigurationContext();
                    if (cc != null && cc.getAxisConfiguration() != null) {
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, 
                                                         cc.getAxisConfiguration());
                    } else {
                        // Probably shouldn't get here.  But if we do, use
                        // a memory sensitve wsdl wrapper
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, true, 2);
                    }
                    wsdlDefParameter.setValue(wrap);
                }

            } else {
                //There is no wsdl composite and there is NOT a wsdl definition
                wsdlLocationParameter.setValue(null);
                wsdlDefParameter.setValue(null);
    
            }
    
            try {
                if (wsdlComposite != null) {
                    axisService.addParameter(wsdlCompositeParameter);
                }
                axisService.addParameter(wsdlDefParameter);
                axisService.addParameter(wsdlLocationParameter);
            } catch (Exception e) {
                throw ExceptionFactory.makeWebServiceException(Messages.getMessage("endpointDescriptionErr4"));
            }
        }
        else {
            // Need to account for SOAP 1.2 WSDL when supplied with application
            Parameter wsdlDefParameter = new Parameter();
            wsdlDefParameter.setName(MDQConstants.WSDL_DEFINITION);
            Parameter wsdlLocationParameter = new Parameter();
            wsdlLocationParameter.setName(MDQConstants.WSDL_LOCATION);
            if (getServiceDescriptionImpl().getWSDLWrapper() != null) {
                wsdlLocationParameter.setValue(getAnnoWebServiceWSDLLocation());

                Definition def = getServiceDescriptionImpl().getWSDLWrapper().getDefinition();
                URL wsdlUrl = getServiceDescriptionImpl().getWSDLWrapper().getWSDLLocation();
                if (def instanceof WSDLDefinitionWrapper) {
                    wsdlDefParameter.setValue(def);
                } else {
                    // Create WSDLDefinitionWrapper
                    WSDLDefinitionWrapper wrap = null;
                    ConfigurationContext cc = composite.getConfigurationContext();
                    if (cc != null && cc.getAxisConfiguration() != null) {
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, 
                                                         cc.getAxisConfiguration());
                    } else {
                        // Probably shouldn't get here.  But if we do, use
                        // a memory sensitve wsdl wrapper
                        wrap = new WSDLDefinitionWrapper(def, wsdlUrl, true, 2);
                    }
                    wsdlDefParameter.setValue(wrap);
                }
            }
            // No WSDL supplied and we do not generate for non-SOAP 1.1/HTTP
            // endpoints
            else {
                wsdlLocationParameter.setValue(null);
                wsdlDefParameter.setValue(null);
            }
            try {
                axisService.addParameter(wsdlDefParameter);
                axisService.addParameter(wsdlLocationParameter);

            } catch (Exception e) {
                throw ExceptionFactory
                    .makeWebServiceException(Messages.getMessage("endpointDescriptionErr4"),e);
            }
        }
        
        // Before we leave we need to drive the CustomAnnotationProcessors if 
        // there were any CustomAnnotationInstance objects registered
        Iterator<CustomAnnotationInstance> annotationIter = customAnnotations.iterator();
        while(annotationIter.hasNext()) {
            CustomAnnotationInstance annotation = annotationIter.next();
            if(log.isDebugEnabled()) {
                log.debug("Checking for CustomAnnotationProcessor for CustomAnnotationInstance " +
                                "class: " + annotation.getClass().getName());
            }
            CustomAnnotationProcessor processor = customAnnotationProcessors.get(annotation.getClass().getName());
            if(processor != null) {
                if(log.isDebugEnabled()) {
                    log.debug("Found CustomAnnotationProcessor: " + processor.getClass().getName() + 
                              " for CustomAnnotationInstance: " + annotation.getClass().getName());
                }
                processor.processTypeLevelAnnotation(this, annotation);
            }
        }
        
        // Configure any available WebServiceFeatures on the endpoint.
        configureWebServiceFeatures();
        
        // REVIEW: there are some throws above that won't cause the release
        setupReleaseResources(composite.getConfigurationContext());
        releaseAxisServiceResources();
    }

    private void addToAxisService() {
        // Add a reference to this EndpointDescription object to the AxisService
        if (axisService != null) {
            Parameter parameter = new Parameter();
            parameter.setName(EndpointDescription.AXIS_SERVICE_PARAMETER);
            parameter.setValue(this);
            try {
                axisService.addParameter(parameter);
            } catch (AxisFault e) {
            	throw ExceptionFactory.makeWebServiceException(Messages.getMessage("endpointDescriptionErr5"),e);
            }
        }
    }

    private void buildEndpointDescriptionFromAnnotations() {
        // TODO: The comments below are not quite correct; this method is used on BOTH the 
        //       client and server.  On the client the class is always an SEI.  On the server it 
        //		 is always a service impl which may be a provider or endpoint based;
        //		 endpoint based may reference an SEI class

        // The Service Implementation class could be either Provider-based or Endpoint-based.  The 
        // annotations that are present are similar but different.  Conformance requirements 
        // per JAX-WS
        // - A Provider based implementation MUST carry the @WebServiceProvider annotation
        //   per section 5.1 javax.xml.ws.Provider on page 63
        // - An Endpoint based implementation MUST carry the @WebService annotation per JSR-181 
        //   (reference TBD) and JAX-WS (reference TBD)
        // - An Endpoint based implementation @WebService annotation MAY reference an endpoint
        //   interface 
        // - The @WebService and @WebServiceProvider annotations can not appear in the same class per 
        //   JAX-WS section 7.7 on page 82.

        // If portName was specified, set it.  Otherwise, we will get it from the appropriate
        // annotation when the getter is called.

        // If this is an Endpoint-based service implementation (i.e. not a 
        // Provider-based one), then create the EndpointInterfaceDescription to contain
        // the operations on the endpoint.  Provider-based endpoints don't have operations
        // associated with them, so they don't have an EndpointInterfaceDescription.
        if (webServiceAnnotation != null) {
            // If this impl class references an SEI, then use that SEI to create the EndpointInterfaceDesc.
            String seiClassName = getAnnoWebServiceEndpointInterface();

            if (!composite.isServiceProvider()) {
                Class seiClass = null;
                if (DescriptionUtils.isEmpty(seiClassName)) {
                    // This is the client code path; the @WebServce will not have an endpointInterface member
                    // For now, just build the EndpointInterfaceDesc based on the class itself.
                    seiClass = composite.getCorrespondingClass();
                }
                endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(seiClass, this);
            } else {
                if (DescriptionUtils.isEmpty(getAnnoWebServiceEndpointInterface())) {

                    endpointInterfaceDescription =
                            new EndpointInterfaceDescriptionImpl(composite, true, this);

                } else {
                    //Otherwise, build the EID based on the SEI composite
                    endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(
                            getServiceDescriptionImpl().getDBCMap().get(seiClassName),
                            false,
                            this);
                    
                    // after this is constructed, we need to update the @WebService.name 
                    // attribute on the axisService instance
                    if(axisService != null) {
                        updateWebServiceNameParameter(((EndpointInterfaceDescriptionImpl) 
                                endpointInterfaceDescription).getAnnoWebServiceName(), axisService); 
                    }
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("WebServiceProvider without WSDL encountered");
            }
            String bindingType = getBindingType();
            if (javax.xml.ws.http.HTTPBinding.HTTP_BINDING.equals(bindingType)||
                    SOAPBinding.SOAP11HTTP_BINDING.equals(bindingType)||
                    SOAPBinding.SOAP12HTTP_BINDING.equals(bindingType)||
                    MDQConstants.SOAP_HTTP_BINDING.equals(bindingType)) {
                endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(composite, this);
            }
        }
    }

    public QName getPortQName() {
        if (portQName == null) {
            // The name was not set by the constructors, so get it from the
            // appropriate annotation.
            String name = getAnnoWebServicePortName();
            String tns = getAnnoWebServiceTargetNamespace();
            portQName = new QName(tns, name);
        }
        return portQName;
    }

    public QName getServiceQName() {
        if (serviceQName == null) {
            // If the service name has been set on the Service, use that.  Otherwise
            // get the name off the annotations
            QName serviceDescQName = getServiceDescription().getServiceQName();
            if (!DescriptionUtils.isEmpty(serviceDescQName)) {
                serviceQName = serviceDescQName;
            } else {
                String localPart = getAnnoWebServiceServiceName();
                String tns = getAnnoWebServiceTargetNamespace();
                serviceQName = new QName(tns, localPart);
            }
        }
        return serviceQName;
    }

    public ServiceDescription getServiceDescription() {
        return parentServiceDescription;
    }

    ServiceDescriptionImpl getServiceDescriptionImpl() {
        return (ServiceDescriptionImpl)parentServiceDescription;
    }

    public EndpointInterfaceDescription getEndpointInterfaceDescription() {
        return endpointInterfaceDescription;
    }

    public AxisService getAxisService() {
        return axisService;
    }

    boolean isDynamicPort() {
        return isDynamicPort;
    }

    void updateWithSEI(Class sei, DescriptionBuilderComposite sparseComposite, Object sparseCompositeKey) {
        // Updating with an SEI is only valid for declared ports; it is not valid for dynamic ports.
        if (isDynamicPort()) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("updateWithSEIErr1",portQName.toString()));
        }
        if (sei == null) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("updateWithSEIErr2",portQName.toString()));
        }
        
        composite.setSparseComposite(sparseCompositeKey, sparseComposite);

        if (endpointInterfaceDescription != null) {
            // The EndpointInterfaceDescription was created previously based on the port declaration (i.e. WSDL)
            // so update that with information from the SEI annotations
            ((EndpointInterfaceDescriptionImpl)endpointInterfaceDescription).updateWithSEI(sei);
        } else {
            // An EndpointInterfaceDescription does not exist yet.  This currently happens in the case where there is 
            // NO WSDL provided and a Dispatch client is created for prior to a getPort being done for that port.
            // There was no WSDL to create the EndpointInterfaceDescription from and there was no annotated SEI to
            // use at that time.  Now we have an annotated SEI, so create the EndpointInterfaceDescription now.
            endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(sei, this);
        }
        return;
    }

    private void setupAxisService() {
        // Build up the AxisService.  Note that if this is a dynamic port, then we don't use the
        // WSDL to build up the AxisService since the port added to the Service by the client is not
        // one that will be present in the WSDL.  A null class passed in as the SEI indicates this 
        // is a dispatch client.
        if (!isDynamicPort && getServiceDescriptionImpl().getWSDLWrapper() != null) {
            isAxisServiceBuiltFromWSDL = buildAxisServiceFromWSDL();
        } else {
            buildAxisServiceFromAnnotations();
        }

        if (axisService == null) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("setupAxisServiceErr1",createAxisServiceName()));
        }

        // Save the Service QName as a parameter.
        Parameter serviceNameParameter = new Parameter();
        serviceNameParameter.setName(WSDL11ToAllAxisServicesBuilder.WSDL_SERVICE_QNAME);
        serviceNameParameter.setValue(getServiceDescription().getServiceQName());

        // Save the Port name.  Note: Axis does not expect a QName since the namespace for the port is the ns from the WSDL definition 
        Parameter portParameter = new Parameter();
        portParameter.setName(WSDL11ToAllAxisServicesBuilder.WSDL_PORT);
        portParameter.setValue(portQName.getLocalPart());
        
        // Store the service class fully qualified name
        Parameter serviceClassNameParam = new Parameter();
        serviceClassNameParam.setName(MDQConstants.CLIENT_SERVICE_CLASS);
        String serviceClassName = this.getServiceDescriptionImpl().getServiceClassName();
        if(log.isDebugEnabled()) {
            log.debug("Setting service class name parameter to: " + serviceClassName + 
                      " on AxisService: " + axisService + "@" + axisService.hashCode());
        }
        serviceClassNameParam.setValue(serviceClassName);
        
        // Store the sei class fully qualified name, if it is available
        Parameter seiClassNameParam = new Parameter();
        seiClassNameParam.setName(MDQConstants.CLIENT_SEI_CLASS);
        String seiClassName = composite.getClassName();
        if(log.isDebugEnabled()) {
            log.debug("Setting sei class name parameter to: " + seiClassName + 
                      " on AxisService: " + axisService + "@" + axisService.hashCode());
        }
        seiClassNameParam.setValue(seiClassName);

        try {
            axisService.addParameter(serviceNameParameter);
            axisService.addParameter(portParameter);
            axisService.addParameter(serviceClassNameParam);
            axisService.addParameter(seiClassNameParam);
        }
        catch (AxisFault e) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("setupAxisServiceErr2"),e);
        }
    }

    /*
     * This setups and builds the AxisService using only the DescriptionBuilderCompositeList
     * 
     */
    private void setupAxisServiceFromDBL() {
        // Build up the AxisService.  Note that if this is a dispatch client, then we don't use the
        // WSDL to build up the AxisService since the port added to the Service by the client is not
        // one that will be present in the WSDL.  A null class passed in as the SEI indicates this 
        // is a dispatch client.

        // If WSDL is present, it may be full or only partial.  If we can create the AxisService from 
        // the WSDL, that WSDL is fully specified.  Otherwise, it is "partial WSDL".  In that case
        // we use annotaions to build the AxisService
        isAxisServiceBuiltFromWSDL = false;
        if (getServiceDescriptionImpl().getWSDLWrapper() != null) {
            isAxisServiceBuiltFromWSDL = buildAxisServiceFromWSDL();

        }

        if (!isAxisServiceBuiltFromWSDL) {
            //generateWSDL(composite);
            buildAxisServiceFromAnnotations();
        }

        if (axisService == null) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("setupAxisServiceErr1",createAxisServiceName()));
        }
        
        //Save the Port Type name
        Parameter portTypeNameParameter = new Parameter();
        portTypeNameParameter.setName(MDQConstants.WSDL_PORTTYPE_NAME);
        portTypeNameParameter.setValue(getName());

        // Save the Service QName as a parameter.
        Parameter serviceNameParameter = new Parameter();
        serviceNameParameter.setName(MDQConstants.WSDL_SERVICE_QNAME);
        serviceNameParameter.setValue(getServiceDescription().getServiceQName());

        // Save the Port name.  Note: Axis does not expect a QName since the namespace
        //   for the port is the ns from the WSDL definition 
        Parameter portParameter = new Parameter();
        portParameter.setName(MDQConstants.WSDL_PORT);
        portParameter.setValue(getPortQName().getLocalPart());

        //Save the fully qualified class name for the serviceImpl
        Parameter serviceClassNameParameter = new Parameter();
        serviceClassNameParameter.setName(MDQConstants.SERVICE_CLASS);
        serviceClassNameParameter
                .setValue(DescriptionUtils.javifyClassName(composite.getClassName()));

        try {
            axisService.addParameter(portTypeNameParameter);
            axisService.addParameter(serviceNameParameter);
            axisService.addParameter(portParameter);
            axisService.addParameter(serviceClassNameParameter);
        }
        catch (AxisFault e) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("setupAxisServiceErr2"),e);
        }
    }

    private boolean buildAxisServiceFromWSDL() {
        boolean isBuiltFromWSDL = false;
        try {

            // Note that the axis service builder takes only the localpart of the port qname.
            // TODO:: This should check that the namespace of the definition matches the namespace of the portQName per JAXRPC spec

            
            // Use getDefinition() so that we have the advantages of the memory features.
            Definition def = getServiceDescriptionImpl().getWSDLWrapper().getDefinition();

            WSDL11ToAxisServiceBuilder serviceBuilder =
                    new WSDL11ToAxisServiceBuilder(def,
                            getServiceDescription().getServiceQName(),
                            getPortQName().getLocalPart());

            if (log.isDebugEnabled()) {
                log.debug("Building AxisService from wsdl: " + getServiceDescriptionImpl().getWSDLWrapper().getWSDLLocation());    
            }
                        
            ClassLoader classLoader;
            if (composite.isServiceProvider()) {
                classLoader = composite.getClassLoader();
            } else {
                classLoader = getContextClassLoader(null);
            }
            JAXWSCatalogManager catalogManager = getServiceDescriptionImpl().getCatalogManager();
            CatalogURIResolver uriResolver = new CatalogURIResolver(catalogManager, classLoader);
            serviceBuilder.setCustomResolver(uriResolver);

            if (getServiceDescriptionImpl().isServerSide())
                serviceBuilder.setServerSide(true);
            else
                serviceBuilder.setServerSide(false);

            // Associate the AxisConfiguration with the ServiceBuilder if it
            // is available.  This is done so that the serviceBuilder can
            // use the appropriate WSDL wrapper memory parameters.
            AxisConfiguration ac = null;
            if (composite.getConfigurationContext() != null) {
                ac = composite.getConfigurationContext().getAxisConfiguration();
                if (ac != null) {
                    serviceBuilder.useAxisConfiguration(ac);
                }
            }
            // Create and populate the AxisService
            axisService = serviceBuilder.populateService();
            
            // If an AxisConfiguration was not available,
            // default to using a memory efficient wrapper
            if (ac == null) {
                Parameter wsdlWrapperParam = 
                    axisService.getParameter(WSDLConstants.WSDL_4_J_DEFINITION);
                if (wsdlWrapperParam != null &&
                    wsdlWrapperParam.getValue() instanceof WSDLDefinitionWrapper) {
                    
                    WSDLDefinitionWrapper wrapper = 
                        (WSDLDefinitionWrapper)  wsdlWrapperParam.getValue();
                    
                    // If only the basic wrapper is being used, upgrade to the
                    // RELOAD wrapper
                    if (wrapper.getMemoryLimitType() == 0) {
                        Definition wsdlDef = wrapper.getUnwrappedDefinition();

                        WSDLDefinitionWrapper wrapper2 = 
                            new WSDLDefinitionWrapper(wsdlDef, true, 2);

                        wsdlWrapperParam.setValue(wrapper2);
                    }
                }
            }
            axisService.setName(createAxisServiceName());
            isBuiltFromWSDL = true;

        } catch (AxisFault e) {
            String wsdlLocation = (getServiceDescriptionImpl().getWSDLLocation() != null) ?
                    getServiceDescriptionImpl().getWSDLLocation().toString() : null;
            String implClassName = composite.getClassName();
            log.warn(Messages.getMessage("bldAxisSrvcFromWSDLErr",implClassName,wsdlLocation),e);

            isBuiltFromWSDL = false;
            return isBuiltFromWSDL;
        }
        return isBuiltFromWSDL;
    }

    private void buildAxisServiceFromAnnotations() {
        String serviceName = null;
        if (portQName != null) {
            serviceName = createAxisServiceName();
        } else {
            // Make this service name unique.  The Axis2 engine assumes that a service it can not find is a client-side service.
            serviceName = ServiceClient.ANON_SERVICE + this.hashCode() + System.currentTimeMillis();
        }
        axisService = new AxisService(serviceName);

    }

    private void releaseAxisServiceResources() {
        // release the schema list in the AxisService
        if (releaseAxisServiceResources && axisService != null) {
            axisService.releaseSchemaList();
        }
    }

    private void buildDescriptionHierachy() {
        // Build up the Description Hierachy.  Note that if this is a dynamic port, then we don't use the
        // WSDL to build up the hierachy since the port added to the Service by the client is not
        // one that will be present in the WSDL.

        if (composite.isServiceProvider()) {
            if (!isDynamicPort && isWSDLFullySpecified())
                buildEndpointDescriptionFromWSDL();
            else
                buildEndpointDescriptionFromAnnotations();
        } else {
            //Still processing annotations from the class
            // This path was not updated
            if (!isDynamicPort && isWSDLFullySpecified()) {
                buildEndpointDescriptionFromWSDL();
            } else if (composite.getCorrespondingClass() != null) {
                // Create the rest of the description hierachy from annotations on the class.
                // If there is no SEI class, then this is a Distpach case, and we currently
                // don't create the rest of the description hierachy (since it is not an SEI and thus
                // not operation-based client.
                buildEndpointDescriptionFromAnnotations();
            }
        }
    }

    private void buildEndpointDescriptionFromWSDL() {
        Definition wsdlDefinition = getServiceDescriptionImpl().getWSDLWrapper().getDefinition();
        javax.wsdl.Service wsdlService =
                wsdlDefinition.getService(getServiceDescription().getServiceQName());
        if (wsdlService == null) {
            throw ExceptionFactory.makeWebServiceException(
                    Messages.getMessage("serviceDescErr2", createAxisServiceName()));
        }

        Map wsdlPorts = wsdlService.getPorts();
        boolean wsdlPortFound = false;
        if (wsdlPorts != null && wsdlPorts.size() > 0) {
            Iterator wsdlPortIterator = wsdlPorts.values().iterator();

            while (wsdlPortIterator.hasNext() && !wsdlPortFound) {
                Port wsdlPort = (Port)wsdlPortIterator.next();
                // Note the namespace is not included on the WSDL Port.
                if (wsdlPort.getName().equals(portQName.getLocalPart())) {

                    // Build the EndpointInterface based on the specified SEI if there is one
                    // or on the service impl class (i.e. an implicit SEI).
                    if (composite.isServiceProvider()) {
                        String seiClassName = getAnnoWebServiceEndpointInterface();
                        if (DescriptionUtils.isEmpty(seiClassName)) {
                            // No SEI specified, so use the service impl as an implicit SEI
                            endpointInterfaceDescription =
                                    new EndpointInterfaceDescriptionImpl(composite, true, this);
                        } else {
                            // Otherwise, build the EID based on the SEI composite
                            endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(
                                    getServiceDescriptionImpl().getDBCMap().get(seiClassName),
                                    false,
                                    this);
                            
                            // after this is constructed, we need to update the @WebService.name 
                            // attribute on the axisService instance
                            if(axisService != null) {
                                updateWebServiceNameParameter(((EndpointInterfaceDescriptionImpl) 
                                        endpointInterfaceDescription).getAnnoWebServiceName(), axisService); 
                            }
                        }

                    } else {
                        // Create the Endpoint Interface Description based on the WSDL.
                        endpointInterfaceDescription = new EndpointInterfaceDescriptionImpl(this);

                        // Update the EndpointInterfaceDescription created with WSDL with information from the
                        // annotations in the SEI
                        ((EndpointInterfaceDescriptionImpl)endpointInterfaceDescription)
                                .updateWithSEI(composite.getCorrespondingClass());
                    }
                    wsdlPortFound = true;
                }
            }
        }

        if (!wsdlPortFound) {
            throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDescErr3",portQName.getLocalPart()));
        }
    }

    /**
     * Adds the anonymous axis operations to the AxisService.  Note that this is only needed on the
     * client side, and they are currently used in two cases (1) For Dispatch clients (which don't
     * use SEIs and thus don't use operations) (2) TEMPORARLIY for Services created without WSDL
     * (and thus which have no AxisOperations created) See the AxisInvocationController invoke
     * methods for more details.
     * <p/>
     * Based on ServiceClient.createAnonymouService
     */
    private void addAnonymousAxisOperations() {
        if (axisService != null) {
            OutOnlyAxisOperation outOnlyOperation =
                    new OutOnlyAxisOperation(ServiceClient.ANON_OUT_ONLY_OP);
            axisService.addOperation(outOnlyOperation);
            outOnlyOperation.setSoapAction(null);

            OutInAxisOperation outInOperation =
                    new OutInAxisOperation(ServiceClient.ANON_OUT_IN_OP);
            axisService.addOperation(outInOperation);
            outInOperation.setSoapAction(null);
        }
    }

    public ServiceClient getServiceClient() {
        try {
            if (serviceClient == null) {
                ConfigurationContext configCtx = getServiceDescription().getAxisConfigContext();
                AxisService axisSvc = getAxisService();
                AxisConfiguration axisCfg = configCtx.getAxisConfiguration();
                if (axisCfg.getService(axisSvc.getName()) != null) {
                    axisSvc.setName(axisSvc.getName() + uniqueID());
                }
                serviceClient = new ServiceClient(configCtx, axisSvc);
            }
        } catch (AxisFault e) {
            throw ExceptionFactory.makeWebServiceException(
                    Messages.getMessage("serviceClientCreateError"), e);
        }
        return serviceClient;
    }

    //This should eventually be deprecated in favor 'createAxisServiceNameFromDBL
    private String createAxisServiceName() {
        String portName = null;
        if (portQName != null) {
            portName = portQName.getLocalPart();
        } else {
            portName = "NoPortNameSpecified";

        }
        return getServiceDescription().getServiceQName().getLocalPart() + "." + portName;
    }

    public boolean isWSDLFullySpecified() {
        return isAxisServiceBuiltFromWSDL;
    }

    public boolean isProviderBased() {
        return webServiceProviderAnnotation != null;
    }

    public boolean isEndpointBased() {
        return webServiceAnnotation != null;
    }

    // ===========================================
    // ANNOTATION: WebService and WebServiceProvider
    // ===========================================

    public String getAnnoWebServiceWSDLLocation() {
        if (annotation_WsdlLocation == null) {

            if (getAnnoWebService() != null) {
                annotation_WsdlLocation = getAnnoWebService().wsdlLocation();

                //If this is not an implicit SEI, then make sure that its not on the SEI
                if (composite.isServiceProvider()) {
                    if (!DescriptionUtils.isEmpty(getAnnoWebServiceEndpointInterface())) {

                        DescriptionBuilderComposite seic =
                                getServiceDescriptionImpl().getDBCMap()
                                        .get(composite.getWebServiceAnnot().endpointInterface());
                        if (!DescriptionUtils.isEmpty(seic.getWebServiceAnnot().wsdlLocation())) {
                            annotation_WsdlLocation = seic.getWebServiceAnnot().wsdlLocation();
                        }
                    }
                }
            } else if (getAnnoWebServiceProvider() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebServiceProvider().wsdlLocation())) {
                annotation_WsdlLocation = getAnnoWebServiceProvider().wsdlLocation();
            } else {
                // There is no default value per JSR-181 MR Sec 4.1 pg 16
                annotation_WsdlLocation = "";
            }
        }
        return annotation_WsdlLocation;
    }

    public String getAnnoWebServiceServiceName() {
        if (annotation_ServiceName == null) {
            if (getAnnoWebService() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebService().serviceName())) {
                annotation_ServiceName = getAnnoWebService().serviceName();
            } else if (getAnnoWebServiceProvider() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebServiceProvider().serviceName())) {
                annotation_ServiceName = getAnnoWebServiceProvider().serviceName();
            } else {
                // Default value is the "simple name" of the class or interface + "Service"
                // Per JSR-181 MR Sec 4.1, pg 15
                annotation_ServiceName = DescriptionUtils.getSimpleJavaClassName(composite.getClassName()) + "Service";
            }
        }
        return annotation_ServiceName;
    }

    public String getAnnoWebServicePortName() {
        if (annotation_PortName == null) {
            if (getAnnoWebService() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebService().portName())) {
                annotation_PortName = getAnnoWebService().portName();
            } else if (getAnnoWebServiceProvider() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebServiceProvider().portName())) {
                annotation_PortName = getAnnoWebServiceProvider().portName();
            } else {
                // Default the value
                if (isProviderBased()) {
                    // This is the @WebServiceProvider annotation path
                    // Default value is not specified in JSR-224, but we can assume it is 
                    // similar to the default in the WebService case, however there is no
                    // name attribute for a WebServiceProvider.  So in this case we use 
                    // the default value for WebService.name per JSR-181 MR sec 4.1 pg 15.
                    // Note that this is really the same thing as the call to getWebServiceName() 
                    // in the WebService case; it is done sepertely just to be clear there is no 
                    // name element on the WebServiceProvider annotation
                    annotation_PortName = DescriptionUtils.getSimpleJavaClassName(composite.getClassName())
                        + "Port";
                } else {
                    // This is the @WebService annotation path
                    // Default value is the @WebService.name of the class or interface + "Port"
                    // Per JSR-181 MR Sec 4.1, pg 15
                    annotation_PortName = getAnnoWebServiceName() + "Port";
                }
            }
        }
        return annotation_PortName;
    }

    public String getAnnoWebServiceTargetNamespace() {
        if (annotation_TargetNamespace == null) {
            if (getAnnoWebService() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebService().targetNamespace())) {
                annotation_TargetNamespace = getAnnoWebService().targetNamespace();
            } else if (getAnnoWebServiceProvider() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebServiceProvider().targetNamespace())) {
                annotation_TargetNamespace = getAnnoWebServiceProvider().targetNamespace();
            } else {
                // Default value per JSR-181 MR Sec 4.1 pg 15 defers to "Implementation defined, 
                // as described in JAX-WS 2.0, section 3.2" which is JAX-WS 2.0 Sec 3.2, pg 29.
                annotation_TargetNamespace =
                    DescriptionUtils.makeNamespaceFromPackageName(
                            DescriptionUtils.getJavaPackageName(composite.getClassName()),
                            "http");
            }
        }
        return annotation_TargetNamespace;
    }

    // ===========================================
    // ANNOTATION: WebServiceProvider
    // ===========================================

    public WebServiceProvider getAnnoWebServiceProvider() {
        return webServiceProviderAnnotation;
    }

    // ===========================================
    // ANNOTATION: WebService
    // ===========================================

    public WebService getAnnoWebService() {
        return webServiceAnnotation;
    }

    public String getAnnoWebServiceEndpointInterface() {
        // TODO: Validation: Not allowed on WebServiceProvider
        if (webService_EndpointInterface == null) {
            if (!isProviderBased() && getAnnoWebService() != null
                    && !DescriptionUtils.isEmpty(getAnnoWebService().endpointInterface())) {
                webService_EndpointInterface = getAnnoWebService().endpointInterface();
            } else {
                // This element is not valid on a WebServiceProvider annotation
                webService_EndpointInterface = "";
            }
        }
        return webService_EndpointInterface;
    }

    public String getAnnoWebServiceName() {
        // TODO: Validation: Not allowed on WebServiceProvider

        //TODO: Per JSR109 v1.2 Sec. 5.3.2.1
        //      If not specified then we can use the default value as specified in JSR 181
        //		(but only if it is unique within the module)...or If the name is
        //		not specified in the Service Implementation Bean then fully
        //		qualified name of the Bean class is used to guarantee uniqueness
        //		If the above is not unique then fully qualified name of the
        //		Bean class is used to guarantee uniqueness

        if (webService_Name == null) {
            if (!isProviderBased()) {
                if (getAnnoWebService() != null
                        && !DescriptionUtils.isEmpty(getAnnoWebService().name())) {
                    webService_Name = getAnnoWebService().name();
                } else {
                    webService_Name =
                        DescriptionUtils.getSimpleJavaClassName(composite.getClassName());
                }
            } else {
                // This element is not valid on a WebServiceProvider annotation
                webService_Name = "";
            }
        }
        return webService_Name;
    }

    // ===========================================
    // ANNOTATION: ServiceMode
    // ===========================================
    public ServiceMode getAnnoServiceMode() {

        if (serviceModeAnnotation == null) {
            serviceModeAnnotation = composite.getServiceModeAnnot();
        }
        return serviceModeAnnotation;
    }

    public Service.Mode getServiceMode() {
        return getAnnoServiceModeValue();
    }

    public Service.Mode getAnnoServiceModeValue() {
        // This annotation is only valid on Provider-based endpoints. 
        if (isProviderBased() && serviceModeValue == null) {
            if (getAnnoServiceMode() != null) {
                serviceModeValue = getAnnoServiceMode().value();
            } else {
                serviceModeValue = ServiceMode_DEFAULT;
            }
        }
        return serviceModeValue;
    }

    // ===========================================
    // ANNOTATION: BindingType
    // ===========================================

    public BindingType getAnnoBindingType() {
        if (bindingTypeAnnotation == null) {
            bindingTypeAnnotation = composite.getBindingTypeAnnot();
        }
        return bindingTypeAnnotation;
    }

    public String getBindingType() {
        return getAnnoBindingTypeValue();
    }

    public String getAnnoBindingTypeValue() {
        if (bindingTypeValue == null) {
            if (getAnnoBindingType() != null &&
                    !DescriptionUtils.isEmpty(getAnnoBindingType().value())) {
                bindingTypeValue = getAnnoBindingType().value();
            } else {
                // No BindingType annotation present or value was empty; use default value
                bindingTypeValue = BindingType_DEFAULT;
            }
        }
        return bindingTypeValue;
    }

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

    public void setHandlerChain(HandlerChainsType handlerChain) {
        handlerChainsType = handlerChain;
    }

    public HandlerChainsType getHandlerChain() {
        return getHandlerChain(null);
    }

    /**
     * Returns a schema derived java class containing the the handler configuration information.  
     * That information, returned in the HandlerChainsType object, is looked for in the following 
     * places in this order:
     * - Set on the sparseComposite for the given key
     * - Set on the composite
     * - Read in from the file specified on HandlerChain annotation
     * 
     * @return HandlerChainsType This is the top-level element for the Handler configuration file
     * 
     */
    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) {
                HandlerChainsType hct = sparseComposite.getHandlerChainsType();
                return hct;
            }
        }
        
        // 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(Messages.getMessage("handlerChainsTypeErr",handlerFileName,composite.getClassName()));
                }

                String className = composite.getClassName();

                // REVIEW: This is using the classloader for EndpointDescriptionImpl; is that OK?
                ClassLoader classLoader = (composite.isServiceProvider()) ?
                        composite.getClassLoader() :
                        (ClassLoader) AccessController.doPrivileged(
                                new PrivilegedAction() {
                                    public Object run() {
                                        return this.getClass().getClassLoader();
                                    }
                                }
                        );

                if(log.isDebugEnabled()){
                    log.debug("Trying to load file " + handlerFileName + " relative to " + className);
                }
                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 {
                    ClassLoader classLoader1 = (ClassLoader) AccessController.doPrivileged(
                            new PrivilegedAction() {
                                public Object run() {
                                    return this.getClass().getClassLoader();
                                }
                            }
                    );
                    handlerChainsType =
                        DescriptionUtils.loadHandlerChains(is, classLoader1);
                }
            }
        }
        return handlerChainsType;
    }
  
    public HandlerChain getAnnoHandlerChainAnnotation(Object sparseCompositeKey) {
        if (this.handlerChainAnnotation == null) {
            if (composite.isServiceProvider()) {
                /*
                 * Per JSR-181 The @HandlerChain annotation MAY be present on
                 * the endpoint interface and service implementation bean. The
                 * service implementations bean's @HandlerChain is used if
                 * @HandlerChain is present on both. So, if we do find the
                 * annotation on this impl, then don't worry about else
                 * Otherwise, check to see if the SEI might be annotated with
                 * @HandlerChain
                 */

                handlerChainAnnotation = composite.getHandlerChainAnnot();
                if (handlerChainAnnotation == null) {

                    // If this is NOT an implicit SEI, then check for the
                    // annotation on the SEI
                    if (!DescriptionUtils.isEmpty(getAnnoWebServiceEndpointInterface())) {

                        DescriptionBuilderComposite seic = getServiceDescriptionImpl().getDBCMap()
                                        .get(composite.getWebServiceAnnot().endpointInterface());
                        if (seic != null) {
                            handlerChainAnnotation = seic.getHandlerChainAnnot();
                        }
                    }
                }
            } else {
                handlerChainAnnotation = composite.getHandlerChainAnnot();
            }
        }
        if (handlerChainAnnotation == null) {
            if (sparseCompositeKey != null) {
                DescriptionBuilderComposite sparseComposite = composite.getSparseComposite(sparseCompositeKey);
                if (sparseComposite != null && sparseComposite.getHandlerChainAnnot() != null) {
                    handlerChainAnnotation = sparseComposite.getHandlerChainAnnot();
                }
            }
        }

        return handlerChainAnnotation;
    }

    // ===========================================
    // ANNOTATION: MTOM
    // ===========================================
    
    /*
     * (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.EndpointDescription#isMTOMEnabled()
     */
    public boolean isMTOMEnabled() {
        if (isMTOMEnabledCache != null) {
            return isMTOMEnabledCache.booleanValue();
        }
        
        // isMTOMEnabled is a combination of the @BindingType and the @MTOM setting.
        MTOM mtomAnnotation =
                (MTOM) getAnnoFeature(MTOMFeature.ID);
        
        // If the @MTOM annotation is set, it wins
        if (mtomAnnotation != null) {
            isMTOMEnabledCache = Boolean.valueOf(mtomAnnotation.enabled());
            return isMTOMEnabledCache.booleanValue();
        }
        
        // Else look at the bindingType
        String bindingType = getBindingType();
        isMTOMEnabledCache = Boolean.valueOf(isMTOMBinding(bindingType));
        return isMTOMEnabledCache.booleanValue();
    }
    
    private static boolean isMTOMBinding(String url) {
        if (url != null && 
               (url.equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING) ||
                       url.equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING) ||
                       url.equals(MDQConstants.SOAP11JMS_MTOM_BINDING) ||
                       url.equals(MDQConstants.SOAP12JMS_MTOM_BINDING))) {
            return true;
        }
        return false;
    }
    
    // ===========================================
    // ANNOTATION: RespectBinding
    // ===========================================
    
    public boolean respectBinding() {
        return respectBinding;
    }
    
    public void setRespectBinding(boolean r) {
        respectBinding = r;
    }


    public boolean addRequiredBinding(QName name) {
        if (requiredBindings == null)
            requiredBindings = new ArrayList();

        return requiredBindings.add(name);
    }

    public List getRequiredBindings() {
        if (requiredBindings == null)
            requiredBindings = new ArrayList();
        return requiredBindings;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.axis2.jaxws.description.EndpointDescription#getMTOMThreshold()
     */
    public int getMTOMThreshold() {
        if (axisService != null) {
            // We should cache this call here so we don't have to make
            // it on every pass through.
            Parameter mtomThreshold = axisService.getParameter(Configuration.MTOM_THRESHOLD);
            if (mtomThreshold != null) {
                return (Integer) mtomThreshold.getValue();
            }
        }
        
        return -1;
    }
    
    // Get the specified WebServiceFeatureAnnotation
    public Annotation getAnnoFeature(String id) {
        return framework.getAnnotation(id);
    }
    
    //The WebServiceFeatures should be configued last so that any other
    //configuration can be overridden. Should only be called on the
    //server side.
    private void configureWebServiceFeatures() {
        String bindingType = getBindingType();
        Set<String> ids = ServerConfiguratorRegistry.getIds();
        
        for (String id : ids) {
            ServerConfigurator configurator = ServerConfiguratorRegistry.getConfigurator(id);
            
            if (configurator.supports(bindingType))
                framework.addConfigurator(id, configurator);
        }
    	
        // The feature instances are stored on the composite from either the 
        // Java class or from something else building the list and setting it there.
        List<Annotation> features = composite.getWebServiceFeatures();
        
        if (features != null && features.size() > 0) {
            // Add each of the annotation instances to the WebServiceFeature framework
            Iterator<Annotation> itr = features.iterator();
            while (itr.hasNext()) {
                Annotation feature = (Annotation) itr.next();
                framework.addAnnotation(feature);
            }
            
            // Kick off the configuration of the WebServiceFeature instances.
            framework.configure(this);
        }
        else {
            if (log.isDebugEnabled()) {
                log.debug("No WebServiceFeatureAnnotation instances were found on the composite.");
            }
        }   
    }
    
    private Definition getWSDLDefinition() {
        return ((ServiceDescriptionWSDL)getServiceDescription()).getWSDLDefinition();
    }

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

    public Port getWSDLPort() {
        javax.wsdl.Service service = getWSDLService();
        if (service != null) {
            return service.getPort(getPortQName().getLocalPart());
        } else {
            return null;
        }
    }

    public Binding getWSDLBinding() {
        Binding wsdlBinding = null;
        Port wsdlPort = getWSDLPort();
        Definition wsdlDef = getWSDLDefinition();
        if (wsdlPort != null && wsdlDef != null) {
            wsdlBinding = wsdlPort.getBinding();
        }
        return wsdlBinding;
    }

    public String getWSDLBindingType() {
        String wsdlBindingType = null;
        String soapTransport = null;
        Binding wsdlBinding = getWSDLBinding();
        if (wsdlBinding != null) {
        	
            // If a WSDL binding was found, we need to find the proper extensibility
            // element and return the namespace.  The namespace for the binding element will 
            // determine whether it is SOAP 1.1 vs. SOAP 1.2 vs. HTTP (or other). If the namespace 
            // indicates SOAP we then need to determine what the transport is (HTTP vs. JMS)
            // TODO: What do we do if no extensibility element exists?
            List<ExtensibilityElement> elements = wsdlBinding.getExtensibilityElements();
            Iterator<ExtensibilityElement> itr = elements.iterator();
            while (itr.hasNext()) {
                ExtensibilityElement e = itr.next();
                if (javax.wsdl.extensions.soap.SOAPBinding.class.isAssignableFrom(e.getClass())) {
                    javax.wsdl.extensions.soap.SOAPBinding soapBnd =
                            (javax.wsdl.extensions.soap.SOAPBinding)e;
                    
                    //representation: this is soap:binding = elementType where NamespaceURI is "soap"
                    // The transport is represented by the 'transport' attribute within this binding element
                    wsdlBindingType = soapBnd.getElementType().getNamespaceURI();

                    soapTransport = soapBnd.getTransportURI();


                    break;
                
                } else if (SOAP12Binding.class.isAssignableFrom(e.getClass())) {
                    SOAP12Binding soapBnd = (SOAP12Binding)e;
                    wsdlBindingType = soapBnd.getElementType().getNamespaceURI();
                    soapTransport = soapBnd.getTransportURI();
                    break;
                
                } else if (HTTPBinding.class.isAssignableFrom(e.getClass())) {
                    HTTPBinding httpBnd = (HTTPBinding)e;
                    wsdlBindingType = httpBnd.getElementType().getNamespaceURI();
                    break;
                }
            }

            // We need to convert the wsdl-based SOAP and HTTP namespace into the expected Binding Type for 
            // HTTP or SOAPBindings with the appropriate transport (HTTP, JMS, etc.)
            //
            // Note that what we're actually returning is the WSDL binding type value conveted
            // to the corresponding SOAPBinding or HTTPBinding value.  We are overwite the 
            // wsdlBindingType with that converted JAXWS annotation binding type value and
            // return it.
            wsdlBindingType = DescriptionUtils.mapBindingTypeWsdlToAnnotation(wsdlBindingType, soapTransport);
        }
        return wsdlBindingType;
    }

    public String getName() {
        return getAnnoWebServiceName();
    }

    public String getTargetNamespace() {
        return getAnnoWebServiceTargetNamespace();
    }

    public PortInfo getPortInfo() {
        if (portInfo == null) {
            portInfo = new PortInfoImpl(getServiceQName(), getPortQName(), getBindingType());
        }
        return portInfo;
    }

    public void setClientBindingID(String clientBindingID) {

        if (clientBindingID == null) {
            this.clientBindingID = DEFAULT_CLIENT_BINDING_ID;
        } else if (validateClientBindingID(clientBindingID)) {
            this.clientBindingID = clientBindingID;
        } else {
            throw ExceptionFactory.makeWebServiceException(
                    Messages.getMessage("addPortErr0", getPortQName().toString()));
        }
    }

    private boolean validateClientBindingID(String bindingId) {
        boolean isValid = true;
        if (bindingId != null && !(bindingId.equals(SOAPBinding.SOAP11HTTP_BINDING) ||
                bindingId.equals(javax.xml.ws.http.HTTPBinding.HTTP_BINDING) ||
                bindingId.equals(SOAPBinding.SOAP12HTTP_BINDING) ||
                bindingId.equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING) ||
                bindingId.equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING) ||
                bindingId.equals(MDQConstants.SOAP11JMS_BINDING) ||
                bindingId.equals(MDQConstants.SOAP12JMS_BINDING) ||
                bindingId.equals(MDQConstants.SOAP11JMS_MTOM_BINDING) ||
                bindingId.equals(MDQConstants.SOAP12JMS_MTOM_BINDING))) {
            throw ExceptionFactory.makeWebServiceException(
                    Messages.getMessage("addPortErr0", getPortQName().toString()));
        }
        return isValid;
    }

    public String getClientBindingID() {
        if (clientBindingID == null) {
            if (getWSDLDefinition() != null) {
                clientBindingID = getWSDLBindingType();
                if (clientBindingID == null) {
                    clientBindingID = DEFAULT_CLIENT_BINDING_ID;
                }
            } else {
                clientBindingID = DEFAULT_CLIENT_BINDING_ID;
            }
        }
        return clientBindingID;
    }

    public void setEndpointAddress(String endpointAddress) {
        // REVIEW: Should this be called whenever BindingProvider.ENDPOINT_ADDRESS_PROPERTY is set by the client?
        if (!DescriptionUtils.isEmpty(endpointAddress)) {
            this.endpointAddress = endpointAddress;
        } else {
            // Since a port can be added without setting an endpoint address, this is not an error.
            if (log.isDebugEnabled())
                log.debug("A null or empty endpoint address was attempted to be set",
                          new Throwable("Stack Traceback"));
        }
    }

    public String getEndpointAddress() {
        if (endpointAddress == null) {
            // If the endpointAddress has not been set explicitly by a call to setEndpointAddress()
            // then try to get it from the WSDL
            endpointAddress = getWSDLSOAPAddress();
        }
        return endpointAddress;
    }
    
    public void setProperty(String key, Object value) {
        if(properties == null) {
            properties = new HashMap<String, Object>();
        }
        properties.put(key, value);
    }
    
    public Object getProperty(String key) {
        if(properties != null) {
           return properties.get(key);
        }
        return null;
    }

    /**
     * Return the SOAP Address from the WSDL for this port.
     *
     * @return The SOAP Address from the WSDL for this port or null.
     */
    public String getWSDLSOAPAddress() {
        if (wsdlSOAPAddress == null) {
            Port wsdlPort = getWSDLPort();
            if (wsdlPort != null) {
                // The port is in the WSDL, so see if it has a SOAP address extensibility element specified.
                List extElementList = wsdlPort.getExtensibilityElements();
                for (Object listElement : extElementList) {
                    ExtensibilityElement extElement = (ExtensibilityElement)listElement;
                    if (isSOAPAddressElement(extElement)) {
                        String soapAddress = getSOAPAddressFromElement(extElement);
                        if (!DescriptionUtils.isEmpty(soapAddress)) {
                            wsdlSOAPAddress = soapAddress;
                        }
                    }
                }
            }
        }
        return wsdlSOAPAddress;
    }

    /**
     * Determine if the WSDL Extensibility element corresponds to the SOAP Address element.
     *
     * @param exElement
     * @return
     */
    static boolean isSOAPAddressElement(ExtensibilityElement exElement) {
        boolean isAddress = false;
        if (exElement != null) {
            isAddress = (SOAP_11_ADDRESS_ELEMENT.equals(exElement.getElementType())
                    ||
                    (SOAP_12_ADDRESS_ELEMENT.equals(exElement.getElementType())));
        }
        return isAddress;
    }

    static String getSOAPAddressFromElement(ExtensibilityElement extElement) {
        String returnAddress = null;

        if (extElement != null) {
            if (SOAP_11_ADDRESS_ELEMENT.equals(extElement.getElementType())) {
                returnAddress = ((SOAPAddress)extElement).getLocationURI();
            } else if (SOAP_12_ADDRESS_ELEMENT.equals(extElement.getElementType())) {
                returnAddress = ((SOAP12Address)extElement).getLocationURI();
            }
        }

        return returnAddress;
    }

    /**
     * Selects a port to use in the case where a portQName was not specified by the client on the
     * Service.getPort(Class) call.  If WSDL is present, then an appropriate port is looked for
     * under the service element, and an exception is thrown if none can be found.  If WSDL is not
     * present, then the selected port is simply the one determined by annotations.
     *
     * @return A QName representing the port that is to be used.
     */
    private QName selectPortToUse() {
        QName portToUse = null;
        // If WSDL Service for this port is present, then we'll find an appropriate port defined in there and set 
        // the name accordingly.  If no WSDL is present, the the PortQName getter will use annotations to set the value.
        if (getWSDLService() != null) {
            portToUse = selectWSDLPortToUse();
        } else {
            // No WSDL, so the port to use is the one defined by the annotations.
            portToUse = getPortQName();
        }
        return portToUse;
    }

    /**
     * Look through the WSDL Service for a port that should be used.  If none can be found, then
     * throw an exception.
     *
     * @param wsdlService
     * @return A QName representing the port from the WSDL that should be used.
     */
    private QName selectWSDLPortToUse() {
        QName wsdlPortToUse = null;

        // To select which WSDL Port to use, we do the following
        // 1) Find the subset of all ports under the service that use the PortType represented by the SEI
        // 2) From the subset in (1) find all those ports that specify a SOAP Address
        // 3) Use the first port from (2)

        // Per JSR-181, 
        // - The portType name corresponds to the WebService.name annotation value, which is
        //   returned by getName()
        // - The portType namespace corresponds to the WebService.targetNamespace annotation, which
        //   is returned by getTargetNamespace()
        String portTypeLP = getName();
        String portTypeTNS = getTargetNamespace();
        QName portTypeQN = new QName(portTypeTNS, portTypeLP);

        ServiceDescriptionWSDL serviceDescWSDL = (ServiceDescriptionWSDL)getServiceDescription();

        List<Port> wsdlPortsUsingPortType = serviceDescWSDL.getWSDLPortsUsingPortType(portTypeQN);
        List<Port> wsdlPortsUsingSOAPAddresses =
                serviceDescWSDL.getWSDLPortsUsingSOAPAddress(wsdlPortsUsingPortType);
        if (wsdlPortsUsingSOAPAddresses != null && !wsdlPortsUsingSOAPAddresses.isEmpty()) {
            // We return the first port that uses the particluar PortType and has a SOAP address.
            // HOWEVER, that is not necessarily the first one in the WSDL that meets that criteria!  
            // The problem is that WSDL4J Service.getPorts(), which is used to get a Map of ports under the service 
            // DOES NOT return the ports in the order they are defined in the WSDL.  
            // Therefore, we can't necessarily predict which one we'll get back as the "first" one in the collection.
            // REVIEW: Note the above comment; is there anything more predictible and determinstic we can do?
            Port portToUse = (Port)wsdlPortsUsingSOAPAddresses.toArray()[0];
            String portLocalPart = portToUse.getName();
            String portNamespace = serviceDescWSDL.getWSDLService().getQName().getNamespaceURI();
            wsdlPortToUse = new QName(portNamespace, portLocalPart);
        }

        return wsdlPortToUse;
    }

    private WsdlComposite generateWSDL(DescriptionBuilderComposite dbc) {

        WsdlComposite wsdlComposite = null;
        Definition defn = dbc.getWsdlDefinition(getServiceQName());
        if(defn == null) {
            defn = dbc.getWsdlDefinition();
        }
        if (defn == null || !isAxisServiceBuiltFromWSDL) {

            //Invoke the callback for generating the wsdl
            if (dbc.getCustomWsdlGenerator() != null) {
                String implName = null;
                if (axisService == null) {
                    implName = DescriptionUtils.javifyClassName(composite.getClassName());
                } else {
                    implName = (String)axisService.getParameterValue(MDQConstants.SERVICE_CLASS);
                }
                wsdlComposite =
                        dbc.getCustomWsdlGenerator().generateWsdl(implName, this);

                if (wsdlComposite != null) {
                    if(wsdlComposite.getWsdlFileName() == null
                            ||
                            "".equals(wsdlComposite.getWsdlFileName())) {
                        wsdlComposite.setWsdlFileName(
                                                      (this.getAnnoWebServiceServiceName() + ".wsdl").toLowerCase()); 
                    }
                    
                    Definition wsdlDef = wsdlComposite.getRootWsdlDefinition();

                    try {
                        ConfigurationContext cc = dbc.getConfigurationContext();
                        WSDL4JWrapper wsdl4jWrapper = null;
                        if (cc != null) {
                            wsdl4jWrapper = new WSDL4JWrapper(dbc.getWsdlURL(), wsdlDef, cc);
                        } else {
                            wsdl4jWrapper = new WSDL4JWrapper(dbc.getWsdlURL(), wsdlDef, true, 2);
                        }
                        getServiceDescriptionImpl().setGeneratedWsdlWrapper(wsdl4jWrapper);
                    } catch (Exception e) {
                        throw ExceptionFactory.makeWebServiceException(Messages.getMessage("generateWSDLErr"),e);
                    }
                } else {
                    // REVIEW:Determine if we should always throw an exception on this, or at this point
                    //throw ExceptionFactory.makeWebServiceException("EndpointDescriptionImpl: Unable to find custom WSDL generator");
                    if (log.isDebugEnabled()) {
                        log.debug(
                                "The custom WSDL generator returned null, so no generated WSDL is available");
                    }

                }
            } else {
                // REVIEW: This used to throw an exception, but it seems we shouldn't require
                // a wsdl generator be provided.
//                throw ExceptionFactory.makeWebServiceException("EndpointDescriptionImpl: Unable to find custom WSDL generator");
                if (log.isDebugEnabled()) {
                    log.debug(
                            "No custom WSDL generator was supplied, so WSDL can not be generated");
                }
            }
        }
        return wsdlComposite;
    }
    
    List<CustomAnnotationInstance> getCustomAnnotationInstances() {
        return customAnnotations;
    }
    
    CustomAnnotationProcessor getCustomAnnotationProcessor(String annotationInstanceClassName) {
        return customAnnotationProcessors != null ? 
                customAnnotationProcessors.get(annotationInstanceClassName) : null;
    }
    
    public DescriptionBuilderComposite getDescriptionBuilderComposite() {
        return composite;
    }

    public String toString() {
        final String newline = "\n";
        final String sameline = "; ";
        StringBuffer string = new StringBuffer();
        try {
            string.append(super.toString());
            string.append(newline);
            string.append("Name: " + getName());
            string.append(sameline);
            string.append("Endpoint Address: " + getEndpointAddress());
            //
            string.append(newline);
            string.append("ServiceQName: " + getServiceQName());
            string.append(sameline);
            string.append("PortQName: " + getPortQName());
            string.append(sameline);
            string.append("TargetNamespace: " + getTargetNamespace());
            //
            string.append(newline);
            string.append("Service Mode: " + getServiceMode());
            string.append(sameline);
            string.append("Binding Type: " + getBindingType());
            string.append(sameline);
            string.append("Client Binding Type: " + getClientBindingID());
            //
            string.append(newline);
            string.append("Is provider-based: " + (isProviderBased() == true));
            string.append(sameline);
            string.append("Is proxy-based: " + (isEndpointBased() == true));
            string.append(sameline);
            string.append("Is WSDL fully specified: " + (isWSDLFullySpecified() == true));
            //
            string.append(newline);
            string.append("AxisService: " + getAxisService());
            //
            string.append(newline);
            EndpointInterfaceDescription endpointInterfaceDesc = getEndpointInterfaceDescription();
            if (endpointInterfaceDesc != null) {
                string.append("EndpointInterfaceDescription: " + endpointInterfaceDesc.toString());
            } else {
                string.append("EndpointInterfaceDescription is null.");
            }
        }
        catch (Throwable t) {
            string.append(newline);
            string.append("Complete debug information not currently available for " +
                    "EndpointDescription");
            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);
            }
        });
    }

    /**
     * Return the class for this name
     *
     * @return Class
     */
    private static Class forName(final String className, final boolean initialize,
                                final ClassLoader classloader) throws ClassNotFoundException {
        Class cl = null;
        try {
            cl = (Class) AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws ClassNotFoundException {
                            return Class.forName(className, initialize, classloader);
                        }
                    }
            );
        } catch (PrivilegedActionException e) {
            if (log.isDebugEnabled()) {
                log.debug("Exception thrown from AccessController: " + e.getMessage(), e);
            }
            throw (ClassNotFoundException) e.getException();
        }

        return cl;
    }

    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;
    }
    
    /**
     * This will update or set the parameter on the AxisService that represents the
     * value of the @WebService.name attribute. This is needed since the @WebService.name
     * value may not be known until the EndpointInterfaceDescription is created for
     * the explicitly defined SEI.
     */
    void updateWebServiceNameParameter(String newName, AxisService service) {
        if(log.isDebugEnabled()) {
            log.debug("Setting @WebService.name value on the " + service.getName() + 
                      " AxisService to: " + newName);
        }
        Parameter param = service.getParameter(MDQConstants.WSDL_PORTTYPE_NAME);
        if(param != null) {
            param.setValue(newName);
        }
        else {
            param = new Parameter();
            param.setName(MDQConstants.WSDL_PORTTYPE_NAME);
            param.setValue(newName);
            try {
              service.addParameter(param);  
            }
            catch (AxisFault e) {
                throw ExceptionFactory.makeWebServiceException(Messages.getMessage("setupAxisServiceErr2"),e);
            }
        }
    }
    
    private static long currentUniqueID = 0;
    private long uniqueID() {
        if (currentUniqueID == 0) {
            currentUniqueID = System.currentTimeMillis();
        }
        return currentUniqueID++;
    }
        
    /**
     * Release the AxisService objects associated with this EndpointDescription.  Note that
     * this should only be called by the ServiceDescription that owns this EndpointDescrition.
     * 
     * @param configurationContext  The Axis2 ConfigurationContext holding the AxisConfiguration
     * from which the AxisServices should be removed.
     */
    void releaseResources(ConfigurationContext configurationContext) {
        if (configurationContext != null) {
            AxisConfiguration axisConfig = configurationContext.getAxisConfiguration();
            AxisService axisService = getAxisService();
            AxisServiceGroup axisServiceGroup = axisService.getAxisServiceGroup();
            try {
                axisConfig.removeServiceGroup(axisServiceGroup.getServiceGroupName());
            } catch (AxisFault e) {
                if (log.isDebugEnabled()) {
                    log.debug("EndpointDescriptionImpl release resources caught exception which it is ignoring", e);
                }
            }
        }
    }
}


