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

import org.apache.axiom.om.OMElement;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.addressing.AddressingConstants;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.dataretrieval.AxisDataLocator;
import org.apache.axis2.dataretrieval.AxisDataLocatorImpl;
import org.apache.axis2.dataretrieval.DRConstants;
import org.apache.axis2.dataretrieval.Data;
import org.apache.axis2.dataretrieval.DataRetrievalException;
import org.apache.axis2.dataretrieval.DataRetrievalRequest;
import org.apache.axis2.dataretrieval.LocatorType;
import org.apache.axis2.dataretrieval.OutputForm;
import org.apache.axis2.dataretrieval.SchemaSupplier;
import org.apache.axis2.dataretrieval.WSDL11SupplierTemplate;
import org.apache.axis2.dataretrieval.WSDL20SupplierTemplate;
import org.apache.axis2.dataretrieval.WSDLSupplier;
import org.apache.axis2.deployment.DeploymentConstants;
import org.apache.axis2.deployment.util.ExcludeInfo;
import org.apache.axis2.deployment.util.PhasesInfo;
import org.apache.axis2.deployment.util.Utils;
import org.apache.axis2.description.java2wsdl.DefaultSchemaGenerator;
import org.apache.axis2.description.java2wsdl.DocLitBareSchemaGenerator;
import org.apache.axis2.description.java2wsdl.Java2WSDLConstants;
import org.apache.axis2.description.java2wsdl.SchemaGenerator;
import org.apache.axis2.description.java2wsdl.TypeTable;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.engine.DefaultObjectSupplier;
import org.apache.axis2.engine.MessageReceiver;
import org.apache.axis2.engine.ObjectSupplier;
import org.apache.axis2.engine.ServiceLifeCycle;
import org.apache.axis2.i18n.Messages;
import org.apache.axis2.phaseresolver.PhaseResolver;
import org.apache.axis2.transport.TransportListener;
import org.apache.axis2.util.IOUtils;
import org.apache.axis2.util.JavaUtils;
import org.apache.axis2.util.Loader;
import org.apache.axis2.util.LoggingControl;
import org.apache.axis2.util.XMLPrettyPrinter;
import org.apache.axis2.util.XMLUtils;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.wsdl.WSDLUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.neethi.Policy;
import org.apache.woden.WSDLSource;
import org.apache.woden.wsdl20.Description;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaExternal;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.apache.ws.commons.schema.utils.NamespacePrefixList;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.http.HTTPAddress;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Class AxisService
 */
public class AxisService extends AxisDescription {

	// ////////////////////////////////////////////////////////////////
	// Standard Parameter names

	/**
	 * If this param is true, and the service has exactly one AxisOperation,
	 * normal operation dispatch (via URI/soapAction/etc) will not be necessary,
	 * and we'll just default to funneling all messages to that op. This is
	 * useful for passthrough/ESB/embedded applications.
	 */
	public static final String SUPPORT_SINGLE_OP = "supportSingleOperation";
	// ////////////////////////////////////////////////////////////////

	public static final String IMPORT_TAG = "import";
	public static final String INCLUDE_TAG = "include";
	public static final String SCHEMA_LOCATION = "schemaLocation";

	private Map<String, AxisEndpoint> endpointMap = new HashMap<String, AxisEndpoint>();

	/*
	 * This is a map between the QName of the element of a message specified in
	 * the WSDL and an Operation. It enables SOAP Body-based dispatching for
	 * doc-literal bindings.
	 */
	private Map<QName, AxisOperation> messageElementQNameToOperationMap = new HashMap<QName, AxisOperation>();

	private int nsCount = 0;
	private static final Log log = LogFactory.getLog(AxisService.class);
	private URL fileName;

	// Maps httpLocations to corresponding operations. Used to dispatch rest
	// messages.
	private HashMap<String, AxisOperation> httpLocationDispatcherMap = null;

	// A map of (String alias, AxisOperation operation). The aliases might
	// include: SOAPAction,
	// WS-Addressing action, the operation name, the AxisInputMessage name. See:
	// - invalidOperationsAliases
	// - mapActionToOperatoin()
	// - getOperationByAction()
	// REVIEW: This really should be seperate maps for the different types of
	// aliases so they don't
	// conflict with each other. For example, so that an identical operation
	// name and soap action
	// on different operatoins don't cause a collision; the following can't be
	// routed because
	// "foo" is not unique across different operations:
	// operation 1: action = foo, name = bar
	// operation 2: action = bar, name = foo
	private HashMap<String, AxisOperation> operationsAliasesMap = null;

	// Collection of aliases that are invalid for this service because they are
	// duplicated across
	// multiple operations under this service.
	private List<String> invalidOperationsAliases = null;
	// private HashMap operations = new HashMap();

	// to store module ref at deploy time parsing
	private ArrayList<String> moduleRefs = null;

	// to keep the time that last update time of the service
	private long lastupdate;
	private HashMap<String, ModuleConfiguration> moduleConfigmap;
	private String name;
	private ClassLoader serviceClassLoader;

	// to keep the XMLScheam getting either from WSDL or java2wsdl
	private ArrayList<XmlSchema> schemaList;
	// private XmlSchema schema;

	// wsdl is there for this service or not (in side META-INF)
	private boolean wsdlFound = false;

	// to store the scope of the service
	private String scope;

	// to store default message receivers
	private HashMap<String, MessageReceiver> messageReceivers;

	// to set the handler chain available in phase info
	private boolean useDefaultChains = true;

	// to keep the status of the service , since service can stop at the run
	// time
	private boolean active = true;

	private boolean elementFormDefault = true;

	// to keep the service target name space
	private String targetNamespace = Java2WSDLConstants.DEFAULT_TARGET_NAMESPACE;
	private String targetNamespacePrefix = Java2WSDLConstants.TARGETNAMESPACE_PREFIX;

	// to store the target namespace for the schema
	private String schematargetNamespace;// = Java2WSDLConstants.AXIS2_XSD;
	private String schematargetNamespacePrefix = Java2WSDLConstants.SCHEMA_NAMESPACE_PRFIX;

	private boolean enableAllTransports = true;
	private List<String> exposedTransports = new ArrayList<String>();

	// To keep reference to ServiceLifeCycle instance , if the user has
	// specified in services.xml
	private ServiceLifeCycle serviceLifeCycle;

	/**
	 * Keeps track whether the schema locations are adjusted
	 */
	private boolean schemaLocationsAdjusted = false;

	private boolean wsdlImportLocationAdjusted = false;

	/**
	 * A table that keeps a mapping of unique xsd names (Strings) against the
	 * schema objects. This is populated in the first instance the schemas are
	 * asked for and then used to serve the subsequent requests
	 */
	private Map schemaMappingTable = null;

	/**
	 * counter variable for naming the schemas
	 */
	private int count = 0;
	/**
	 * A custom schema Name prefix. if set this will be used to modify the
	 * schema names
	 */
	private String customSchemaNamePrefix = null;

	/**
	 * A custom schema name suffix. will be attached to the schema file name
	 * when the files are uniquely named. A good place to add a file extension
	 * if needed
	 */
	private String customSchemaNameSuffix = null;

	// ///////////////////////////////////////
	// WSDL related stuff ////////////////////
	// //////////////////////////////////////

	/** Map of prefix -> namespaceURI */
	private NamespaceMap namespaceMap;

	private String soapNsUri;
	private String endpointName;
	private String endpointURL;
    
    private List importedNamespaces;

	private boolean clientSide = false;

	// To keep a ref to ObjectSupplier instance
	private ObjectSupplier objectSupplier;

	// package to namespace mapping
	private Map p2nMap;

	// to keep the exclude property details
	private ExcludeInfo excludeInfo;

	private TypeTable typeTable;

	// Data Locators for WS-Mex Support
	private HashMap dataLocators;
	private HashMap<String, String> dataLocatorClassNames;
	private AxisDataLocatorImpl defaultDataLocator;
	// Define search sequence for datalocator based on Data Locator types.
	LocatorType[] availableDataLocatorTypes = new LocatorType[] {
			LocatorType.SERVICE_DIALECT, LocatorType.SERVICE_LEVEL,
			LocatorType.GLOBAL_DIALECT, LocatorType.GLOBAL_LEVEL,
			LocatorType.DEFAULT_AXIS };

	// name of the binding used : use in codegeneration
	private String bindingName;
        
	// List of MessageContextListeners that listen for events on the MessageContext
        private CopyOnWriteArrayList<MessageContextListener> messageContextListeners = 
            new CopyOnWriteArrayList<MessageContextListener>();

    // names list keep to preserve the parameter order
    private List operationsNameList;
    
    // Excluded operations name list to know which operations to exclude.
    private List excludeOperationsNameList;

	private String[] eprs;
	private boolean customWsdl = false;

	private HashMap policyMap = new HashMap();

	public AxisEndpoint getEndpoint(String key) {
		return (AxisEndpoint) endpointMap.get(key);
	}

	public void addEndpoint(String key, AxisEndpoint axisEndpoint) {
		this.endpointMap.put(key, axisEndpoint);
	}

	public boolean isSchemaLocationsAdjusted() {
		return schemaLocationsAdjusted;
	}

	public void setSchemaLocationsAdjusted(boolean schemaLocationsAdjusted) {
		this.schemaLocationsAdjusted = schemaLocationsAdjusted;
	}

	public Map getSchemaMappingTable() {
		return schemaMappingTable;
	}

	public void setSchemaMappingTable(Map schemaMappingTable) {
		this.schemaMappingTable = schemaMappingTable;
	}

	public String getCustomSchemaNamePrefix() {
		return customSchemaNamePrefix;
	}

	public void setCustomSchemaNamePrefix(String customSchemaNamePrefix) {
		this.customSchemaNamePrefix = customSchemaNamePrefix;
	}

	public String getCustomSchemaNameSuffix() {
		return customSchemaNameSuffix;
	}

	public void setCustomSchemaNameSuffix(String customSchemaNameSuffix) {
		this.customSchemaNameSuffix = customSchemaNameSuffix;
	}

	/**
	 * Constructor AxisService.
	 */
	public AxisService() {
		super();
		this.operationsAliasesMap = new HashMap();
		this.invalidOperationsAliases = new ArrayList();
		this.excludeOperationsNameList = new ArrayList();
		moduleConfigmap = new HashMap();
		// by default service scope is for the request
		scope = Constants.SCOPE_REQUEST;
		httpLocationDispatcherMap = new HashMap();
		messageReceivers = new HashMap();
		moduleRefs = new ArrayList();
		schemaList = new ArrayList();
		serviceClassLoader = (ClassLoader) org.apache.axis2.java.security.AccessController
				.doPrivileged(new PrivilegedAction<ClassLoader>() {
					public ClassLoader run() {
						return Thread.currentThread().getContextClassLoader();
					}
				});
		objectSupplier = new DefaultObjectSupplier();
		dataLocators = new HashMap();
		dataLocatorClassNames = new HashMap();
	}

	public String getBindingName() {
		return bindingName;
	}

	public void setBindingName(String bindingName) {
		this.bindingName = bindingName;
	}

	/**
	 * get the SOAPVersion
	 */
	public String getSoapNsUri() {
		return soapNsUri;
	}

	public void setSoapNsUri(String soapNsUri) {
		this.soapNsUri = soapNsUri;
	}

	/**
	 * get the endpointName
	 */
	public String getEndpointName() {
		return endpointName;
	}

	public void setEndpointName(String endpoint) {
		this.endpointName = endpoint;
	}

	/**
	 * Constructor AxisService.
	 */
	public AxisService(String name) {
		this();
		this.name = name;
	}

	@SuppressWarnings("deprecation")
    public void addMessageReceiver(String mepURI,
			MessageReceiver messageReceiver) {
		if (WSDL2Constants.MEP_URI_IN_ONLY.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_ONLY
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_ONLY
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_IN_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_IN_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_IN_ONLY,
					messageReceiver);
		} else if (WSDL2Constants.MEP_URI_OUT_ONLY.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_ONLY
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_ONLY
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_OUT_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_OUT_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_OUT_ONLY,
					messageReceiver);
		} else if (WSDL2Constants.MEP_URI_IN_OUT.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_OUT
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_OUT
						.equals(mepURI)) {
			messageReceivers
					.put(WSDL2Constants.MEP_URI_IN_OUT, messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_IN_OUT,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_IN_OUT,
					messageReceiver);
		} else if (WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT,
					messageReceiver);
			messageReceivers
					.put(
					        WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT,
							messageReceiver);
		} else if (WSDL2Constants.MEP_URI_OUT_IN.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_IN
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_IN
						.equals(mepURI)) {
			messageReceivers
					.put(WSDL2Constants.MEP_URI_OUT_IN, messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_OUT_IN,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_OUT_IN,
					messageReceiver);
		} else if (WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN,
					messageReceiver);
			messageReceivers
					.put(
					        WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN,
							messageReceiver);
		} else if (WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY.equals(mepURI)
				|| WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY,
					messageReceiver);
			messageReceivers
					.put(
					        WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY,
							messageReceiver);
		} else if (WSDL2Constants.MEP_URI_ROBUST_IN_ONLY.equals(mepURI)
				|| WSDL2Constants.MEP_URI_ROBUST_IN_ONLY
						.equals(mepURI)
				|| WSDL2Constants.MEP_URI_ROBUST_IN_ONLY
						.equals(mepURI)) {
			messageReceivers.put(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_ROBUST_IN_ONLY,
					messageReceiver);
			messageReceivers.put(
			        WSDL2Constants.MEP_URI_ROBUST_IN_ONLY,
					messageReceiver);
		} else {
			messageReceivers.put(mepURI, messageReceiver);
		}
	}

	public MessageReceiver getMessageReceiver(String mepURL) {
		return messageReceivers.get(mepURL);
	}

	/**
	 * Adds module configuration , if there is moduleConfig tag in service.
	 * 
	 * @param moduleConfiguration
	 */
	public void addModuleConfig(ModuleConfiguration moduleConfiguration) {
		moduleConfigmap.put(moduleConfiguration.getModuleName(),
				moduleConfiguration);
	}

	/**
	 * Add any control operations defined by a Module to this service.
	 * 
	 * @param module
	 *            the AxisModule which has just been engaged
	 * @throws AxisFault
	 *             if a problem occurs
	 */
	void addModuleOperations(AxisModule module) throws AxisFault {
		HashMap<QName, AxisOperation> map = module.getOperations();
		Collection<AxisOperation> col = map.values();
		PhaseResolver phaseResolver = new PhaseResolver(getAxisConfiguration());
		for (Iterator<AxisOperation> iterator = col.iterator(); iterator.hasNext();) {
			AxisOperation axisOperation = copyOperation((AxisOperation) iterator
					.next());
			if (this.getOperation(axisOperation.getName()) == null) {
				ArrayList<String> wsamappings = axisOperation.getWSAMappingList();
				if (wsamappings != null) {
					for (int j = 0, size = wsamappings.size(); j < size; j++) {
						String mapping = (String) wsamappings.get(j);
                        //If there is already an operation with this action
						//mapping (e.g. if the service has a matching operation)
						//then we're going to check to see if the module's
						//operation says that it's OK to be overridden and
						//if so, we'll simply ignore the mapping, otherwise
						//we continue as before
						AxisOperation mappedOperation = getOperationByAction(mapping);
						if ((mappedOperation != null)
						    && (axisOperation.isParameterTrue(DeploymentConstants.TAG_ALLOWOVERRIDE))) {
						  if (log.isDebugEnabled()) {
						    log
						    .debug("addModuleOperations: Mapping already exists for action: "
						           + mapping
						           + " to operation: "
						           + axisOperation
						           + " named: "
						           + axisOperation.getName()
						           + " and an override is allowed, so the module mapping for module: "
						           + module.getName()
						           + " is being ignored.");
						    log.debug(JavaUtils.callStackToString());
						  }
						} else {
						  mapActionToOperation(mapping, axisOperation);
						}
					}
				}
				// If we've set the "expose" parameter for this operation, it's
				// normal (non-
				// control) and therefore it will appear in generated WSDL. If
				// we haven't,
				// it's a control operation and will be ignored at WSDL-gen
				// time.
				if (axisOperation
						.isParameterTrue(DeploymentConstants.TAG_EXPOSE)) {
					axisOperation.setControlOperation(false);
				} else {
					axisOperation.setControlOperation(true);
				}

				phaseResolver.engageModuleToOperation(axisOperation, module);

				this.addOperation(axisOperation);
			}
		}
	}

	public void addModuleref(String moduleref) {
		moduleRefs.add(moduleref);
	}

    /**
     * Adds operation name to exclude list.
     * 
     * @param operation operation name to add to exclude list
     * 
     */
    public void addExcludeOperationName(String operation){        
        excludeOperationsNameList.add(operation);       
    }
    
    /**
     * Removes operation name from exclude list.
     * 
     * @param operation operation name to remove from exclude list
     * 
     */
    public void removeExcludeOperationName(String operation){        
        excludeOperationsNameList.remove(operation);       
    }
    
    /**
     * Returns whether operation name is in exclude list. 
     * 
     * @param operation name to check if operation is in the exlude list.
     * @return boolean indicating whether operation name is in exlude list. 
     */
    public boolean isExcludedOperation(String operation){        
        return excludeOperationsNameList.contains(operation);       
    }
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.axis2.description.AxisService#addOperation(org.apache.axis2.description.AxisOperation)
	 */

	/**
	 * Method addOperation.
	 * 
	 * @param axisOperation
	 */
	public void addOperation(AxisOperation axisOperation) {
		axisOperation.setParent(this);
        
        if (log.isDebugEnabled()) {
            if (axisOperation.getName().equals(ServiceClient.ANON_OUT_ONLY_OP)
                    || (axisOperation.getName().equals(ServiceClient.ANON_OUT_ONLY_OP))
                    || (axisOperation.getName().equals(ServiceClient.ANON_OUT_ONLY_OP))) {
                log.debug("Client-defined operation name matches default operation name. "
                        + "this may cause interoperability issues.  Name is: " + axisOperation.getName().toString());
            }
        }

		Iterator<AxisModule> modules = getEngagedModules().iterator();

		while (modules.hasNext()) {
			AxisModule module = (AxisModule) modules.next();
			try {
				axisOperation.engageModule(module);
			} catch (AxisFault axisFault) {
				log.info(Messages.getMessage("modulealredyengagetoservice",
						module.getName()));
			}
		}
		if (axisOperation.getMessageReceiver() == null) {
			axisOperation.setMessageReceiver(loadDefaultMessageReceiver(
					axisOperation.getMessageExchangePattern(), this));
		}
		if (axisOperation.getInputAction() == null) {
			axisOperation.setSoapAction("urn:"
					+ axisOperation.getName().getLocalPart());
		}

		if (axisOperation.getOutputAction() == null) {
			axisOperation.setOutputAction("urn:"
					+ axisOperation.getName().getLocalPart()
					+ Java2WSDLConstants.RESPONSE);
		}
		addChild(axisOperation);

		String operationName = axisOperation.getName().getLocalPart();

		/*
		 * Some times name of the operation can be different from the name of
		 * the first child of the SOAPBody. This will put the correct mapping
		 * associating that name with the operation. This will be useful
		 * especially for the SOAPBodyBasedDispatcher
		 */

		Iterator axisMessageIter = axisOperation.getChildren();

		while (axisMessageIter.hasNext()) {
			AxisMessage axisMessage = (AxisMessage) axisMessageIter.next();
			String messageName = axisMessage.getName();
			if (messageName != null && !messageName.equals(operationName)) {
				mapActionToOperation(messageName, axisOperation);
			}
		}

		mapActionToOperation(operationName, axisOperation);

		String action = axisOperation.getInputAction();
		if (action.length() > 0) {
			mapActionToOperation(action, axisOperation);
		}

		ArrayList<String> wsamappings = axisOperation.getWSAMappingList();
		if (wsamappings != null) {
			for (int j = 0, size = wsamappings.size(); j < size; j++) {
				String mapping = (String) wsamappings.get(j);
				mapActionToOperation(mapping, axisOperation);
			}
		}

		if (axisOperation.getMessageReceiver() == null) {
			axisOperation.setMessageReceiver(loadDefaultMessageReceiver(
					axisOperation.getMessageExchangePattern(), this));
		}
	}

	private MessageReceiver loadDefaultMessageReceiver(String mepURL,
			AxisService service) {
		MessageReceiver messageReceiver;
		if (mepURL == null) {
			mepURL = WSDL2Constants.MEP_URI_IN_OUT;
		}
		if (service != null) {
			messageReceiver = service.getMessageReceiver(mepURL);
			if (messageReceiver != null) {
				return messageReceiver;
			}
		}
		if (getAxisConfiguration() != null) {
			return getAxisConfiguration().getMessageReceiver(mepURL);
		}
		return null;
	}

	/**
	 * Gets a copy from module operation.
	 * 
	 * @param axisOperation
	 * @return Returns AxisOperation.
	 * @throws AxisFault
	 */
	private AxisOperation copyOperation(AxisOperation axisOperation)
			throws AxisFault {
		AxisOperation operation = AxisOperationFactory
				.getOperationDescription(axisOperation
						.getMessageExchangePattern());

		operation.setMessageReceiver(axisOperation.getMessageReceiver());
		operation.setName(axisOperation.getName());

		Iterator<Parameter> parameters = axisOperation.getParameters().iterator();

		while (parameters.hasNext()) {
			Parameter parameter = (Parameter) parameters.next();

			operation.addParameter(parameter);
		}

		PolicyInclude policyInclude = new PolicyInclude(operation);
		PolicyInclude axisOperationPolicyInclude = axisOperation
				.getPolicyInclude();

		if (axisOperationPolicyInclude != null) {
			Policy policy = axisOperationPolicyInclude.getPolicy();
			if (policy != null) {
				policyInclude.setPolicy(axisOperationPolicyInclude.getPolicy());
			}
		}
		operation.setPolicyInclude(policyInclude);

		operation.setWsamappingList(axisOperation.getWSAMappingList());
		operation.setRemainingPhasesInFlow(axisOperation
				.getRemainingPhasesInFlow());
		operation.setPhasesInFaultFlow(axisOperation.getPhasesInFaultFlow());
		operation.setPhasesOutFaultFlow(axisOperation.getPhasesOutFaultFlow());
		operation.setPhasesOutFlow(axisOperation.getPhasesOutFlow());

		operation.setOutputAction(axisOperation.getOutputAction());
		String[] faultActionNames = axisOperation.getFaultActionNames();
		for (int i = 0; i < faultActionNames.length; i++) {
			operation.addFaultAction(faultActionNames[i], axisOperation
					.getFaultAction(faultActionNames[i]));
		}

		return operation;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.axis2.description.AxisService#addToengagedModules(javax.xml.namespace.QName)
	 */

	/**
	 * Engages a module. It is required to use this method.
	 * 
	 * @param axisModule
	 * @param engager
	 */
	public void onEngage(AxisModule axisModule, AxisDescription engager)
			throws AxisFault {
		// adding module operations
		addModuleOperations(axisModule);

		Iterator<AxisOperation> operations = getOperations();
		while (operations.hasNext()) {
			AxisOperation axisOperation = (AxisOperation) operations.next();
			axisOperation.engageModule(axisModule, engager);
		}
	}

	/**
	 * Maps an alias (such as a SOAPAction, WSA action, or an operation name) to
	 * the given AxisOperation. This is used by dispatching (both SOAPAction-
	 * and WSAddressing- based dispatching) to figure out which operation a
	 * given message is for. Some notes on restrictions of "action" - A null or
	 * empty action will be ignored - An action that is a duplicate and
	 * references an idential operation is allowed - An acton that is a
	 * duplicate and references a different operation is NOT allowed. In this
	 * case, the action for the original operation is removed from the alias
	 * table, thus removing the ability to route based on this action. This is
	 * necessary to prevent mis-directing incoming message to the wrong
	 * operation based on SOAPAction.
	 * 
	 * Note that an alias could be a SOAPAction, WS-Addressing Action, the
	 * operation name, or some other alias.
	 * 
	 * @see #getOperationByAction(String)
	 * 
	 * @param action
	 *            the alias key
	 * @param axisOperation
	 *            the operation to map to
	 */
	public void mapActionToOperation(String action, AxisOperation axisOperation) {
		if (action == null || "".equals(action)) {
			if (log.isDebugEnabled()) {
				log
						.debug("mapActionToOperation: A null or empty action cannot be used to map to an operation.");
			}
			return;
		}
		if (log.isDebugEnabled()) {
			log
					.debug("mapActionToOperation: Mapping Action to Operation: action: "
							+ action
							+ "; operation: "
							+ axisOperation
							+ "named: " + axisOperation.getName());
              log.debug(JavaUtils.callStackToString());
		}
                      
		//If there is already an operation with this action
		//mapping then we're going to check to see if the
		//operation says that it's OK to be overridden and
		//if so, we'll simply ignore the mapping, otherwise
		//we continue as before
		AxisOperation mappedOperation = getOperationByAction(action);
		if ((mappedOperation != null)
		    && (axisOperation.isParameterTrue(DeploymentConstants.TAG_ALLOWOVERRIDE))) {
		  if (log.isDebugEnabled()) {
		    log
		    .debug("addModuleOperations: Mapping already exists for action: "
		           + action
		           + " to operation: "
		           + axisOperation
		           + " named: "
		           + axisOperation.getName()
		           + " and an override is allowed, so the mapping is being ignored.");
		    log.debug(JavaUtils.callStackToString());
		  }
		  return;		
		}

		// First check if this action has already been flagged as invalid
		// because it is a duplicate.
		if (invalidOperationsAliases.contains(action)) {
			// This SOAPAction has already been determined to be invalid; log a
			// message
			// and do not add it to the operation alias map.
			if (log.isDebugEnabled()) {
				log
						.debug("mapActionToOperation: The action: "
								+ action
								+ " can not be used for operation: "
								+ axisOperation
								+ " with operation name: "
								+ axisOperation.getName()
								+ " because that SOAPAction is not unique for this service.");
			}
			return;
		}

		// Check if the action is currently mapping to an operation.
		AxisOperation currentlyMappedOperation = getOperationByAction(action);
		if (currentlyMappedOperation != null) {
			if (currentlyMappedOperation == axisOperation) {
				// This maps to the same operation, then it is already in the
				// alias table, so
				// just silently ignore this mapping request.
				if (log.isDebugEnabled()) {
					log
							.debug("mapActionToOperation: This operation is already mapped to this action: "
									+ action
									+ "; AxisOperation: "
									+ currentlyMappedOperation
									+ " named: "
									+ currentlyMappedOperation.getName());
				}
			} else {
				// This action is already mapped, but it is to a different
				// operation. Remove
				// the action mapping from the alias table and add it to the
				// list of invalid mappings
				operationsAliasesMap.remove(action);
				invalidOperationsAliases.add(action);
				if (log.isDebugEnabled()) {
					log
							.debug("mapActionToOperation: The action is already mapped to a different "
									+ "operation.  The mapping of the action to any operations will be "
									+ "removed.  Action: "
									+ action
									+ "; original operation: "
									+ currentlyMappedOperation
									+ " named "
									+ currentlyMappedOperation.getName()
									+ "; new operation: "
									+ axisOperation
									+ " named " + axisOperation.getName());
				}
			}
		} else {
			operationsAliasesMap.put(action, axisOperation);
			// Adding operation name to the mapping table
			// operationsAliasesMap.put(axisOperation.getName().getLocalPart(),
			// axisOperation);
		}
	}

	/**
	 * Maps an constant string in the whttp:location to the given operation.
	 * This is used by RequestURIOperationDispatcher based dispatching to figure
	 * out which operation it is that a given message is for.
	 * 
	 * @param string
	 *            the constant drawn from whttp:location
	 * @param axisOperation
	 *            the operation to map to
	 */
	public void addHttpLocationDispatcherString(String string,
			AxisOperation axisOperation) {
		httpLocationDispatcherMap.put(string, axisOperation);
	}

	/**
	 * Prints the schema to the given output stream.
	 * @param out The output stream for the data to be written. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @throws AxisFault
	 */
	public void printSchema(OutputStream out) throws AxisFault {
		for (int i = 0; i < schemaList.size(); i++) {
			XmlSchema schema = addNameSpaces(i);
            try {
                schema.write(out);
            } catch (UnsupportedEncodingException e) {
                log.error("Error while printing schema ", e);
                throw new AxisFault(e.getMessage(), e);
            }
        }
	}

	public XmlSchema getSchema(int index) {
		return addNameSpaces(index);
	}

	/**
	 * Release the list of schema objects. <p/> In some environments, this can
	 * provide significant relief of memory consumption in the java heap, as
	 * long as the need for the schema list has completed.
	 */
	public void releaseSchemaList() {
		if (schemaList != null) {
			// release the schema list
			schemaList.clear();
		}

		if (log.isDebugEnabled()) {
			log.debug("releaseSchemaList: schema list has been released.");
		}
	}

	private XmlSchema addNameSpaces(int i) {
		XmlSchema schema = (XmlSchema) schemaList.get(i);
		NamespaceMap map = (NamespaceMap) namespaceMap.clone();
		NamespacePrefixList namespaceContext = schema.getNamespaceContext();
		String prefixes[] = namespaceContext.getDeclaredPrefixes();
		for (int j = 0; j < prefixes.length; j++) {
			String prefix = prefixes[j];
			map.add(prefix, namespaceContext.getNamespaceURI(prefix));
		}
		schema.setNamespaceContext(map);
		return schema;
	}

	public void setEPRs(String[] eprs) {
		this.eprs = eprs;
	}

	public String[] getEPRs() {
		if (eprs != null && eprs.length != 0) {
			return eprs;
		}
		eprs = calculateEPRs();
		return eprs;
	}

	private String[] calculateEPRs() {
		try {
			String requestIP = org.apache.axis2.util.Utils.getIpAddress(getAxisConfiguration());
			return calculateEPRs(requestIP);
		} catch (SocketException e) {
			log.error("Cannot get local IP address", e);
		}
		return new String[0];
	}

	private String[] calculateEPRs(String requestIP) {
		AxisConfiguration axisConfig = getAxisConfiguration();
		if (axisConfig == null) {
			return null;
		}
		ArrayList<String> eprList = new ArrayList<String>();
		if (enableAllTransports) {
			for (Iterator<TransportInDescription> transports = axisConfig.getTransportsIn().values()
					.iterator(); transports.hasNext();) {
				TransportInDescription transportIn = (TransportInDescription) transports
						.next();
				TransportListener listener = transportIn.getReceiver();
				if (listener != null) {
					try {
						EndpointReference[] eprsForService = listener
								.getEPRsForService(this.name, requestIP);
						if (eprsForService != null) {
							for (int i = 0; i < eprsForService.length; i++) {
								EndpointReference endpointReference = eprsForService[i];
								if (endpointReference != null) {
									String address = endpointReference
											.getAddress();
									if (address != null) {
										eprList.add(address);
									}
								}
							}
						}
					} catch (AxisFault axisFault) {
						log.warn(axisFault.getMessage());
					}
				}
			}
		} else {
			List<String> trs = this.exposedTransports;
			for (int i = 0; i < trs.size(); i++) {
				String trsName = (String) trs.get(i);
				TransportInDescription transportIn = axisConfig
						.getTransportIn(trsName);
				if (transportIn != null) {
					TransportListener listener = transportIn.getReceiver();
					if (listener != null) {
						try {
							EndpointReference[] eprsForService = listener
									.getEPRsForService(this.name, requestIP);
							if (eprsForService != null) {
								for (int j = 0; j < eprsForService.length; j++) {
									EndpointReference endpointReference = eprsForService[j];
									if (endpointReference != null) {
										String address = endpointReference
												.getAddress();
										if (address != null) {
											eprList.add(address);
										}
									}
								}
							}
						} catch (AxisFault axisFault) {
							log.warn(axisFault.getMessage());
						}
					}
				}
			}
		}
		eprs = (String[]) eprList.toArray(new String[eprList.size()]);
		return eprs;
	}

	/**
	 * Prints the given definition object.
	 * @param definition The definition.
	 * @param out The output stream the data to be written to. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param requestIP The host IP address.
	 * @throws AxisFault
	 * @throws WSDLException
	 */
	private synchronized void printDefinitionObject(Definition definition, OutputStream out,
			String requestIP) throws AxisFault, WSDLException {
        // Synchronized this method to fix the NullPointer exception occurred when load is high.
        // This error happens because wsdl4j is not thread safe and we are using same WSDL Definition for printing the
        // WSDL.
        // Please refer AXIS2-4511,AXIS2-4517,AXIS2-3276.
		if (isModifyUserWSDLPortAddress()) {
			setPortAddress(definition, requestIP);
		}
		if (!wsdlImportLocationAdjusted) {
			changeImportAndIncludeLocations(definition);
			wsdlImportLocationAdjusted = true;
		}
		WSDLWriter writer = WSDLFactory.newInstance().newWSDLWriter();
		writer.writeWSDL(definition, out);
	}

	public void printUserWSDL(OutputStream out, String wsdlName)
			throws AxisFault {
			printUserWSDL(out, wsdlName, null);
	}

	/**
	 * Prints the user WSDL.
	 * @param out The output stream for the data to be written. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param wsdlName The name of the WSDL.
	 * @param ip The host IP address.
	 * @throws AxisFault
	 */
	public void printUserWSDL(OutputStream out, String wsdlName, String ip)
			throws AxisFault {
		Definition definition = null;
		// first find the correct wsdl definition
		Parameter wsdlParameter = getParameter(WSDLConstants.WSDL_4_J_DEFINITION);
		if (wsdlParameter != null) {
			definition = (Definition) wsdlParameter.getValue();
		}

		if (definition != null) {
			try {
				printDefinitionObject(getWSDLDefinition(definition, wsdlName),
						out, ip);
			} catch (WSDLException e) {
				throw AxisFault.makeFault(e);
			}
		} else {
			printWSDLError(out);
		}

	}
	
    public void printUserWSDL2(OutputStream out, String wsdlName, String ip) throws AxisFault {
        Description description = null;
        // first find the correct wsdl definition
        Parameter wsdlParameter = getParameter(WSDLConstants.WSDL_20_DESCRIPTION);
        if (wsdlParameter != null) {
            description = (Description) wsdlParameter.getValue();
            try {
                org.apache.woden.WSDLFactory factory = org.apache.woden.WSDLFactory.newInstance();
                org.apache.woden.WSDLWriter writer = factory.newWSDLWriter();
                writer.writeWSDL(description.toElement(), out);
            } catch (org.apache.woden.WSDLException e) {
                throw AxisFault.makeFault(e);
            }
        }

}

	/**
	 * find the defintion object for given name
	 * 
	 * @param parentDefinition
	 * @param name
	 * @return wsdl definition
	 */
	private Definition getWSDLDefinition(Definition parentDefinition,
			String name) {

		if (name == null)
			return parentDefinition;

		Definition importedDefinition = null;
		Iterator iter = parentDefinition.getImports().values().iterator();
		Vector values = null;
		Import wsdlImport = null;
		for (; iter.hasNext();) {
			values = (Vector) iter.next();
			for (Iterator valuesIter = values.iterator(); valuesIter.hasNext();) {
				wsdlImport = (Import) valuesIter.next();
				if (wsdlImport.getLocationURI().endsWith(name)) {
					importedDefinition = wsdlImport.getDefinition();
					break;
				} else {
					importedDefinition = getWSDLDefinition(wsdlImport
							.getDefinition(), name);
				}
				if (importedDefinition != null) {
					break;
				}
			}
			if (importedDefinition != null) {
				break;
			}
		}
		return importedDefinition;
	}

	/**
	 * this procesdue recursively adjust the wsdl imports locations and the
	 * schmea import and include locations.
	 * 
	 * @param definition
	 */
	private void changeImportAndIncludeLocations(Definition definition) throws AxisFault {

        // adjust the schema locations in types section
        Types types = definition.getTypes();
        if (types != null) {
            List extensibilityElements = types.getExtensibilityElements();
            Object extensibilityElement = null;
            Schema schema = null;
            for (Iterator iter = extensibilityElements.iterator(); iter.hasNext();) {
                extensibilityElement = iter.next();
                if (extensibilityElement instanceof Schema) {
                    schema = (Schema) extensibilityElement;
                    changeLocations(schema.getElement());
                }
            }
        }

        Iterator iter = definition.getImports().values().iterator();
        Vector values = null;
        Import wsdlImport = null;
        String originalImprotString = null;
        for (; iter.hasNext();) {
            values = (Vector) iter.next();
            for (Iterator valuesIter = values.iterator(); valuesIter.hasNext();) {
                wsdlImport = (Import) valuesIter.next();
                originalImprotString = wsdlImport.getLocationURI();
                if (originalImprotString.indexOf("://") == -1 && originalImprotString.indexOf("?wsdl=") == -1){
                    wsdlImport.setLocationURI(this.getServiceEPR() + "?wsdl=" + originalImprotString);
                }
                changeImportAndIncludeLocations(wsdlImport.getDefinition());
            }
        }

    }

	/**
	 * change the schema Location in the elemment
	 * 
	 * @param element
	 */

	private void changeLocations(Element element) throws AxisFault {
        NodeList nodeList = element.getChildNodes();
        String tagName;
        for (int i = 0; i < nodeList.getLength(); i++) {
            tagName = nodeList.item(i).getLocalName();
            if (IMPORT_TAG.equals(tagName) || INCLUDE_TAG.equals(tagName)) {
                processImport(nodeList.item(i));
            }
        }
    }

	private void updateSchemaLocation(XmlSchema schema) throws AxisFault {
        for (XmlSchemaExternal xmlSchemaExternal : schema.getExternals()) {
            XmlSchema s = xmlSchemaExternal.getSchema();
            updateSchemaLocation(s, xmlSchemaExternal);
        }
    }

	private void updateSchemaLocation(XmlSchema s, XmlSchemaExternal xmlSchemaExternal) throws AxisFault {
        if (s != null) {
            String schemaLocation = xmlSchemaExternal.getSchemaLocation();

            if (schemaLocation.indexOf("://") == -1 && schemaLocation.indexOf("?xsd=") == -1) {
                String newscheamlocation = this.getServiceEPR() + "?xsd=" + schemaLocation;
                xmlSchemaExternal.setSchemaLocation(newscheamlocation);
            }
        }
    }
	   
	private void processImport(Node importNode) throws AxisFault {
        NamedNodeMap nodeMap = importNode.getAttributes();
        Node attribute;
        String attributeValue;
        for (int i = 0; i < nodeMap.getLength(); i++) {
            attribute = nodeMap.item(i);
            if (attribute.getNodeName().equals("schemaLocation")) {
                attributeValue = attribute.getNodeValue();
                if (attributeValue.indexOf("://") == -1 && attributeValue.indexOf("?xsd=") == -1) {
                    attribute.setNodeValue(this.getServiceEPR() + "?xsd=" + attributeValue);
                }
            }
        }
    }

    private String getServiceEPR() {
        String serviceEPR = null;
        Parameter parameter = this.getParameter(Constants.Configuration.GENERATE_ABSOLUTE_LOCATION_URIS);
        if ((parameter != null) && JavaUtils.isTrueExplicitly(parameter.getValue())) {
            String[] eprs = this.getEPRs();
            for (int i = 0; i < eprs.length; i++) {
                if ((eprs[i] != null) && (eprs[i].startsWith("http:"))){
                    serviceEPR = eprs[i];
                    break;
                }
            }
            if (serviceEPR == null){
                serviceEPR = eprs[0];
            }
        } else {
            serviceEPR = this.name;
        }
        if (serviceEPR.endsWith("/")){
            serviceEPR = serviceEPR.substring(0, serviceEPR.lastIndexOf("/"));
        }
        return serviceEPR;
    }

    /**
	 * Produces a XSD for this AxisService and prints it to the specified
	 * OutputStream.
	 * 
	 * @param out
	 *            destination stream, NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param xsd
	 *            schema name
	 * @return -1 implies not found, 0 implies redirect to root, 1 implies
	 *         found/printed a schema
	 * @throws IOException
	 */
	public int printXSD(OutputStream out, String xsd) throws IOException {

		// If we find a SchemaSupplier, use that
		SchemaSupplier supplier = (SchemaSupplier) getParameterValue("SchemaSupplier");
		if (supplier != null) {
			XmlSchema schema = supplier.getSchema(this, xsd);
			if (schema != null) {
			    updateSchemaLocation(schema);
				schema.write(new OutputStreamWriter(out, "UTF8"));
				out.flush();
				return 1;
			}
		}

		// call the populator
		populateSchemaMappings();
		Map schemaMappingtable = getSchemaMappingTable();
		ArrayList<XmlSchema> schemas = getSchema();

		// a name is present - try to pump the requested schema
        if ((xsd != null) && (!"".equals(xsd))) {
            XmlSchema schema = (XmlSchema) schemaMappingtable.get(xsd);
            if (schema == null) {
                int dotIndex = xsd.lastIndexOf('.');
                if (dotIndex > 0) {
                    String schemaKey = xsd.substring(0, dotIndex);
                    schemaKey = schemaKey.replace("./" , "");
                    schema = (XmlSchema) schemaMappingtable.get(schemaKey);
                }
            }
            if (schema != null) {
                // schema is there - pump it outs
                schema.write(new OutputStreamWriter(out, "UTF8"));
                out.flush();
            } else {
                // make sure we are only serving .xsd files and ignore requests with
                // ".." in the name.
                if (xsd.endsWith(".xsd") && xsd.indexOf("..") == -1) {
                    InputStream in = getClassLoader().getResourceAsStream(
                            DeploymentConstants.META_INF + "/" + xsd);
                    if (in != null) {
                        IOUtils.copy(in, out, true);
                    } else {
                        // Can't find the schema
                        return -1;
                    }
                } else {
                    // bad schema request
                    return -1;
                }
            }
		} else if (schemas.size() > 1) {
			// multiple schemas are present and the user specified
			// no name - in this case we cannot possibly pump a schema
			// so redirect to the service root
			return 0;
		} else {
			// user specified no name and there is only one schema
			// so pump that out
			ArrayList<XmlSchema> list = getSchema();
			if (list.size() > 0) {
				XmlSchema schema = getSchema(0);
				if (schema != null) {
					schema.write(new OutputStreamWriter(out, "UTF8"));
					out.flush();
				}
			} else {
				String xsdNotFound = "<error>"
						+ "<description>Unable to access schema for this service</description>"
						+ "</error>";
				out.write(xsdNotFound.getBytes());
				out.flush();
			}
		}
		return 1;
	}

	/**
	 * Produces a WSDL for this AxisService and prints it to the specified
	 * OutputStream.
	 * 
	 * @param out
	 *            destination stream. The WSDL will be sent here. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param requestIP
	 *            the hostname the WSDL request was directed at. This should be
	 *            the address that appears in the generated WSDL.
	 * @throws AxisFault
	 *             if an error occurs
	 */
	public void printWSDL(OutputStream out, String requestIP) throws AxisFault {
		// If we're looking for pre-existing WSDL, use that.
		if (isUseUserWSDL()) {
			printUserWSDL(out, null, requestIP);
			return;
		}

		// If we find a WSDLSupplier with WSDL 1.1 content, use that
		WSDLSupplier supplier = getUserDefinedWSDLSupplier("wsdl");
		if(supplier == null){
			supplier = (WSDLSupplier) getParameterValue(Constants.WSDL_SUPPLIER_PARAM);	
			if(supplier instanceof WSDL11SupplierTemplate){
			    ((WSDL11SupplierTemplate)supplier).init(this);
			}
		}			
		if (supplier != null) {
			Object wsdlContent = supplier.getWSDL(this);
			if( wsdlContent instanceof Definition){
				try {
					Definition definition = (Definition) wsdlContent;
					if (definition != null) {
					    changeImportAndIncludeLocations(definition);
	                    printDefinitionObject(getWSDLDefinition(definition, null),
								out, requestIP);
					}
				} catch (Exception e) {
					printWSDLError(out, e);
				}
			// wsdlContent can be a OMElement			
			} else if (wsdlContent instanceof OMElement) {
				OMElement wsdlElement = (OMElement) wsdlContent;
				QName wsdlName = wsdlElement.getQName();
				if (wsdlName != null
						&& wsdlName.getLocalPart().equals("definitions")
						&& wsdlName.getNamespaceURI().equals("http://schemas.xmlsoap.org/wsdl/")) {
					// TODO How to address port number/ ip name customization
					// here ?
					try {
						XMLPrettyPrinter.prettify(wsdlElement, out);
						out.flush();
					} catch (Exception e) {
						throw AxisFault.makeFault(e);
					}
				}
			}
			return;
		}

        if (isSetEndpointsToAllUsedBindings()) {
            Utils.setEndpointsToAllUsedBindings(this);
        }

		// Otherwise, generate WSDL ourselves
		String[] eprArray = requestIP == null ? new String[] { this.endpointName }
				: calculateEPRs(requestIP);
		getWSDL(out, eprArray);
	}

    /**
     * users can use this parameter when they supply a wsdl file with the .aar file
     * but wants to generate the endpoints for all available transports. here it assume
     * that the user has not set the useOriginalwsdl
     * @return
     */
	public boolean isSetEndpointsToAllUsedBindings() {
		Parameter parameter = getParameter("setEndpointsToAllUsedBindings");
		if (parameter != null) {
			String value = (String) parameter.getValue();
			if ("true".equals(value)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Print the WSDL with a default URL. This will be called only during
	 * codegen time.
	 * 
	 * @param out The output stream for the data to be written. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @throws AxisFault
	 */
	public void printWSDL(OutputStream out) throws AxisFault {
		printWSDL(out, null);
	}

        private AxisEndpoint getAxisEndpoint(String port) {
            // if service has a single endpoint, this will cause the [serviceName] address
            // to be used in wsdl instead of the [serviceName].[endpointName]
            if (endpointMap.size() == 1 && endpointMap.containsKey(getEndpointName())) {
                return null;
            } else {
                return (AxisEndpoint)endpointMap.get(port);
            }
        }

	private void setPortAddress(Definition definition, String requestIP)
			throws AxisFault {
		Iterator serviceItr = definition.getServices().values().iterator();
		while (serviceItr.hasNext()) {
			Service serviceElement = (Service) serviceItr.next();
			Iterator portItr = serviceElement.getPorts().values().iterator();
			while (portItr.hasNext()) {
				Port port = (Port) portItr.next();
				AxisEndpoint endpoint = getAxisEndpoint(port.getName());
				List list = port.getExtensibilityElements();
				for (int i = 0; i < list.size(); i++) {
					Object extensibilityEle = list.get(i);
					if (extensibilityEle instanceof SOAPAddress) {
						SOAPAddress soapAddress = (SOAPAddress) extensibilityEle;
						String existingAddress = soapAddress.getLocationURI();
						if (existingAddress == null
								|| existingAddress
										.equals("REPLACE_WITH_ACTUAL_URL")) {
							if (endpoint != null) {
								((SOAPAddress) extensibilityEle)
										.setLocationURI(endpoint
												.calculateEndpointURL(requestIP));
							} else {
								((SOAPAddress) extensibilityEle)
										.setLocationURI(getEPRs()[0]);
							}
						} else {
							if (requestIP == null) {
								if (endpoint != null) {
									((SOAPAddress) extensibilityEle)
											.setLocationURI(endpoint
													.calculateEndpointURL());
								} else {
									((SOAPAddress) extensibilityEle)
											.setLocationURI(getLocationURI(
													getEPRs(), existingAddress));
								}
							} else {
								if (endpoint != null) {
									((SOAPAddress) extensibilityEle)
											.setLocationURI(endpoint
													.calculateEndpointURL(requestIP));
								} else {
									((SOAPAddress) extensibilityEle)
											.setLocationURI(getLocationURI(
													calculateEPRs(requestIP),
													existingAddress));
								}
							}
						}
					} else if (extensibilityEle instanceof SOAP12Address) {
						SOAP12Address soapAddress = (SOAP12Address) extensibilityEle;
						String exsistingAddress = soapAddress.getLocationURI();
						if (requestIP == null) {
							if (endpoint != null) {
								((SOAP12Address) extensibilityEle)
										.setLocationURI(endpoint
												.calculateEndpointURL());

							} else {
								((SOAP12Address) extensibilityEle)
										.setLocationURI(getLocationURI(
												getEPRs(), exsistingAddress));
							}
						} else {
							if (endpoint != null) {
								((SOAP12Address) extensibilityEle)
										.setLocationURI(endpoint
												.calculateEndpointURL(requestIP));
							} else {
								((SOAP12Address) extensibilityEle)
										.setLocationURI(getLocationURI(
												calculateEPRs(requestIP),
												exsistingAddress));

							}
						}
					} else if (extensibilityEle instanceof HTTPAddress) {
						HTTPAddress httpAddress = (HTTPAddress) extensibilityEle;
						String exsistingAddress = httpAddress.getLocationURI();
						if (requestIP == null) {
							if (endpoint != null) {
								((HTTPAddress) extensibilityEle)
										.setLocationURI(endpoint
												.calculateEndpointURL());
							} else {
								((HTTPAddress) extensibilityEle)
										.setLocationURI(getLocationURI(
												getEPRs(), exsistingAddress));
							}
						} else {
							if (endpoint != null) {
								((HTTPAddress) extensibilityEle)
										.setLocationURI(endpoint
												.calculateEndpointURL(requestIP));
							} else {
								((HTTPAddress) extensibilityEle)
										.setLocationURI(getLocationURI(
												calculateEPRs(requestIP),
												exsistingAddress));
							}
						}
					} else if (extensibilityEle instanceof UnknownExtensibilityElement){
                        UnknownExtensibilityElement unknownExtensibilityElement = (UnknownExtensibilityElement) extensibilityEle;
                        Element element = unknownExtensibilityElement.getElement();
                        if (AddressingConstants.ENDPOINT_REFERENCE.equals(element.getLocalName())){
                            NodeList nodeList = element.getChildNodes();
                            Node node = null;
                            Element currentElement = null;
                            for (int j = 0; j < nodeList.getLength(); j++) {
                                node = nodeList.item(j);
                                if (node instanceof Element){
                                    currentElement = (Element) node;
                                    if (AddressingConstants.EPR_ADDRESS.equals(currentElement.getLocalName())) {
                                        String exsistingAddress = currentElement.getTextContent();
                                        if (requestIP == null) {
                                            if (endpoint != null) {
                                                currentElement.setTextContent(endpoint.calculateEndpointURL());
                                            } else {
                                                currentElement.setTextContent(getLocationURI(getEPRs(), exsistingAddress));
                                            }
                                        } else {
                                            if (endpoint != null) {
                                                currentElement.setTextContent(endpoint.calculateEndpointURL(requestIP));
                                            } else {
                                                currentElement.setTextContent(getLocationURI(calculateEPRs(requestIP),exsistingAddress));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
				}
			}
		}
	}

	/**
	 * this method returns the new IP address corresponding to the already
	 * existing ip
	 * 
	 * @param eprs
	 * @param epr
	 * @return corresponding Ip address
	 */
	private String getLocationURI(String[] eprs, String epr) throws AxisFault {
		String returnIP = null;
		if (epr != null) {
		    String existingProtocol = org.apache.axis2.util.Utils.getURIScheme(epr);
			if (existingProtocol != null) {
				for (int i = 0; i < eprs.length; i++) {
					if (existingProtocol.equals(org.apache.axis2.util.Utils.getURIScheme(eprs[i]))) {
						returnIP = eprs[i];
						break;
					}
				}
				if (returnIP != null) {
					return returnIP;
				} else {
					throw new AxisFault(
							"Server does not have an epr for the wsdl epr==>"
									+ epr);
				}
			} else {
				throw new AxisFault("invalid epr is given epr ==> " + epr);
			}
		} else {
			throw new AxisFault("No epr is given in the wsdl port");
		}
	}

	/**
	 * Retrieves the WSDL data associated with the given serviceURL.
	 * @param out The output stream for the WSDL data to be written, NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param serviceURL The fist element of this array i.e. serviceURL[0] is taken in retrieving the target service.
	 */
	private void getWSDL(OutputStream out, String[] serviceURL)
			throws AxisFault {
		// Retrieve WSDL using the same data retrieval path for GetMetadata
		// request.
		DataRetrievalRequest request = new DataRetrievalRequest();
		request.putDialect(DRConstants.SPEC.DIALECT_TYPE_WSDL);
		request.putOutputForm(OutputForm.INLINE_FORM);

		MessageContext context = new MessageContext();
		context.setAxisService(this);
		context.setTo(new EndpointReference(serviceURL[0]));

		Data[] result = getData(request, context);
		OMElement wsdlElement;
		if (result != null && result.length > 0) {
			wsdlElement = (OMElement) (result[0].getData());
			try {
				XMLPrettyPrinter.prettify(wsdlElement, out);
				out.flush();
			} catch (Exception e) {
				throw AxisFault.makeFault(e);
			}
		}
	}

	/**
	 * Prints generic WSDL error to the given output stream.
	 * @param out The output stream the data to be written to. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @throws AxisFault
	 */
	private void printWSDLError(OutputStream out) throws AxisFault {
		printWSDLError(out, null);
	}

	/**
	 * Prints WSDL error condition that is given in the exception.
	 * @param out The output stream for the error message to be written. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param e The exception describing the error condition.
	 * @throws AxisFault
	 */
	private void printWSDLError(OutputStream out, Exception e) throws AxisFault {
		try {
			String wsdlntfound = "<error>"
					+ "<description>Unable to generate WSDL 1.1 for this service</description>"
					+ "<reason>If you wish Axis2 to automatically generate the WSDL 1.1, then please "
					+ "set useOriginalwsdl as false in your services.xml</reason>";
			out.write(wsdlntfound.getBytes());
			if (e != null) {
				PrintWriter pw = new PrintWriter(out);
				e.printStackTrace(pw);
				pw.flush();
			}
			out.write("</error>".getBytes());
			out.flush();
		} catch (IOException ex) {
			throw AxisFault.makeFault(ex);
		}
	}

	/**
	 * Print the WSDL2.0 with a default URL. This will be called only during
	 * codegen time.
	 * 
	 * @param out The output stream for the data to be written for. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @throws AxisFault
	 */
	public void printWSDL2(OutputStream out) throws AxisFault {
		printWSDL2(out, null);
	}

	/**
	 * Prints WSDL2.0 data for the service with the given host IP address.
	 * @param out The output stream for the data to be written for. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
	 * @param requestIP The host IP address.
	 * @throws AxisFault
	 */
	public void printWSDL2(OutputStream out, String requestIP) throws AxisFault {
	 // If we're looking for pre-existing WSDL, use that.
        if (isUseUserWSDL()) {
            printUserWSDL2(out, null, requestIP);
            return;
        }
		AxisService2WSDL20 axisService2WSDL2 = new AxisService2WSDL20(this);
		
		// If we find a WSDLSupplier with WSDL 2.0 content, use that
		WSDLSupplier supplier = getUserDefinedWSDLSupplier("wsdl2");
		if(supplier == null){
			supplier = (WSDLSupplier) getParameterValue(Constants.WSDL_SUPPLIER_PARAM);	
			if(supplier instanceof WSDL20SupplierTemplate){
                ((WSDL20SupplierTemplate)supplier).init(this);
            }
		}				
		if (supplier != null) {
			Object wsdlContent = supplier.getWSDL(this);
			if( wsdlContent instanceof Description){
				try {
					Description definition = (Description) wsdlContent;
					if (definition != null) {
						//TODO  -- Need to implement this method for WSDL 2.0
					    //changeImportAndIncludeLocations(definition);
	                    printDescriptionObject(definition, out, requestIP);
					}
				} catch (Exception e) {
					printWSDLError(out, e);
				}
				
		    // wsdlContent can be a OMElement           
            } else if (wsdlContent instanceof OMElement) {
                OMElement wsdlElement = (OMElement) wsdlContent;
                QName wsdlName = wsdlElement.getQName();
                if (wsdlName != null
                        && wsdlName.getLocalPart().equals("description")
                        && wsdlName.getNamespaceURI().equals("http://www.w3.org/ns/wsdl")) {
                    // TODO How to address port number/ ip name customization
                    // here ?
                    try {
                        XMLPrettyPrinter.prettify(wsdlElement, out);
                        out.flush();
                    } catch (Exception e) {
                        throw AxisFault.makeFault(e);
                    }
                }
            }			
			return;
		}
		
		try {
			if (requestIP != null) {
				axisService2WSDL2.setEPRs(calculateEPRs(requestIP));
			}
			OMElement wsdlElement = axisService2WSDL2.generateOM();
			wsdlElement.serialize(out);
			out.flush();
		} catch (Exception e) {
			throw AxisFault.makeFault(e);
		}
	}

    /**
     * Produces a WSDL2 for this AxisService and prints it to the specified
     * OutputStream.
     * 
     * @param out
     *            destination stream. NOTE: the stream is not closed after the operation, 
	 *            it is the responsibility of the caller to close the stream after usage.
     * @param wsdl
     *            wsdl name
     * @return -1 implies not found, 0 implies redirect to root, 1 implies
     *         found/printed wsdl
     * @throws IOException
     */
    public int printWSDL2(OutputStream out, String requestIP, String wsdl) 
        throws IOException, AxisFault {    
        // if the wsdl2 parameter is not empty or null in the requested URL, get the wsdl  from the META-INF and serve.
        //else construct the wsdl out of axis service and serve.
        if ((wsdl != null ) && (!"".equals(wsdl))) {
            // make sure we are only serving .wsdl files and ignore requests with
            // ".." in the name.
            if (wsdl.endsWith(".wsdl") && wsdl.indexOf("..") == -1) {
                InputStream in = getClassLoader().getResourceAsStream(
                                    DeploymentConstants.META_INF + "/" + wsdl);
                if (in != null) {
                    IOUtils.copy(in, out, true);
                } else {
                    // can't find the wsdl
                    return -1;
                }
            } else {
                // bad wsdl2 request
                return -1;
            }
        } else {
            printWSDL2(out, requestIP);
        }
        
        return 1;
    }
    

	/**
	 * Method getClassLoader.
	 * 
	 * @return Returns ClassLoader.
	 */
	public ClassLoader getClassLoader() {
		return this.serviceClassLoader;
	}

	/**
	 * Gets the control operation which are added by module like RM.
	 */
	public ArrayList<AxisOperation> getControlOperations() {
		Iterator<AxisOperation> op_itr = getOperations();
		ArrayList<AxisOperation> operationList = new ArrayList<AxisOperation>();

		while (op_itr.hasNext()) {
			AxisOperation operation = (AxisOperation) op_itr.next();

			if (operation.isControlOperation()) {
				operationList.add(operation);
			}
		}

		return operationList;
	}

	public URL getFileName() {
		return fileName;
	}

    public long getLastUpdate() {
        return lastupdate;
    }

	public ModuleConfiguration getModuleConfig(String moduleName) {
		return (ModuleConfiguration) moduleConfigmap.get(moduleName);
	}

	public ArrayList<String> getModules() {
		return moduleRefs;
	}

	public String getName() {
		return name;
	}

	/**
	 * Method getOperation.
	 * 
	 * @param operationName
	 * @return Returns AxisOperation.
	 */
	public AxisOperation getOperation(QName operationName) {
        if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled())
            log.debug("Get operation for " + operationName);
                
		AxisOperation axisOperation = (AxisOperation) getChild(operationName);
		
        if (axisOperation == null) {
			axisOperation = (AxisOperation) getChild(new QName(
					getTargetNamespace(), operationName.getLocalPart()));
            
            if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled())
                log.debug("Target namespace: " + getTargetNamespace());
		}
        
		if (axisOperation == null) {
			axisOperation = (AxisOperation) operationsAliasesMap
					.get(operationName.getLocalPart());
            
            if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled())
                log.debug("Operations aliases map: " + operationsAliasesMap);
		}
        
        //The operation may be associated with a namespace other than the
        //target namespace, e.g. if the operation is from an imported wsdl.
        if (axisOperation == null) {            
            List namespaces = getImportedNamespaces();
            
            if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled())
                log.debug("Imported namespaces: " + namespaces);

            if (namespaces != null) {
                Iterator iterator = namespaces.iterator();
                
                while (iterator.hasNext()) {
                    String namespace = (String) iterator.next();
                    axisOperation = (AxisOperation) getChild(new QName(
                            namespace, operationName.getLocalPart()));
                    
                    if (axisOperation != null)
                        break;
                }
            }
        }

        if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled())
            log.debug("Found axis operation:  " + axisOperation);

		return axisOperation;
	}

	/**
	 * Returns the AxisOperation which has been mapped to the given alias.
	 * 
	 * @see #mapActionToOperation(String, AxisOperation)
	 * 
	 * @param action
	 *            the alias key
	 * @return Returns the corresponding AxisOperation or null if it isn't
	 *         found.
	 */
	public AxisOperation getOperationByAction(String action) {
		return (AxisOperation) operationsAliasesMap.get(action);
	}

	/**
	 * Returns the operation given a SOAP Action. This method should be called
	 * if only one Endpoint is defined for this Service. If more than one
	 * Endpoint exists, one of them will be picked. If more than one Operation
	 * is found with the given SOAP Action; null will be returned. If no
	 * particular Operation is found with the given SOAP Action; null will be
	 * returned. If the action is in the list of invaliad aliases, which means
	 * it did not uniquely identify an operation, a null will be returned.
	 * 
	 * @param soapAction
	 *            SOAP Action defined for the particular Operation
	 * @return Returns an AxisOperation if a unique Operation can be found with
	 *         the given SOAP Action otherwise will return null.
	 */
	public AxisOperation getOperationBySOAPAction(String soapAction) {

		// Check for illegal soapActions
		if ((soapAction == null) || soapAction.length() == 0) {
			if (log.isDebugEnabled()) {
				log.debug("getOperationBySOAPAction: " + soapAction
						+ " is null or ''. Returning null.");
			}
			return null;
		}

		// If the action maps to an alais that is not unique, then it can't be
		// used to map to
		// an operation.
		if (invalidOperationsAliases.contains(soapAction)) {
			if (log.isDebugEnabled()) {
				log.debug("getOperationBySOAPAction: " + soapAction
						+ " is an invalid operation alias. Returning null.");
			}
			return null;
		}

		// Get the operation from the action->operation map
		AxisOperation operation = (AxisOperation) operationsAliasesMap
				.get(soapAction);

		if (operation != null) {
			if (log.isDebugEnabled()) {
				log.debug("getOperationBySOAPAction: Operation (" + operation
						+ "," + operation.getName() + ") for soapAction: "
						+ soapAction + " found in action map.");
			}
			return operation;
		}

		// The final fallback is to check the operations for a matching name.

		Iterator children = getChildren();
		// I could not find any spec statement that explicitly forbids using a
		// short name in the SOAPAction header or wsa:Action element,
		// so I believe this to be valid. There may be customers using the
		// shortname as the SOAPAction in their client code that would
		// also require this support.
		while (children.hasNext() && (operation == null)) {
			AxisOperation op = (AxisOperation) children.next();
			if (op.getName().getLocalPart().equals(soapAction)) {
				operation = op;
			}
		}

		if (operation != null) {
			if (log.isDebugEnabled()) {
				log.debug("getOperationBySOAPAction: Operation (" + operation
						+ "," + operation.getName() + ") for soapAction: "
						+ soapAction + " found as child.");
			}
		}

		return operation;
	}

	/**
	 * Method getOperations.
	 * 
	 * @return Returns HashMap
	 */
	public Iterator<AxisOperation> getOperations() {
		return (Iterator<AxisOperation>) getChildren();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.axis2.description.ParameterInclude#getParameter(java.lang.String)
	 */

	/**
	 * Gets only the published operations.
	 */
	public ArrayList<AxisOperation> getPublishedOperations() {
		Iterator<AxisOperation> op_itr = getOperations();
		ArrayList<AxisOperation> operationList = new ArrayList<AxisOperation>();

		while (op_itr.hasNext()) {
			AxisOperation operation = (AxisOperation) op_itr.next();

			if (!operation.isControlOperation()) {
				operationList.add(operation);
			}
		}

		return operationList;
	}	

	/**
	 * Method setClassLoader.
	 * 
	 * @param classLoader
	 */
	public void setClassLoader(ClassLoader classLoader) {
		this.serviceClassLoader = classLoader;
	}

	public void setFileName(URL fileName) {
		this.fileName = fileName;
	}

    /**
     * Sets the current time as last update time of the service.
     */
    public void setLastUpdate() {
        lastupdate = new Date().getTime();
    }

	public void setName(String name) {
		this.name = name;
	}

	public ArrayList<XmlSchema> getSchema() {
		return schemaList;
	}

	public void addSchema(XmlSchema schema) {
		if (schema != null) {
			schemaList.add(schema);
			if (schema.getTargetNamespace() != null) {
				addSchemaNameSpace(schema);
			}
		}
	}

	public void addSchema(Collection<XmlSchema> schemas) {
		Iterator<XmlSchema> iterator = schemas.iterator();
		while (iterator.hasNext()) {
			XmlSchema schema = iterator.next();
            addSchema(schema);
		}
	}

	public boolean isWsdlFound() {
		return wsdlFound;
	}

	public void setWsdlFound(boolean wsdlFound) {
		this.wsdlFound = wsdlFound;
	}

	public String getScope() {
		return scope;
	}

	/**
	 * @param scope -
	 *            Available scopes : Constants.SCOPE_APPLICATION
	 *            Constants.SCOPE_TRANSPORT_SESSION Constants.SCOPE_SOAP_SESSION
	 *            Constants.SCOPE_REQUEST.equals
	 */
	public void setScope(String scope) {
		if (Constants.SCOPE_APPLICATION.equals(scope)
				|| Constants.SCOPE_TRANSPORT_SESSION.equals(scope)
				|| Constants.SCOPE_SOAP_SESSION.equals(scope)
				|| Constants.SCOPE_REQUEST.equals(scope)) {
			this.scope = scope;
		}
	}

	public boolean isUseDefaultChains() {
		return useDefaultChains;
	}

	public void setUseDefaultChains(boolean useDefaultChains) {
		this.useDefaultChains = useDefaultChains;
	}

	public Object getKey() {
		return this.name;
	}

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public String getSchemaTargetNamespace() {
		return schematargetNamespace;
	}

	public void setSchemaTargetNamespace(String schematargetNamespace) {
		this.schematargetNamespace = schematargetNamespace;
	}

	public String getSchemaTargetNamespacePrefix() {
		return schematargetNamespacePrefix;
	}

	public void setSchemaTargetNamespacePrefix(
			String schematargetNamespacePrefix) {
		this.schematargetNamespacePrefix = schematargetNamespacePrefix;
	}

	public String getTargetNamespace() {
		return targetNamespace;
	}

	public void setTargetNamespace(String targetNamespace) {
		this.targetNamespace = targetNamespace;
	}

	public String getTargetNamespacePrefix() {
		return targetNamespacePrefix;
	}

	public void setTargetNamespacePrefix(String targetNamespacePrefix) {
		this.targetNamespacePrefix = targetNamespacePrefix;
	}

	public XmlSchemaElement getSchemaElement(QName elementQName) {
		XmlSchemaElement element;
		for (int i = 0; i < schemaList.size(); i++) {
			XmlSchema schema = (XmlSchema) schemaList.get(i);
			if (schema != null) {
				element = schema.getElementByName(elementQName);
				if (element != null) {
					return element;
				}
			}
		}
		return null;
	}

	public boolean isEnableAllTransports() {
		return enableAllTransports;
	}

	/**
	 * To eneble service to be expose in all the transport
	 * 
	 * @param enableAllTransports
	 */
	public void setEnableAllTransports(boolean enableAllTransports) {
		this.enableAllTransports = enableAllTransports;
		eprs = calculateEPRs();
	}

	public List<String> getExposedTransports() {
		return this.exposedTransports;
	}

	public void setExposedTransports(List transports) {
		enableAllTransports = false;
		this.exposedTransports = transports;
		eprs = null; // Do not remove this. We need to force EPR
						// recalculation.
	}

	public void addExposedTransport(String transport) {
		enableAllTransports = false;
		if (!this.exposedTransports.contains(transport)) {
			this.exposedTransports.add(transport);
			try {
				eprs = calculateEPRs();
			} catch (Exception e) {
				eprs = null;
			}
		}
	}

	public void removeExposedTransport(String transport) {
		enableAllTransports = false;
		this.exposedTransports.remove(transport);
		try {
			eprs = calculateEPRs();
		} catch (Exception e) {
			eprs = null;
		}
	}

	public boolean isExposedTransport(String transport) {
		return exposedTransports.contains(transport);
	}

	public void onDisengage(AxisModule module) throws AxisFault {
		removeModuleOperations(module);
		for (Iterator operations = getChildren(); operations.hasNext();) {
			AxisOperation axisOperation = (AxisOperation) operations.next();
			axisOperation.disengageModule(module);
		}
		AxisConfiguration config = getAxisConfiguration();
		if (!config.isEngaged(module.getName())) {
			PhaseResolver phaseResolver = new PhaseResolver(config);
			phaseResolver.disengageModuleFromGlobalChains(module);
		}
	}

	/**
	 * Remove any operations which were added by a given module.
	 * 
	 * @param module
	 *            the module in question
	 */
	private void removeModuleOperations(AxisModule module) {
		HashMap<QName, AxisOperation> moduleOperations = module.getOperations();
		if (moduleOperations != null) {
			for (Iterator<AxisOperation> modOpsIter = moduleOperations.values().iterator(); modOpsIter
					.hasNext();) {
				AxisOperation operation = (AxisOperation) modOpsIter.next();
				removeOperation(operation.getName());
			}
		}
	}

	// #######################################################################################
	// APIs to create AxisService

	//

	/**
	 * To create a AxisService for a given WSDL and the created client is most
	 * suitable for client side invocation not for server side invocation. Since
	 * all the soap action and wsa action is added to operations
	 * 
	 * @param wsdlURL
	 *            location of the WSDL
	 * @param wsdlServiceName
	 *            name of the service to be invoke , if it is null then the
	 *            first one will be selected if there are more than one
	 * @param portName
	 *            name of the port , if there are more than one , if it is null
	 *            then the first one in the iterator will be selected
	 * @param options
	 *            Service client options, to set the target EPR
	 * @return AxisService , the created service will be return
	 */
	public static AxisService createClientSideAxisService(URL wsdlURL,
			QName wsdlServiceName, String portName, Options options)
			throws AxisFault {
        try {
            InputStream in = wsdlURL.openConnection().getInputStream();
            Document doc = XMLUtils.newDocument(in);
            String namespaceURI = doc.getDocumentElement().getNamespaceURI();
            if (Constants.NS_URI_WSDL11.equals(namespaceURI)) {
            	WSDLReader reader = WSDLUtil.newWSDLReaderWithPopulatedExtensionRegistry();
                reader.setFeature("javax.wsdl.importDocuments", true);
                Definition wsdlDefinition = reader.readWSDL(getBaseURI(wsdlURL.toString()), doc);
                if (wsdlDefinition != null) {
                    wsdlDefinition.setDocumentBaseURI(getDocumentURI(wsdlURL.toString()));
                }
                return createClientSideAxisService(wsdlDefinition, wsdlServiceName, portName,
                        options);
            } else if (Constants.NS_URI_WSDL20.equals(namespaceURI)) {
                org.apache.woden.WSDLReader reader = org.apache.woden.WSDLFactory.newInstance()
                        .newWSDLReader();
                WSDLSource source = reader.createWSDLSource();
                source.setSource(doc);
                source.setBaseURI(wsdlURL.toURI());
                Description description = reader.readWSDL(source);
                return createClientSideAxisService(description, wsdlServiceName, portName, options);
            } else {
                throw new AxisFault("No namespace found : Invalid WSDL");
            }
			
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw AxisFault.makeFault(e);
		} catch (ParserConfigurationException e) {
			log.error(e.getMessage(), e);
			throw AxisFault.makeFault(e);
		} catch (SAXException e) {
			log.error(e.getMessage(), e);
			throw AxisFault.makeFault(e);
		} catch (WSDLException e) {
			log.error(e.getMessage(), e);
			throw AxisFault.makeFault(e);
        } catch (org.apache.woden.WSDLException e) {
            log.error(e.getMessage(), e);
            throw AxisFault.makeFault(e);
        } catch (URISyntaxException e) {
            log.error(e.getMessage(), e);
            throw AxisFault.makeFault(e);
        }
	}

	private static String getBaseURI(String currentURI) {
		try {
			File file = new File(currentURI);
			if (file.exists()) {
				return file.getCanonicalFile().getParentFile().toURI()
						.toString();
			}
			String uriFragment = currentURI.substring(0, currentURI
					.lastIndexOf("/"));
			return uriFragment + (uriFragment.endsWith("/") ? "" : "/");
		} catch (IOException e) {
			return null;
		}
	}

	private static String getDocumentURI(String currentURI) {
		try {
			File file = new File(currentURI);
			return file.getCanonicalFile().toURI().toString();
		} catch (IOException e) {
			return null;
		}
	}

	public static AxisService createClientSideAxisService(
			Definition wsdlDefinition, QName wsdlServiceName, String portName,
			Options options) throws AxisFault {
		WSDL11ToAxisServiceBuilder serviceBuilder = new WSDL11ToAxisServiceBuilder(
				wsdlDefinition, wsdlServiceName, portName);
		serviceBuilder.setServerSide(false);
		AxisService axisService = serviceBuilder.populateService();
		AxisEndpoint axisEndpoint = (AxisEndpoint) axisService.getEndpoints()
				.get(axisService.getEndpointName());

		if (axisEndpoint != null) {
            options.setTo(new EndpointReference(axisEndpoint.getEndpointURL()));
			options.setSoapVersionURI((String) axisEndpoint.getBinding()
					.getProperty(WSDL2Constants.ATTR_WSOAP_VERSION));
		}
		return axisService;
	}

	/**
	 * To create an AxisService using given service impl class name first
	 * generate schema corresponding to the given java class , next for each
	 * methods AxisOperation will be created. If the method is in-out it will
	 * uses RPCMessageReceiver else RPCInOnlyMessageReceiver <p/> Note : Inorder
	 * to work this properly RPCMessageReceiver should be available in the class
	 * path otherewise operation can not continue
	 * 
	 * @param implClass
	 *            Service implementation class
	 * @param axisConfig
	 *            Current AxisConfiguration
	 * @return return created AxisSrevice the creted service , it can either be
	 *         null or valid service
	 */
	public static AxisService createService(String implClass,
			AxisConfiguration axisConfig) throws AxisFault {

		try {
			HashMap<String, MessageReceiver> messageReciverMap = new HashMap<String, MessageReceiver>();
			Class inOnlyMessageReceiver = Loader
					.loadClass("org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver");
			MessageReceiver messageReceiver = (MessageReceiver) inOnlyMessageReceiver
					.newInstance();
			messageReciverMap.put(WSDL2Constants.MEP_URI_IN_ONLY,
					messageReceiver);
			Class inoutMessageReceiver = Loader
					.loadClass("org.apache.axis2.rpc.receivers.RPCMessageReceiver");
			MessageReceiver inOutmessageReceiver = (MessageReceiver) inoutMessageReceiver
					.newInstance();
			messageReciverMap.put(WSDL2Constants.MEP_URI_IN_OUT,
					inOutmessageReceiver);
			messageReciverMap.put(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY,
					inOutmessageReceiver);

			return createService(implClass, axisConfig, messageReciverMap,
					null, null, axisConfig.getSystemClassLoader());
		} catch (Exception e) {
			throw AxisFault.makeFault(e);
		}
	}

	/**
	 * messageReceiverClassMap will hold the MessageReceivers for given meps.
	 * Key will be the mep and value will be the instance of the MessageReceiver
	 * class. Ex: Map mrMap = new HashMap();
	 * mrMap.put("http://www.w3.org/ns/wsdl/in-only",
	 * RPCInOnlyMessageReceiver.class.newInstance());
	 * mrMap.put("http://www.w3.org/ns/wsdl/in-out",
	 * RPCMessageReceiver.class.newInstance());
	 * 
	 * @param implClass
	 * @param axisConfiguration
	 * @param messageReceiverClassMap
	 * @param targetNamespace
	 * @param schemaNamespace
	 * @throws AxisFault
	 */
	public static AxisService createService(String implClass,
			AxisConfiguration axisConfiguration, Map messageReceiverClassMap,
			String targetNamespace, String schemaNamespace, ClassLoader loader)
			throws AxisFault {
		int index = implClass.lastIndexOf(".");
		String serviceName;
		if (index > 0) {
			serviceName = implClass.substring(index + 1, implClass.length());
		} else {
			serviceName = implClass;
		}

		SchemaGenerator schemaGenerator;
		ArrayList excludeOpeartion = new ArrayList();
		AxisService service = new AxisService();
		service.setParent(axisConfiguration);
		service.setName(serviceName);

		try {
			Parameter generateBare = service
					.getParameter(Java2WSDLConstants.DOC_LIT_BARE_PARAMETER);
			if (generateBare != null && "true".equals(generateBare.getValue())) {
				schemaGenerator = new DocLitBareSchemaGenerator(loader,
						implClass, schemaNamespace,
						Java2WSDLConstants.SCHEMA_NAMESPACE_PRFIX, service);
			} else {
				schemaGenerator = new DefaultSchemaGenerator(loader, implClass,
						schemaNamespace,
						Java2WSDLConstants.SCHEMA_NAMESPACE_PRFIX, service);
			}
			schemaGenerator
					.setElementFormDefault(Java2WSDLConstants.FORM_DEFAULT_UNQUALIFIED);
			Utils.addExcludeMethods(excludeOpeartion);
			schemaGenerator.setExcludeMethods(excludeOpeartion);
		} catch (Exception e) {
			throw AxisFault.makeFault(e);
		}

		return createService(implClass, serviceName, axisConfiguration,
				messageReceiverClassMap, targetNamespace, loader,
				schemaGenerator, service);
	}

	/**
	 * messageReceiverClassMap will hold the MessageReceivers for given meps.
	 * Key will be the mep and value will be the instance of the MessageReceiver
	 * class. Ex: Map mrMap = new HashMap();
	 * mrMap.put("http://www.w3.org/ns/wsdl/in-only",
	 * RPCInOnlyMessageReceiver.class.newInstance());
	 * mrMap.put("http://www.w3.org/ns/wsdl/in-out",
	 * RPCMessageReceiver.class.newInstance());
	 * 
	 * @param implClass
	 * @param axisConfiguration
	 * @param messageReceiverClassMap
	 * @param targetNamespace
	 * @throws AxisFault
	 */
	public static AxisService createService(String implClass,
			String serviceName, AxisConfiguration axisConfiguration,
			Map<String, MessageReceiver> messageReceiverClassMap, String targetNamespace,
			ClassLoader loader, SchemaGenerator schemaGenerator,
			AxisService axisService) throws AxisFault {
		Parameter parameter = new Parameter(Constants.SERVICE_CLASS, implClass);
		OMElement paraElement = Utils.getParameter(Constants.SERVICE_CLASS,
				implClass, false);
		parameter.setParameterElement(paraElement);
		axisService.setUseDefaultChains(false);
		axisService.addParameter(parameter);
		axisService.setName(serviceName);
		axisService.setClassLoader(loader);

		NamespaceMap map = new NamespaceMap();
		map.put(Java2WSDLConstants.AXIS2_NAMESPACE_PREFIX,
				Java2WSDLConstants.AXIS2_XSD);
		map.put(Java2WSDLConstants.DEFAULT_SCHEMA_NAMESPACE_PREFIX,
				Java2WSDLConstants.URI_2001_SCHEMA_XSD);
		axisService.setNamespaceMap(map);
		Utils.processBeanPropertyExclude(axisService);
		axisService.setElementFormDefault(false);
		try {
			axisService.addSchema(schemaGenerator.generateSchema());
		} catch (Exception e) {
			throw AxisFault.makeFault(e);
		}
		axisService.setSchemaTargetNamespace(schemaGenerator
				.getSchemaTargetNameSpace());
		axisService.setTypeTable(schemaGenerator.getTypeTable());
		if (targetNamespace == null) {
			targetNamespace = schemaGenerator.getSchemaTargetNameSpace();
		}
		if (targetNamespace != null && !"".equals(targetNamespace)) {
			axisService.setTargetNamespace(targetNamespace);
		}
		Method[] method = schemaGenerator.getMethods();
		PhasesInfo pinfo = axisConfiguration.getPhasesInfo();
		for (int i = 0; i < method.length; i++) {
			Method jmethod = method[i];

            String methodName = jmethod.getName();         
            
			AxisOperation operation = axisService.getOperation(new QName(methodName));

			String mep = operation.getMessageExchangePattern();
			MessageReceiver mr;
			if (messageReceiverClassMap != null) {

				if (messageReceiverClassMap.get(mep) != null) {
					Object obj = messageReceiverClassMap.get(mep);
					if (obj instanceof MessageReceiver) {
						mr = (MessageReceiver) obj;
						operation.setMessageReceiver(mr);
					} else {
						log
								.error("Object is not an instance of MessageReceiver, thus, default MessageReceiver has been set");
						mr = axisConfiguration.getMessageReceiver(operation
								.getMessageExchangePattern());
						operation.setMessageReceiver(mr);
					}
				} else {
					log
							.error("Required MessageReceiver couldn't be found, thus, default MessageReceiver has been used");
					mr = axisConfiguration.getMessageReceiver(operation
							.getMessageExchangePattern());
					operation.setMessageReceiver(mr);
				}
			} else {
				log
						.error("MessageRecevierClassMap couldn't be found, thus, default MessageReceiver has been used");
				mr = axisConfiguration.getMessageReceiver(operation
						.getMessageExchangePattern());
				operation.setMessageReceiver(mr);
			}
			pinfo.setOperationPhases(operation);
			axisService.addOperation(operation);
		}

		String endpointName = axisService.getEndpointName();
		if ((endpointName == null || endpointName.length() == 0)
				&& axisService.getAxisConfiguration() != null) {
			Utils.addEndpointsToService(axisService, axisService.getAxisConfiguration());
		}

		return axisService;

	}

	public void removeOperation(QName opName) {
		AxisOperation operation = getOperation(opName);
		if (operation != null) {
			removeChild(opName);
			ArrayList<String> mappingList = operation.getWSAMappingList();
			if (mappingList != null) {
				for (int i = 0; i < mappingList.size(); i++) {
					String actionMapping = (String) mappingList.get(i);
					operationsAliasesMap.remove(actionMapping);
					invalidOperationsAliases.remove(actionMapping);
				}
			}
			operationsAliasesMap.remove(operation.getName().getLocalPart());
			invalidOperationsAliases.remove(operation.getName().getLocalPart());
		}
	}

	/**
	 * Get the namespace map for this service.
	 * 
	 * @return a Map of prefix (String) to namespace URI (String)
	 */
	public Map getNamespaceMap() {
		return namespaceMap;
	}
    
    /**
     * Get the namespaces associated with imported WSDLs
     * 
     * @return a <code>List</code> of namespace URIs (String)
     */
    public List getImportedNamespaces() {
        return importedNamespaces;
    }

    /**
     * Set the namespaces associated with imported WSDLs
     * 
     * @param importedNamespaces
     */
    public void setImportedNamespaces(List importedNamespaces) {
        this.importedNamespaces = importedNamespaces;
    }
    

	public void setNamespaceMap(NamespaceMap namespaceMap) {
		this.namespaceMap = namespaceMap;
	}

	private void addSchemaNameSpace(XmlSchema schema) {
		String targetNameSpace = schema.getTargetNamespace();
		String prefix = schema.getNamespaceContext().getPrefix(targetNameSpace);

		if (namespaceMap == null) {
			namespaceMap = new NamespaceMap();
		}

		if (!namespaceMap.values().contains(targetNameSpace)) {
			// i.e this target namespace not exists in the namesapce map
			// find a non exists prefix to add this target namesapce
			while ((prefix == null) || namespaceMap.keySet().contains(prefix)) {
				prefix = "ns" + nsCount++;
			}
			namespaceMap.put(prefix, targetNameSpace);
		}

	}

	public Map populateSchemaMappings() {
		// when calling from other than codegen. i.e from deployment
		// engine we don't have to override the absolute http locations.
		return populateSchemaMappings(false);
	}

	/**
	 * runs the schema mappings if it has not been run previously it is best
	 * that this logic be in the axis service since one can call the axis
	 * service to populate the schema mappings
	 */
	public Map populateSchemaMappings(boolean overrideAbsoluteAddress) {

		// populate the axis service with the necessary schema references
		ArrayList schema = this.schemaList;
		Map changedSchemaLocations = null;
		if (!this.schemaLocationsAdjusted) {
			Hashtable nameTable = new Hashtable();
			Hashtable sourceURIToNewLocationMap = new Hashtable();
			// calculate unique names for the schemas
			calculateSchemaNames(schema, nameTable, sourceURIToNewLocationMap,
					overrideAbsoluteAddress);
			// adjust the schema locations as per the calculated names
			changedSchemaLocations = adjustSchemaNames(schema, nameTable,
					sourceURIToNewLocationMap);
			// reverse the nametable so that there is a mapping from the
			// name to the schemaObject
			setSchemaMappingTable(swapMappingTable(nameTable));
			setSchemaLocationsAdjusted(true);
		}
		return changedSchemaLocations;
	}

	/**
	 * run 1 -calcualte unique names
	 * 
	 * @param schemas
	 */
	private void calculateSchemaNames(List schemas, Hashtable nameTable,
			Hashtable sourceURIToNewLocationMap, boolean overrideAbsoluteAddress) {
		// first traversal - fill the hashtable
		for (int i = 0; i < schemas.size(); i++) {
			XmlSchema schema = (XmlSchema) schemas.get(i);
			for (XmlSchemaExternal externalSchema : schema.getExternals()) {
				if (externalSchema != null) {
                    XmlSchema s = externalSchema.getSchema();
					if (s != null
							&& getScheamLocationWithDot(
									sourceURIToNewLocationMap, s) == null) {
						// insert the name into the table
						insertIntoNameTable(nameTable, s,
								sourceURIToNewLocationMap,
								overrideAbsoluteAddress);
						// recursively call the same procedure
						calculateSchemaNames(Arrays
								.asList(new XmlSchema[] { s }), nameTable,
								sourceURIToNewLocationMap,
								overrideAbsoluteAddress);
					}
				}
			}
		}
	}

	/**
	 * A quick private sub routine to insert the names
	 * 
	 * @param nameTable
	 * @param s
	 */
	private void insertIntoNameTable(Hashtable nameTable, XmlSchema s,
			Hashtable sourceURIToNewLocationMap, boolean overrideAbsoluteAddress) {
		String sourceURI = s.getSourceURI();
		// check whether the sourece uri is an absolute one and are
		// we allowed to override it.
		// if the absolute uri overriding is not allowed the use the
		// original sourceURI as new one
		if (sourceURI.startsWith("http") && !overrideAbsoluteAddress) {
			nameTable.put(s, sourceURI);
			sourceURIToNewLocationMap.put(sourceURI, sourceURI);
		} else {
			String newURI = sourceURI.substring(sourceURI.lastIndexOf('/') + 1);
			if (newURI.endsWith(".xsd")) {
				// remove the .xsd extention
				newURI = newURI.substring(0, newURI.lastIndexOf("."));
			} else {
				newURI = "xsd" + count++;
			}

			newURI = customSchemaNameSuffix != null ? newURI
					+ customSchemaNameSuffix : newURI;
			// make it unique
			while (nameTable.containsValue(newURI)) {
				newURI = newURI + count++;
			}

			nameTable.put(s, newURI);
			sourceURIToNewLocationMap.put(sourceURI, newURI);
		}

	}

	/**
	 * Run 2 - adjust the names
	 */
	private Map adjustSchemaNames(List schemas, Hashtable nameTable,
			Hashtable sourceURIToNewLocationMap) {
		Hashtable importedSchemas = new Hashtable();
		// process the schemas in the main schema list
		for (int i = 0; i < schemas.size(); i++) {
			adjustSchemaName((XmlSchema) schemas.get(i), nameTable,
					importedSchemas, sourceURIToNewLocationMap);
		}
		// process all the rest in the name table
		Enumeration nameTableKeys = nameTable.keys();
		while (nameTableKeys.hasMoreElements()) {
			adjustSchemaName((XmlSchema) nameTableKeys.nextElement(),
					nameTable, importedSchemas, sourceURIToNewLocationMap);

		}
		return importedSchemas;
	}

	/**
	 * Adjust a single schema
	 * 
	 * @param parentSchema
	 * @param nameTable
	 */
    private void adjustSchemaName(XmlSchema parentSchema, Hashtable nameTable,
                                  Hashtable importedScheams, Hashtable sourceURIToNewLocationMap) {
        for (XmlSchemaExternal xmlSchemaExternal : parentSchema.getExternals()) {
            XmlSchema s = xmlSchemaExternal.getSchema();
            adjustSchemaLocation(s, xmlSchemaExternal, nameTable,
                                 importedScheams, sourceURIToNewLocationMap);
        }
    }

    /**
	 * Adjusts a given schema location
	 * 
	 * @param s
	 * @param xmlSchemaExternal
	 * @param nameTable
	 */
    private void adjustSchemaLocation(XmlSchema s,
                                      XmlSchemaExternal xmlSchemaExternal, Hashtable nameTable,
                                      Hashtable importedScheams, Hashtable sourceURIToNewLocationMap) {
        if (s != null) {
            String schemaLocation = xmlSchemaExternal.getSchemaLocation();

            String newscheamlocation = customSchemaNamePrefix == null ?
                    // use the default mode
                    (this.getServiceEPR() + "?xsd=" + getScheamLocationWithDot(
                            sourceURIToNewLocationMap, s))
                    :
                    // custom prefix is present - add the custom prefix
                    (customSchemaNamePrefix + getScheamLocationWithDot(
                            sourceURIToNewLocationMap, s));
            xmlSchemaExternal.setSchemaLocation(newscheamlocation);
            importedScheams.put(schemaLocation, newscheamlocation);
        }
    }

	private Object getScheamLocationWithDot(
			Hashtable sourceURIToNewLocationMap, XmlSchema s) {
		String o = (String) sourceURIToNewLocationMap.get(s.getSourceURI());
		if (o != null && o.indexOf(".") < 0) {
			return o + ".xsd";
		}
		return o;
	}

	/**
	 * Swap the key,value pairs
	 * 
	 * @param originalTable
	 */
	private Map swapMappingTable(Map originalTable) {
		HashMap swappedTable = new HashMap(originalTable.size());
		Iterator keys = originalTable.keySet().iterator();
		Object key;
		while (keys.hasNext()) {
			key = keys.next();
			swappedTable.put(originalTable.get(key), key);
		}

		return swappedTable;
	}

	public boolean isClientSide() {
		return clientSide;
	}

	public void setClientSide(boolean clientSide) {
		this.clientSide = clientSide;
	}

	public boolean isElementFormDefault() {
		return elementFormDefault;
	}

	public void setElementFormDefault(boolean elementFormDefault) {
		this.elementFormDefault = elementFormDefault;
	}

	/**
	 * User can set a parameter in services.xml saying he want to show the
	 * original wsdl that he put into META-INF once someone ask for ?wsdl so if
	 * you want to use your own wsdl then add following parameter into
	 * services.xml <parameter name="useOriginalwsdl">true</parameter>
	 */
	public boolean isUseUserWSDL() {
		Parameter parameter = getParameter("useOriginalwsdl");
		if (parameter != null) {
			String value = (String) parameter.getValue();
			if ("true".equals(value)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * By default the port address in user WSDLs is modified, set the following
	 * parameter to override this behaviour <parameter
	 * name="modifyUserWSDLPortAddress">false</parameter>
	 */
	public boolean isModifyUserWSDLPortAddress() {
		Parameter parameter = getParameter("modifyUserWSDLPortAddress");
		if (parameter != null) {
			String value = (String) parameter.getValue();
			if ("false".equals(value)) {
				return false;
			}
		}
		return true;
	}

	public ServiceLifeCycle getServiceLifeCycle() {
		return serviceLifeCycle;
	}

	public void setServiceLifeCycle(ServiceLifeCycle serviceLifeCycle) {
		this.serviceLifeCycle = serviceLifeCycle;
	}

	public Map getP2nMap() {
		return p2nMap;
	}

	public void setP2nMap(Map p2nMap) {
		this.p2nMap = p2nMap;
	}

	public ObjectSupplier getObjectSupplier() {
		return objectSupplier;
	}

	public void setObjectSupplier(ObjectSupplier objectSupplier) {
		this.objectSupplier = objectSupplier;
	}

	public TypeTable getTypeTable() {
		return typeTable;
	}

	public void setTypeTable(TypeTable typeTable) {
		this.typeTable = typeTable;
	}

	/**
	 * Find a data locator from the available data locators (both configured and
	 * default ones) to retrieve Metadata or data specified in the request.
	 * 
	 * @param request
	 *            an {@link DataRetrievalRequest} object
	 * @param msgContext
	 *            message context
	 * @return array of {@link Data} object for the request.
	 * @throws AxisFault
	 */

	public Data[] getData(DataRetrievalRequest request,
			MessageContext msgContext) throws AxisFault {

		Data[] data;
		String dialect = request.getDialect();
		AxisDataLocator dataLocator = null;
		int nextDataLocatorIndex = 0;
		int totalLocators = availableDataLocatorTypes.length;
		for (int i = 0; i < totalLocators; i++) {
			dataLocator = getDataLocator(availableDataLocatorTypes[i], dialect);
			if (dataLocator != null) {
				nextDataLocatorIndex = i + 1;
				break;
			}
		}

		if (dataLocator == null) {
			return null;
		}

		data = dataLocator.getData(request, msgContext);
		// Null means Data Locator not understood request. Automatically find
		// Data Locator in the hierarchy to process the request.
		if (data == null) {
			if (nextDataLocatorIndex < totalLocators) {
				data = bubbleupDataLocators(nextDataLocatorIndex, request,
						msgContext);
			}

		}
		return data;
	}

	/*
	 * To search the next Data Locator from the available Data Locators that
	 * understood the data retrieval request.
	 */
	private Data[] bubbleupDataLocators(int nextIndex,
			DataRetrievalRequest request, MessageContext msgContext)
			throws AxisFault {
		Data[] data = null;
		if (nextIndex < availableDataLocatorTypes.length) {
			AxisDataLocator dataLocator = getDataLocator(
					availableDataLocatorTypes[nextIndex], request.getDialect());
			nextIndex++;
			if (dataLocator != null) {
				data = dataLocator.getData(request, msgContext);
				if (data == null) {
					data = bubbleupDataLocators(nextIndex, request, msgContext);
				} else {
					return data;
				}

			} else {
				data = bubbleupDataLocators(nextIndex, request, msgContext);
			}

		}
		return data;
	}

	/**
	 * Save data Locator configured at service level for this Axis Service
	 * 
	 * @param dialect-
	 *            an absolute URI represents the Dialect i.e. WSDL, Policy,
	 *            Schema or "ServiceLevel" for non-dialect service level data
	 *            locator.
	 * @param dataLocatorClassName -
	 *            class name of the Data Locator configured to support data
	 *            retrieval for the specified dialect.
	 */
	public void addDataLocatorClassNames(String dialect,
			String dataLocatorClassName) {
		dataLocatorClassNames.put(dialect, dataLocatorClassName);
	}

	/*
	 * Get data locator instance based on the LocatorType and dialect.
	 */
	public AxisDataLocator getDataLocator(LocatorType locatorType,
			String dialect) throws AxisFault {
		AxisDataLocator locator;
		if (locatorType == LocatorType.SERVICE_DIALECT) {
			locator = getServiceDataLocator(dialect);
		} else if (locatorType == LocatorType.SERVICE_LEVEL) {
			locator = getServiceDataLocator(DRConstants.SERVICE_LEVEL);
		} else if (locatorType == LocatorType.GLOBAL_DIALECT) {
			locator = getGlobalDataLocator(dialect);
		} else if (locatorType == LocatorType.GLOBAL_LEVEL) {
			locator = getGlobalDataLocator(DRConstants.GLOBAL_LEVEL);
		} else if (locatorType == LocatorType.DEFAULT_AXIS) {
			locator = getDefaultDataLocator();
		} else {
			locator = getDefaultDataLocator();
		}

		return locator;
	}

	// Return default Axis2 Data Locator
	private AxisDataLocator getDefaultDataLocator()
			throws DataRetrievalException {

		if (defaultDataLocator == null) {
			defaultDataLocator = new AxisDataLocatorImpl(this);
		}

		defaultDataLocator.loadServiceData();

		return defaultDataLocator;
	}

	/*
	 * Checks if service level data locator configured for specified dialect.
	 * Returns an instance of the data locator if exists, and null otherwise.
	 */
	private AxisDataLocator getServiceDataLocator(String dialect)
			throws AxisFault {
		AxisDataLocator locator;
		locator = (AxisDataLocator) dataLocators.get(dialect);
		if (locator == null) {
			String className = (String) dataLocatorClassNames.get(dialect);
			if (className != null) {
				locator = loadDataLocator(className);
				dataLocators.put(dialect, locator);
			}

		}

		return locator;

	}

	/*
	 * Checks if global level data locator configured for specified dialect.
	 * @param dialect- an absolute URI represents the Dialect i.e. WSDL, Policy,
	 * Schema or "GlobalLevel" for non-dialect Global level data locator.
	 * Returns an instance of the data locator if exists, and null otherwise.
	 */

	public AxisDataLocator getGlobalDataLocator(String dialect)
			throws AxisFault {
		AxisConfiguration axisConfig = getAxisConfiguration();
		AxisDataLocator locator = null;
		if (axisConfig != null) {
			locator = axisConfig.getDataLocator(dialect);
			if (locator == null) {
				String className = axisConfig.getDataLocatorClassName(dialect);
				if (className != null) {
					locator = loadDataLocator(className);
					axisConfig.addDataLocator(dialect, locator);
				}
			}
		}

		return locator;

	}

	protected AxisDataLocator loadDataLocator(String className)
			throws AxisFault {

		AxisDataLocator locator;

		try {
			Class dataLocator;
			dataLocator = Class.forName(className, true, serviceClassLoader);
			locator = (AxisDataLocator) dataLocator.newInstance();
		} catch (ClassNotFoundException e) {
			throw AxisFault.makeFault(e);
		} catch (IllegalAccessException e) {
			throw AxisFault.makeFault(e);
		} catch (InstantiationException e) {
			throw AxisFault.makeFault(e);

		}

		return locator;
	}

	/**
	 * Set the map of WSDL message element QNames to AxisOperations for this
	 * service. This map is used during SOAP Body-based routing for
	 * document/literal bare services to match the first child element of the
	 * SOAP Body element to an operation. (Routing for RPC and document/literal
	 * wrapped services occurs via the operationsAliasesMap.) <p/> From section
	 * 4.7.6 of the WS-I BP 1.1: the "operation signature" is "the fully
	 * qualified name of the child element of SOAP body of the SOAP input
	 * message described by an operation in a WSDL binding," and thus this map
	 * must be from a QName to an operation.
	 * 
	 * @param messageElementQNameToOperationMap
	 *            The map from WSDL message element QNames to AxisOperations.
	 */
	public void setMessageElementQNameToOperationMap(
			Map messageElementQNameToOperationMap) {
		this.messageElementQNameToOperationMap = messageElementQNameToOperationMap;
	}

	/**
	 * Look up an AxisOperation for this service based off of an element QName
	 * from a WSDL message element.
	 * 
	 * @param messageElementQName
	 *            The QName to search for.
	 * @return The AxisOperation registered to the QName or null if no match was
	 *         found.
	 * @see #setMessageElementQNameToOperationMap(Map)
	 */
	public AxisOperation getOperationByMessageElementQName(
			QName messageElementQName) {
		return (AxisOperation) messageElementQNameToOperationMap
				.get(messageElementQName);
	}

	/**
	 * Add an entry to the map between element QNames in WSDL messages and
	 * AxisOperations for this service.
	 * 
	 * @param messageElementQName
	 *            The QName of the element on the input message that maps to the
	 *            given operation.
	 * @param operation
	 *            The AxisOperation to be mapped to.
	 * @see #setMessageElementQNameToOperationMap(Map)
	 */
	public void addMessageElementQNameToOperationMapping(
			QName messageElementQName, AxisOperation operation) {
		// when setting an operation we have to set it only if the
		// messegeElementQName does not
		// exists in the map.
		// does exists means there are two or more operations which has the same
		// input element (in doc/literal
		// this is possible. In this case better to set it as null without
		// giving
		// a random operation.
		if (messageElementQNameToOperationMap.containsKey(messageElementQName)
				&& messageElementQNameToOperationMap.get(messageElementQName) != operation) {
			messageElementQNameToOperationMap.put(messageElementQName, null);
		} else {
			messageElementQNameToOperationMap.put(messageElementQName,
					operation);
		}

	}

	/**
	 * @deprecated use {@link AxisEndpoint#getEndpointURL()}
	 */
	public String getEndpointURL() {
		return endpointURL;
	}

	/**
	 * @deprecated use {@link AxisEndpoint#setEndpointURL(String)}
	 */
	public void setEndpointURL(String endpointURL) {
		this.endpointURL = endpointURL;
	}

	// TODO : Explain what goes in this map!
	public Map<String, AxisEndpoint> getEndpoints() {
		return endpointMap;
	}

	public boolean isCustomWsdl() {
		return customWsdl;
	}

	public void setCustomWsdl(boolean customWsdl) {
		this.customWsdl = customWsdl;
	}

	public List getOperationsNameList() {
		return operationsNameList;
	}

	public void setOperationsNameList(List operationsNameList) {
		this.operationsNameList = operationsNameList;
	}

	public AxisServiceGroup getAxisServiceGroup() {
		return (AxisServiceGroup) parent;
	}

	public void setParent(AxisServiceGroup parent) {
		this.parent = parent;
	}

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

	public ExcludeInfo getExcludeInfo() {
		return excludeInfo;
	}

	public void setExcludeInfo(ExcludeInfo excludeInfo) {
		this.excludeInfo = excludeInfo;
	}

	public void registerPolicy(String key, Policy policy) {
		policyMap.put(key, policy);
	}

	public Policy lookupPolicy(String key) {
		return (Policy) policyMap.get(key);
	}
    
    /**
     * Add a ServiceContextListener
     * @param scl
     */
    public void addMessageContextListener(MessageContextListener scl) {
        messageContextListeners.add(scl);
    }
    
    /**
     * Remove a ServiceContextListener
     * @param scl
     */
    public void removeMessageContextListener(MessageContextListener scl) {
        messageContextListeners.remove(scl);
    }
    
    /**
     * @param cls Class of ServiceContextListener
     * @return true if ServiceContextLister is in the list
     */
    public boolean hasMessageContextListener(Class cls) {
        for (int i=0; i<messageContextListeners.size(); i++) {
            if (messageContextListeners.get(i).getClass() == cls) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Signal an Attach ServiceContext Event
     * @param sc ServiceContext
     * @param mc MessageContext
     */
    public void attachServiceContextEvent(ServiceContext sc, MessageContext mc) {
        for (int i=0; i<messageContextListeners.size(); i++) {
            messageContextListeners.get(i).attachServiceContextEvent(sc, mc);
        }
    }
    
    /**
     * Signal an Attach Envelope Event
     * @param mc MessageContext
     */
    public void attachEnvelopeEvent(MessageContext mc) {
        for (int i=0; i<messageContextListeners.size(); i++) {
            messageContextListeners.get(i).attachEnvelopeEvent(mc);
        }
    }

    /**
     * returns a axisService given a input Stream of WSDL2.0 document.
     * 
     * @param description
     *            WSDL description
     * @param wsdlServiceName
     * @param endPoint
     * @param options
     * @return
     * @throws AxisFault
     */
    public static AxisService createClientSideAxisService(Description description,
            QName wsdlServiceName, String endPoint, Options options) throws AxisFault {
        WSDL20ToAxisServiceBuilder serviceBuilder = new WSDL20ToAxisServiceBuilder(description,
                wsdlServiceName, endPoint);
        serviceBuilder.setServerSide(false);
        AxisService axisService = serviceBuilder.populateService();
        AxisEndpoint axisEndpoint = (AxisEndpoint) axisService.getEndpoint(axisService
                .getEndpointName());
        if (axisEndpoint != null) {
            options.setTo(new EndpointReference(axisEndpoint.getEndpointURL()));
            options.setSoapVersionURI((String) axisEndpoint.getBinding().getProperty(
                    WSDL2Constants.ATTR_WSOAP_VERSION));
        }
        return axisService;
    }    
    private void printDescriptionObject(Description definition,
			OutputStream out, String requestIP) {
		//TODO - complete this method
    	org.apache.woden.WSDLFactory fac;
		try {
			fac = org.apache.woden.WSDLFactory.newInstance();
			org.apache.woden.WSDLWriter writer = fac.newWSDLWriter();
	    	writer.writeWSDL(definition.toElement(), out);
		} catch (org.apache.woden.WSDLException e) {
			e.printStackTrace();
		}
    	
		
	}
    
    private WSDLSupplier getUserDefinedWSDLSupplier(String wsdlVersion){
    	WSDLSupplier wsdlSupplier = null;
    	if("wsdl".equals(wsdlVersion)){
    		Parameter para = getParameter(Constants.WSDL_11_SUPPLIER_CLASS_PARAM);
            if (para != null) {
                try {
                    wsdlSupplier = (WSDLSupplier) Class.forName((String) para.getValue()).newInstance();
                    if( wsdlSupplier instanceof WSDL11SupplierTemplate){
                        ((WSDL11SupplierTemplate)wsdlSupplier).init(this);                       
                    }
                } catch (Exception e) {
                    System.err.println("Following exception occurred when generating WSDL using "
                            + para);
                    e.printStackTrace();
                }
            }
    	} else if("wsdl2".equals(wsdlVersion)){
    		Parameter para = getParameter(Constants.WSDL_20_SUPPLIER_CLASS_PARAM);
    		if(para != null){    		
        		try {
					wsdlSupplier = (WSDLSupplier) Class.forName((String) para.getValue()).newInstance() ;
					if( wsdlSupplier instanceof WSDL20SupplierTemplate){
                        ((WSDL20SupplierTemplate)wsdlSupplier).init(this);                       
                    }
				} catch (Exception e) {	
					System.err.println("Following exception occurred when generating WSDL using "+ para );
					e.printStackTrace();
				} 	    			
    		}
    		
    	}
    	return wsdlSupplier;    	
    }
}
