# HttpCoreNIOListener.java

    /*
     *  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.synapse.transport.nhttp;

    import java.io.IOException;
    import java.io.InterruptedIOException;
    import java.net.InetSocketAddress;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Queue;
    import java.util.Set;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    import java.net.MalformedURLException;
    import java.net.URL;

    import org.apache.axiom.om.OMElement;
    import org.apache.axis2.AxisFault;
    import org.apache.axis2.addressing.EndpointReference;
    import org.apache.axis2.context.ConfigurationContext;
    import org.apache.axis2.context.MessageContext;
    import org.apache.axis2.context.SessionContext;
    import org.apache.axis2.description.AxisModule;
    import org.apache.axis2.description.AxisService;
    import org.apache.axis2.description.AxisServiceGroup;
    import org.apache.axis2.description.Parameter;
    import org.apache.axis2.description.TransportInDescription;
    import org.apache.axis2.engine.AxisConfiguration;
    import org.apache.axis2.engine.AxisEvent;
    import org.apache.axis2.engine.AxisObserver;
    import org.apache.axis2.transport.TransportListener;
    import org.apache.axis2.transport.base.BaseConstants;
    import org.apache.axis2.transport.base.BaseUtils;
    import org.apache.axis2.transport.base.ManagementSupport;
    import org.apache.axis2.transport.base.TransportMBeanSupport;
    import org.apache.axis2.transport.base.threads.NativeThreadFactory;
    import org.apache.axis2.util.JavaUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.http.HttpHost;
    import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
    import org.apache.http.impl.nio.reactor.IOReactorConfig;
    import org.apache.http.nio.reactor.IOEventDispatch;
    import org.apache.http.nio.reactor.IOReactorExceptionHandler;
    import org.apache.http.nio.reactor.ListenerEndpoint;
    import org.apache.http.params.BasicHttpParams;
    import org.apache.http.params.CoreConnectionPNames;
    import org.apache.http.params.CoreProtocolPNames;
    import org.apache.http.params.HttpParams;
    import org.apache.synapse.transport.http.conn.Scheme;
    import org.apache.synapse.transport.http.conn.ServerConnFactory;
    import org.apache.synapse.transport.nhttp.config.ServerConnFactoryBuilder;
    import org.apache.synapse.transport.nhttp.util.NhttpMetricsCollector;
    import org.apache.synapse.transport.nhttp.util.ActiveConnectionMonitor;

    /**
     * NIO transport listener for Axis2 based on HttpCore and NIO extensions
     */
    public class HttpCoreNIOListener implements TransportListener, ManagementSupport {

        private static final Log log = LogFactory.getLog(HttpCoreNIOListener.class);
        /** The Axis2 configuration context */
        private volatile ConfigurationContext cfgCtx;
        /** The IOReactor */
        private volatile DefaultListeningIOReactor ioReactor;
        /** The I/O dispatch */
        private volatile ServerIODispatch iodispatch;
        /** Protocol scheme used by this listener **/
        private volatile Scheme scheme;
        /** Connection factory used by this listener **/
        private volatile ServerConnFactory connFactory;
        /** The component name (to be used in logs)*/
        private volatile String name;
        /** HTTP parameters */
        private volatile HttpParams params;
        /** The ServerHandler */
        private volatile ServerHandler handler;
        /** JMX support */
        private volatile TransportMBeanSupport mbeanSupport;
        /** Listener configurations */
        private volatile ListenerContext listenerContext;
        /** Metrics */
        private volatile NhttpMetricsCollector metrics;
        /** state of the listener */
        private volatile int state = BaseConstants.STOPPED;
        /** Checks wheather WSDLEPRPrefix is set*/
        private boolean EPRPrefixCheck = true;

        /** The EPR prefix for services available over this transport */
        private volatile String serviceEPRPrefix;
        /** The EPR prefix for services with custom URI available over this transport */
        private volatile String customEPRPrefix;
        /** The custom URI map for the services if there are any */
        private final Map<String, String> serviceNameToEPRMap = new HashMap<String, String>();
        /** The servicename map for the custom URI if there are any */
        private Map<String, String> eprToServiceNameMap = new HashMap<String, String>();
        /** the axis observer that gets notified of service life cycle events*/
        private final AxisObserver axisObserver = new GenericAxisObserver();
        /** Active Connection Monitor Scheduler  */
        private final ScheduledExecutorService activeConnectionMonitorScheduler = Executors.newSingleThreadScheduledExecutor();
        /** Delay for ActiveConnectionMonitor */
        public static final long ACTIVE_CONNECTION_MONITOR_DELAY = 1000;

        protected Scheme initScheme() {
            return new Scheme("http", 80, false);
        }

        protected ServerConnFactoryBuilder initConnFactoryBuilder(
                final TransportInDescription transportIn, final HttpHost host) throws AxisFault {
            return new ServerConnFactoryBuilder(transportIn, host);
        }

        /**
         * Initialize the transport listener, and execute reactor in new separate thread
         * @param "cfgCtx" the Axis2 configuration context
         * @param transportIn the description of the http/s transport from Axis2 configuration
         * @throws AxisFault on error
         */
        public void init(ConfigurationContext ctx, TransportInDescription transportIn)
                throws AxisFault {

            cfgCtx = ctx;
            Map<String, String> o = (Map<String, String>) cfgCtx.getProperty(NhttpConstants.EPR_TO_SERVICE_NAME_MAP);
            if (o != null) {
                this.eprToServiceNameMap = o;
            } else {
                eprToServiceNameMap = new HashMap<String, String>();
                cfgCtx.setProperty(NhttpConstants.EPR_TO_SERVICE_NAME_MAP, eprToServiceNameMap);
            }

            NHttpConfiguration cfg = NHttpConfiguration.getInstance();

            // Initialize connection factory
            params = new BasicHttpParams();
            params
                    .setIntParameter(CoreConnectionPNames.SO_TIMEOUT,
                            cfg.getProperty(NhttpConstants.SO_TIMEOUT_RECEIVER, 60000))
                    .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
                            cfg.getProperty(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024))
                    .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "Synapse-HttpComponents-NIO");
    //                .setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET,
    //                        cfg.getStringValue(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, HTTP.DEFAULT_PROTOCOL_CHARSET)); //TODO:This does not works with HTTPCore 4.3

            name = transportIn.getName().toUpperCase(Locale.US) + " Listener";
            scheme = initScheme();

            // Setup listener context
            listenerContext = new ListenerContextBuilder(transportIn).parse().build();

            System.setProperty(transportIn.getName() + ".nio.port", String.valueOf(listenerContext.getPort()));

            // Setup connection factory
            HttpHost host = new HttpHost(
                listenerContext.getHostname(),
                listenerContext.getPort(),
                scheme.getName());
            connFactory = initConnFactoryBuilder(transportIn, host).build(params);

            // configure the IO reactor on the specified port
            try {
                String prefix = name + " I/O dispatcher";
                IOReactorConfig ioReactorConfig = new IOReactorConfig();
                ioReactorConfig.setIoThreadCount(
                        cfg.getServerIOWorkers());
                ioReactorConfig.setSoTimeout(
                        cfg.getProperty(NhttpConstants.SO_TIMEOUT_RECEIVER, 60000));
                ioReactorConfig.setTcpNoDelay(
                        cfg.getProperty(CoreConnectionPNames.TCP_NODELAY, 1) == 1);
                if (cfg.getBooleanValue("http.nio.interest-ops-queueing", false)) {
                    ioReactorConfig.setInterestOpQueued(true);
                }
                ioReactorConfig.setSoReuseAddress(cfg.getBooleanValue(CoreConnectionPNames.SO_REUSEADDR, false));

                ioReactor = new DefaultListeningIOReactor(
                        ioReactorConfig,
                        new NativeThreadFactory(new ThreadGroup(prefix + " thread group"), prefix));

                ioReactor.setExceptionHandler(new IOReactorExceptionHandler() {
                    public boolean handle(IOException ioException) {
                        log.warn("System may be unstable: IOReactor encountered a checked exception : "
                                + ioException.getMessage(), ioException);
                        return true;
                    }

                    public boolean handle(RuntimeException runtimeException) {
                        log.warn("System may be unstable: IOReactor encountered a runtime exception : "
                                + runtimeException.getMessage(), runtimeException);
                        return true;
                    }
                });
            } catch (IOException e) {
                handleException("Error creating IOReactor", e);
            }

            metrics = new NhttpMetricsCollector(true, transportIn.getName());

            handler = new ServerHandler(cfgCtx, scheme, listenerContext, metrics);
            iodispatch = new ServerIODispatch(handler, connFactory);

            Parameter param = transportIn.getParameter(NhttpConstants.WSDL_EPR_PREFIX);
            if (param != null) {
                serviceEPRPrefix = getServiceEPRPrefix(cfgCtx, (String) param.getValue());
                customEPRPrefix = (String) param.getValue();
                EPRPrefixCheck = false;
            } else {
                serviceEPRPrefix = getServiceEPRPrefix(cfgCtx, listenerContext.getHostname(), listenerContext.getPort());
                customEPRPrefix = scheme.getName() + "://" + listenerContext.getHostname() +
                        ":" + (listenerContext.getPort() == scheme.getDefaultPort() ? "" : listenerContext.getPort()) + "/";
            }

            // register to receive updates on services for lifetime management
            cfgCtx.getAxisConfiguration().addObservers(axisObserver);

            // register with JMX
            mbeanSupport = new TransportMBeanSupport(this, "nio-" + transportIn.getName());
            mbeanSupport.register();
        }

        public int getActiveConnectionsSize() {
            return handler.getActiveConnectionsSize();
        }

        /**
         * Return the EPR prefix for services made available over this transport
         * @param cfgCtx configuration context to retrieve the service context path
         * @param host name of the host
         * @param port listening port
         * @return wsdlEPRPrefix for the listener
         */
        protected String getServiceEPRPrefix(ConfigurationContext cfgCtx, String host, int port) {
            return scheme.getName() + "://" +
                    host + (port == scheme.getDefaultPort() ? "" : ":" + port) +
                    (!cfgCtx.getServiceContextPath().startsWith("/") ? "/" : "") +
                    cfgCtx.getServiceContextPath() +
                    (!cfgCtx.getServiceContextPath().endsWith("/") ? "/" : "");
        }

        /**
         * Return the EPR prefix for services made available over this transport
         * @param cfgCtx configuration context to retrieve the service context path
         * @param wsdlEPRPrefix specified wsdlPrefix
         * @return wsdlEPRPrefix for the listener
         */
        protected String getServiceEPRPrefix(ConfigurationContext cfgCtx, String wsdlEPRPrefix) {
            return wsdlEPRPrefix +
                    (!cfgCtx.getServiceContextPath().startsWith("/") ? "/" : "") +
                    cfgCtx.getServiceContextPath() +
                    (!cfgCtx.getServiceContextPath().endsWith("/") ? "/" : "");
        }

        /**
         * Start the transport listener. This method returns when the listener is ready to
         * accept connections.
         * @throws AxisFault
         */
        public void start() throws AxisFault {
            if (log.isDebugEnabled()) {
                log.debug("Starting Listener...");
            }

            for (Object obj : cfgCtx.getAxisConfiguration().getServices().values()) {
                addToServiceURIMap((AxisService) obj);
            }

            state = BaseConstants.STARTED;

            // start the IO reactor in a new separate thread
            final IOEventDispatch ioEventDispatch = iodispatch;
            Thread t = new Thread(new Runnable() {
                public void run() {
                    try {
                        ioReactor.execute(ioEventDispatch);
                    } catch (InterruptedIOException ex) {
                        log.fatal("Reactor Interrupted", ex);
                    } catch (IOException e) {
                        log.fatal("Encountered an I/O error: " + e.getMessage(), e);
                    } catch (Exception e) {
                        log.fatal("Unexpected exception in I/O reactor", e);
                    }
                    log.info(name + " Shutdown");
                }
            }, "HttpCoreNIOListener");

            t.start();

            listenerContext.getHttpGetRequestProcessor().init(cfgCtx, handler);
            startEndpoints();
        }

        private void startEndpoints() throws AxisFault {
            Queue<ListenerEndpoint> endpoints = new LinkedList<ListenerEndpoint>();

            Set<InetSocketAddress> addressSet = new HashSet<InetSocketAddress>();
            addressSet.addAll(connFactory.getBindAddresses());
            if (NHttpConfiguration.getInstance().getMaxActiveConnections() != -1) {
                addMaxConnectionCountController(NHttpConfiguration.getInstance().getMaxActiveConnections());
            }
            if (listenerContext.getBindAddress() != null) {
                addressSet.add(new InetSocketAddress(listenerContext.getBindAddress(), listenerContext.getPort()));
            }
            if (addressSet.isEmpty()) {
                addressSet.add(new InetSocketAddress(listenerContext.getPort()));
            }

            // Ensure simple but stable order
            List<InetSocketAddress> addressList = new ArrayList<InetSocketAddress>(addressSet);
            Collections.sort(addressList, new Comparator<InetSocketAddress>() {

                public int compare(InetSocketAddress a1, InetSocketAddress a2) {
                    String s1 = a1.toString();
                    String s2 = a2.toString();
                    return s1.compareTo(s2);
                }

            });
            for (InetSocketAddress address: addressList) {
                endpoints.add(ioReactor.listen(address));
            }

            // Wait for the endpoint to become ready, i.e. for the listener to start accepting
            // requests.
            while (!endpoints.isEmpty()) {
                ListenerEndpoint endpoint = endpoints.remove();
                try {
                    endpoint.waitFor();
                    if (log.isInfoEnabled()) {
                        InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
                        if (!address.isUnresolved()) {
                            log.info(name + " started on " + address.getHostName() + ":" + address.getPort());
                        } else {
                            log.info(name + " started on " + address);
                        }
                    }
                } catch (InterruptedException e) {
                    log.warn("Listener startup was interrupted");
                    break;
                }
            }
        }

        /**
         * Start specific end points given by InetSockeAddress list
         *
         * @param endpointsClosed InetSocketAddresses of endpoints to be started
         * @throws AxisFault
         */
        private void startSpecificEndpoints(List<InetSocketAddress> endpointsClosed) throws AxisFault {
            Queue<ListenerEndpoint> endpoints = new LinkedList<ListenerEndpoint>();

            // Ensure simple but stable order
            List<InetSocketAddress> addressList = endpointsClosed;
            Collections.sort(addressList, new Comparator<InetSocketAddress>() {

                public int compare(InetSocketAddress a1, InetSocketAddress a2) {
                    String s1 = a1.toString();
                    String s2 = a2.toString();
                    return s1.compareTo(s2);
                }

            });

            for (InetSocketAddress address : addressList) {
                endpoints.add(ioReactor.listen(address));
            }

            // Wait for the endpoint to become ready, i.e. for the listener to start accepting
            // requests.
            while (!endpoints.isEmpty()) {
                ListenerEndpoint endpoint = endpoints.remove();
                try {
                    endpoint.waitFor();
                    if (log.isInfoEnabled()) {
                        InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
                        if (!address.isUnresolved()) {
                            log.info(name + " started on " + address.getHostName() + ":" + address.getPort());
                        } else {
                            log.info(name + " started on " + address);
                        }
                    }
                } catch (InterruptedException e) {
                    log.warn("Listener startup was interrupted");
                    break;
                }
            }
        }

        private void addToServiceURIMap(AxisService service) {
            Parameter param = service.getParameter(NhttpConstants.SERVICE_URI_LOCATION);
            if (param != null) {
                String uriLocation = param.getValue().toString();
                if (uriLocation.startsWith("/")) {
                    uriLocation = uriLocation.substring(1);
                }
                serviceNameToEPRMap.put(service.getName(), uriLocation);
                eprToServiceNameMap.put(uriLocation, service.getName());
            }
        }

        private void removeServiceFfromURIMap(AxisService service) {
            eprToServiceNameMap.remove(serviceNameToEPRMap.get(service.getName()));
            serviceNameToEPRMap.remove(service.getName());
        }

        /**
         * Stop the listener
         * @throws AxisFault on error
         */
        public void stop() throws AxisFault {
            if (state == BaseConstants.STOPPED) return;
            try {
                int wait = NHttpConfiguration.getInstance().getListenerShutdownWaitTime();
                if (wait > 0) {
                    ioReactor.pause();
                    log.info("Waiting " + wait/1000 + " seconds to cleanup active connections...");
                    Thread.sleep(wait);
                    ioReactor.shutdown(wait);
                }  else {
                    ioReactor.shutdown();
                }
                handler.stop();
                state = BaseConstants.STOPPED;
                for (Object obj : cfgCtx.getAxisConfiguration().getServices().values()) {
                    removeServiceFfromURIMap((AxisService) obj);
                }
            } catch (IOException e) {
                handleException("Error shutting down IOReactor", e);
            } catch (InterruptedException e) {
                handleException("Error waiting for connection drain", e);
            }
        }

        /**
         * Pause the listener - Stops accepting new connections, but continues processing existing
         * connections until they complete. This helps bring an instance into a maintenence mode
         * @throws AxisFault
         */
        public void pause() throws AxisFault {
            if (state != BaseConstants.STARTED) return;
            try {
                ioReactor.pause();
                handler.markActiveConnectionsToBeClosed();
                state = BaseConstants.PAUSED;
                log.info(name + " Paused");
            } catch (IOException e) {
                handleException("Error pausing IOReactor", e);
            }
        }

        /**
         * Resume the lister - Brings the lister into active mode back from a paused state
         * @throws AxisFault
         */
        public void resume() throws AxisFault {
            if (state != BaseConstants.PAUSED) return;
            try {
                ioReactor.resume();
                state = BaseConstants.STARTED;
                log.info(name + " Resumed");
            } catch (IOException e) {
                handleException("Error resuming IOReactor", e);
            }
        }

        public void reload(final TransportInDescription transportIn) throws AxisFault {
            if (state != BaseConstants.STARTED) return;

            // Close all listener endpoints and stop accepting new connections
            Set<ListenerEndpoint> endpoints = ioReactor.getEndpoints();
            for (ListenerEndpoint endpoint: endpoints) {
                endpoint.close();
            }

            // Rebuild connection factory
            HttpHost host = new HttpHost(
                listenerContext.getHostname(),
                listenerContext.getPort(),
                scheme.getName());
            ServerConnFactoryBuilder connFactoryBuilder = initConnFactoryBuilder(transportIn, host);
            connFactory = connFactoryBuilder.build(params);
            iodispatch.update(connFactory);

            startEndpoints();

            log.info(name + " Reloaded");
        }

        /**
         * Restart specific endpoints which was updated by new configurations
         *
         * @param transportIn TransportInDescription of new configuration
         * @throws AxisFault
         */
        public void reloadSpecificEndpoints(final TransportInDescription transportIn) throws AxisFault {
            if (state != BaseConstants.STARTED) {
                return;
            }

            HttpHost host = new HttpHost(
                    listenerContext.getHostname(),
                    listenerContext.getPort(),
                    scheme.getName());

            // Rebuild connection factory
            ServerConnFactoryBuilder connFactoryBuilder = initConnFactoryBuilder(transportIn, host);
            connFactory = connFactoryBuilder.build(params);
            iodispatch.update(connFactory);

            List<InetSocketAddress> endPointsClosed = new ArrayList<InetSocketAddress>();

            //Close endpoints related to new profile's bind addresses
            Set<InetSocketAddress> endPointsToReload = connFactory.getBindAddresses();

            for (InetSocketAddress inetSocketAddress : endPointsToReload) {
                for (ListenerEndpoint listenerEndpoint : ioReactor.getEndpoints()) {
                    if (inetSocketAddress.getHostName().equalsIgnoreCase(((InetSocketAddress) listenerEndpoint.getAddress()).getHostName())) {
                        listenerEndpoint.close();
                        endPointsClosed.add((InetSocketAddress) listenerEndpoint.getAddress());
                    }
                }
            }

            //Start closed inpoints again with new configurations
            startSpecificEndpoints(endPointsClosed);

            log.info(name + " Reloaded");
        }

        /**
         * Returns the number of active threads processing messages
         * @return number of active threads processing messages
         */
        public int getActiveThreadCount() {
            return handler.getActiveCount();
        }

        /**
         * Returns the number of requestes queued in the thread pool
         * @return queue size
         */
        public int getQueueSize() {
            return handler.getQueueSize();
        }

        /**
         * Stop accepting new connections, and wait the maximum specified time for in-flight
         * requests to complete before a controlled shutdown for maintenence
         *
         * @param millis a number of milliseconds to wait until pending requests are allowed to complete
         * @throws AxisFault
         */
        public void maintenenceShutdown(long millis) throws AxisFault {
            if (state != BaseConstants.STARTED) return;
            try {
                long start = System.currentTimeMillis();
                ioReactor.pause();
                ioReactor.shutdown(millis);
                state = BaseConstants.STOPPED;
                log.info("Listener shutdown in : " + (System.currentTimeMillis() - start) / 1000 + "s");
            } catch (IOException e) {
                handleException("Error shutting down the IOReactor for maintenence", e);
            }
        }

        /**
         * Return the EPR for the given service (implements deprecated method temporarily)
         */
        public EndpointReference getEPRForService(String serviceName, String ip) throws AxisFault {

            EndpointReference ep = getEPRsForService(serviceName, ip)[0];
            return ep;
    //        String trailler = "";
    //        //Strip out the operation name
    //        if (serviceName.indexOf('/') != -1) {
    //            trailler += serviceName.substring(serviceName.indexOf("/"));
    //            serviceName = serviceName.substring(0, serviceName.indexOf('/'));
    //        }
    //        // strip out the endpoint name if present
    //        if (serviceName.indexOf('.') != -1) {
    //            trailler += serviceName.substring(serviceName.indexOf("."));
    //            serviceName = serviceName.substring(0, serviceName.indexOf('.'));
    //        }
    //
    //        if (serviceNameToEPRMap.containsKey(serviceName)) {
    //            return new EndpointReference(
    //                    customEPRPrefix + serviceNameToEPRMap.get(serviceName));
    //        } else {
    //            return new EndpointReference(serviceEPRPrefix + serviceName + trailler);
    //        }
        }

        /**
         * Return the EPRs for the given service over this transport
         * @param serviceName name of the service
         * @param ip IP address
         * @return the EndpointReferences for this service over the transport
         * @throws AxisFault on error
         */
        public EndpointReference[] getEPRsForService(String serviceName, String ip) throws AxisFault {

            String trailer = "";
            boolean isServiceWithCustomURI = isServiceWithCustomURI(serviceName);
            //Strip out the operation name
            if (serviceName.indexOf('/') != -1) {
                trailer += serviceName.substring(serviceName.indexOf("/"));
                serviceName = serviceName.substring(0, serviceName.indexOf('/'));
            }
            // strip out the endpoint name if present
            if (serviceName.indexOf('.') != -1 && !isServiceWithCustomURI) {
                trailer += serviceName.substring(serviceName.indexOf("."));
                serviceName = serviceName.substring(0, serviceName.indexOf('.'));
            }else if(isServiceWithCustomURI){
                serviceName = getServiceNameFromServiceWithCustomURI(serviceName);
            }

            EndpointReference[] endpointReferences = new EndpointReference[1];
            String service = serviceEPRPrefix;
            if (ip != null && !"".equals(ip) && EPRPrefixCheck) {
                service = replaceHostname(service, ip);
            }
            if (serviceNameToEPRMap.containsKey(serviceName)) {
                endpointReferences[0] = new EndpointReference(
                        customEPRPrefix + serviceNameToEPRMap.get(serviceName));
            } else {
                if (service == null || "".equals(service)) {
                    return null;
                }
                endpointReferences[0]
                        = new EndpointReference(service + serviceName + trailer);
            }
            return endpointReferences;
        }

        private boolean isServiceWithCustomURI(String serviceName){

            if(serviceNameToEPRMap.containsKey(serviceName)){
                return true;
            }
            //if map is not containing service name and there is no dot in service name
            //service is not have a custom URI
            if(!serviceName.contains(".")){
                return false;
            }
            serviceName = serviceName.substring(0,serviceName.lastIndexOf("."));
            return isServiceWithCustomURI(serviceName);
        }

        /** Only call this service IFF isServiceWithCustomURI(serviceName) is true
         * Else some nasty things can happen
         * @param serviceName
         * @return serviceName without endpoint part
         */
        private String getServiceNameFromServiceWithCustomURI(String serviceName){

            if(serviceNameToEPRMap.containsKey(serviceName)){
                return serviceName;
            }
            serviceName = serviceName.substring(0,serviceName.lastIndexOf("."));
            return getServiceNameFromServiceWithCustomURI(serviceName);
        }

        /**
         * Pause the IOReactor if the maxActive listener connection is
         * exceed the configured value.
         */
        private void addMaxConnectionCountController(int maxActiveConnections) {
            ActiveConnectionMonitor activeConnectionMonitor =
                    new ActiveConnectionMonitor(metrics, ioReactor, maxActiveConnections);
            activeConnectionMonitorScheduler.scheduleWithFixedDelay(
                    activeConnectionMonitor, 0, ACTIVE_CONNECTION_MONITOR_DELAY, TimeUnit.MILLISECONDS);
        }

        /**
         * TODO: Return session context from transport, this is an improvement in axis2 1.2 and
         * is not currently supported
         * @param messageContext context to be used
         * @return always null
         */
        public SessionContext getSessionContext(MessageContext messageContext) {
            return null;
        }

        public void destroy() {
            ioReactor = null;
            cfgCtx.getAxisConfiguration().getObserversList().remove(axisObserver);
            mbeanSupport.unregister();
            metrics.destroy();
        }

        /**
         * An AxisObserver which will start listening for newly deployed or started services,
         * and stop listening when services are undeployed or stopped.
         */
        class GenericAxisObserver implements AxisObserver {

            // The initilization code will go here
            public void init(AxisConfiguration axisConfig) {
            }

            public void serviceUpdate(AxisEvent event, AxisService service) {

                if (!ignoreService(service)
                        && BaseUtils.isUsingTransport(service, listenerContext.getTransportIn().getName())) {
                    switch (event.getEventType()) {
                        case AxisEvent.SERVICE_DEPLOY :
                            addToServiceURIMap(service);
                            break;
                        case AxisEvent.SERVICE_REMOVE :
                            removeServiceFfromURIMap(service);
                            break;
                        case AxisEvent.SERVICE_START  :
                            addToServiceURIMap(service);
                            break;
                        case AxisEvent.SERVICE_STOP   :
                            removeServiceFfromURIMap(service);
                            break;
                    }
                }
            }

            public void moduleUpdate(AxisEvent event, AxisModule module) {}
            public void addParameter(Parameter param) throws AxisFault {}
            public void removeParameter(Parameter param) throws AxisFault {}
            public void deserializeParameters(OMElement parameterElement) throws AxisFault {}
            public Parameter getParameter(String name) { return null; }
            public ArrayList<Parameter> getParameters() { return null; }
            public boolean isParameterLocked(String parameterName) { return false; }
            public void serviceGroupUpdate(AxisEvent event, AxisServiceGroup serviceGroup) {}
        }

        private boolean ignoreService(AxisService service) {
            // these are "private" services
            return service.getName().startsWith("__") || JavaUtils.isTrueExplicitly(
                    service.getParameter(NhttpConstants.HIDDEN_SERVICE_PARAM_NAME));
        }

        // -------------- utility methods -------------
        private void handleException(String msg, Exception e) throws AxisFault {
            log.error(msg, e);
            throw new AxisFault(msg, e);
        }

        // -- jmx/management methods--
        public long getMessagesReceived() {
            if (metrics != null) {
                return metrics.getMessagesReceived();
            }
            return -1;
        }

        public long getFaultsReceiving() {
            if (metrics != null) {
                return metrics.getFaultsReceiving();
            }
            return -1;
        }

        public long getBytesReceived() {
            if (metrics != null) {
                return metrics.getBytesReceived();
            }
            return -1;
        }

        public long getMessagesSent() {
            if (metrics != null) {
                return metrics.getMessagesSent();
            }
            return -1;
        }

        public long getFaultsSending() {
            if (metrics != null) {
                return metrics.getFaultsSending();
            }
            return -1;
        }

        public long getBytesSent() {
            if (metrics != null) {
                return metrics.getBytesSent();
            }
            return -1;
        }

        public long getTimeoutsReceiving() {
            if (metrics != null) {
                return metrics.getTimeoutsReceiving();
            }
            return -1;
        }

        public long getTimeoutsSending() {
            if (metrics != null) {
                return metrics.getTimeoutsSending();
            }
            return -1;
        }

        public long getMinSizeReceived() {
            if (metrics != null) {
                return metrics.getMinSizeReceived();
            }
            return -1;
        }

        public long getMaxSizeReceived() {
            if (metrics != null) {
                return metrics.getMaxSizeReceived();
            }
            return -1;
        }

        public double getAvgSizeReceived() {
            if (metrics != null) {
                return metrics.getAvgSizeReceived();
            }
            return -1;
        }

        public long getMinSizeSent() {
            if (metrics != null) {
                return metrics.getMinSizeSent();
            }
            return -1;
        }

        public long getMaxSizeSent() {
            if (metrics != null) {
                return metrics.getMaxSizeSent();
            }
            return -1;
        }

        public double getAvgSizeSent() {
            if (metrics != null) {
                return metrics.getAvgSizeSent();
            }
            return -1;
        }

        public Map getResponseCodeTable() {
            if (metrics != null) {
                return metrics.getResponseCodeTable();
            }
            return null;
        }

        public void resetStatistics() {
            if (metrics != null) {
                metrics.reset();
            }
        }

        public long getLastResetTime() {
            if (metrics != null) {
                return metrics.getLastResetTime();
            }
            return -1;
        }

        public long getMetricsWindow() {
            if (metrics != null) {
                return System.currentTimeMillis() - metrics.getLastResetTime();
            }
            return -1;
        }

        private String replaceHostname(String url, String hostName) {
            if (null == url) {
                return "";
            }
            try {
                URL newURL =  new URL(url);
                String replaceURL = "";
                // for default ports such as http->80, https->443
                if(-1 != newURL.getPort()){
                    replaceURL = newURL.getProtocol() + "://" + hostName+ ":" + newURL.getPort() + newURL.getPath();
                }else {
                    replaceURL = newURL.getProtocol() + "://" + hostName + newURL.getPath();
                }
                return replaceURL;
            } catch (MalformedURLException e) {
                log.warn("URL is not in the correct form" + e);
            }
            return "";
        }

        /**
         * Reload SSL profiles and reset connections
         *
         * @param transportInDescription TransportInDescription of the configuration
         * @throws AxisFault
         */
        public void reloadDynamicSSLConfig(TransportInDescription transportInDescription)
                throws AxisFault {
            log.info("HttpCoreNIOListener reloading SSL Config..");

            Parameter oldParameter = transportInDescription.getParameter("SSLProfiles");
            Parameter profilePathParam = transportInDescription.getParameter("dynamicSSLProfilesConfig");

            if (oldParameter != null && profilePathParam != null) {
                transportInDescription.removeParameter(oldParameter);
                this.reloadSpecificEndpoints(transportInDescription);
            }
        }

    }