# PassThroughHttpListener.java

    /**
     *  Copyright (c) 2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  Licensed 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.passthru;

    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.net.UnknownHostException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Set;

    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.threads.NativeThreadFactory;
    import org.apache.axis2.transport.base.threads.WorkerPool;
    import org.apache.axis2.transport.base.tracker.AxisServiceFilter;
    import org.apache.axis2.transport.base.tracker.AxisServiceTracker;
    import org.apache.axis2.transport.base.tracker.AxisServiceTrackerListener;
    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.nio.reactor.IOReactorException;
    import org.apache.http.nio.reactor.IOReactorExceptionHandler;
    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.passthru.config.PassThroughConfiguration;
    import org.apache.synapse.transport.passthru.config.SourceConfiguration;
    import org.apache.synapse.transport.passthru.core.PassThroughSharedListenerConfiguration;
    import org.apache.synapse.transport.passthru.core.PassThroughListeningIOReactorManager;

    import org.apache.synapse.transport.passthru.jmx.MBeanRegistrar;
    import org.apache.synapse.transport.passthru.jmx.PassThroughTransportMetricsCollector;
    import org.apache.synapse.transport.passthru.jmx.TransportView;

    import org.apache.synapse.transport.passthru.util.ActiveConnectionMonitor;

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    /**
     * This is the TransportListener listening for incoming connections. This class start the IOReactor
     * and registers the InRequest Handlers on it.
     */
    public class PassThroughHttpListener implements TransportListener {

        protected Log log = LogFactory.getLog(this.getClass());

        /** The reactor being used */
        private DefaultListeningIOReactor ioReactor;
        /**
         * IOReactor Manager
         */
        private PassThroughListeningIOReactorManager passThroughListeningIOReactorManager;
        /** The protocol handler */
        private SourceHandler handler;
        /** The connection factory */
        private ServerConnFactory connFactory;
        /** The protocol scheme of the listener*/
        private Scheme scheme;
        /** The configuration of the listener */
        private SourceConfiguration sourceConfiguration = null;

        /** The custom URI map for the services if there are any */
        private Map<String, String> serviceNameToEPRMap = new HashMap<String, String>();
        /** The service name 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();

        private volatile int state = BaseConstants.STOPPED;

        private String namePrefix;

        /** Active Connection Monitor Scheduler **/
        private final ScheduledExecutorService activeConnectionMonitorScheduler = Executors.newSingleThreadScheduledExecutor();

        /** Delay for ActiveConnectionMonitor **/
        public static final long ACTIVE_CONNECTION_MONITOR_DELAY = 1000;

        /** ServiceTracker to receive updates on services for lifetime management **/
        private AxisServiceTracker serviceTracker;

        private TransportInDescription pttInDescription;

        private ConfigurationContext configurationContext;

        /**
         * HttpListener Running port
         */
        private  int operatingPort;
        protected Scheme initScheme() {
            return new Scheme("http", 80, false);
        }

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

        public void init(ConfigurationContext cfgCtx, TransportInDescription transportInDescription)
                throws AxisFault {

            log.info("Initializing Pass-through HTTP/S Listener...");

            this.configurationContext = cfgCtx;
            pttInDescription = transportInDescription;
            namePrefix = transportInDescription.getName().toUpperCase(Locale.US);
            scheme = initScheme();

            int portOffset = Integer.parseInt(System.getProperty("portOffset", "0"));
            Parameter portParam = transportInDescription.getParameter("port");
            int port = Integer.parseInt(portParam.getValue().toString());
            operatingPort = port + portOffset;
            portParam.setValue(String.valueOf(operatingPort));
            portParam.getParameterElement().setText(String.valueOf(operatingPort));

            System.setProperty(transportInDescription.getName() + ".nio.port", String.valueOf(operatingPort));

            Object obj = cfgCtx.getProperty(PassThroughConstants.PASS_THROUGH_TRANSPORT_WORKER_POOL);
            WorkerPool workerPool = null;
            if (obj != null) {
                workerPool = (WorkerPool) obj;
            }

            PassThroughTransportMetricsCollector metrics = new PassThroughTransportMetricsCollector(
                    true, scheme.getName());

            TransportView view = new TransportView(this, null, metrics, null);
            MBeanRegistrar.getInstance().registerMBean(
                    view, "Transport",
                    "passthru-" + namePrefix.toLowerCase() + "-receiver");

            sourceConfiguration = new SourceConfiguration(cfgCtx, transportInDescription, scheme, workerPool, metrics);
            sourceConfiguration.build();

            HttpHost host = new HttpHost(
                    sourceConfiguration.getHostname(),
                    sourceConfiguration.getPort(),
                    sourceConfiguration.getScheme().getName());
            ServerConnFactoryBuilder connFactoryBuilder = initConnFactoryBuilder(transportInDescription, host, cfgCtx);
            connFactory = connFactoryBuilder.build(sourceConfiguration.getHttpParams());

            handler = new SourceHandler(sourceConfiguration);
            passThroughListeningIOReactorManager = PassThroughListeningIOReactorManager.getInstance();

            // register to receive updates on services for lifetime management
            //cfgCtx.getAxisConfiguration().addObservers(axisObserver);
            String prefix = namePrefix + "-Listener I/O dispatcher";
            try {
                ioReactor = (DefaultListeningIOReactor) passThroughListeningIOReactorManager.
                        initIOReactor(operatingPort,handler,
                                new PassThroughSharedListenerConfiguration(
                                        new NativeThreadFactory(new ThreadGroup(prefix + " thread group"), prefix),
                                        connFactory, sourceConfiguration));
            } catch (IOReactorException e) {
                handleException("Error initiating " + namePrefix + " ListeningIOReactor", e);
            }

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

            cfgCtx.setProperty(PassThroughConstants.PASS_THROUGH_TRANSPORT_WORKER_POOL,
                    sourceConfiguration.getWorkerPool());

            /* register to receive updates on services */
            serviceTracker = new AxisServiceTracker(
                    cfgCtx.getAxisConfiguration(),
                    new AxisServiceFilter() {
                        public boolean matches(AxisService service) {
                            return !service.getName().startsWith("__") // these are "private" services
                                    && BaseUtils.isUsingTransport(service, pttInDescription.getName());
                        }
                    },
                    new AxisServiceTrackerListener() {
                        public void serviceAdded(AxisService service) {
                            addToServiceURIMap(service);
                        }

                        public void serviceRemoved(AxisService service) {
                            removeServiceFfromURIMap(service);
                        }
                    });

        }

        public void start() throws AxisFault {
            serviceTracker.start();
            log.info("Starting Pass-through " + namePrefix + " Listener...");

                String prefix = namePrefix + "-Listener I/O dispatcher";

                passThroughListeningIOReactorManager.startIOReactor(ioReactor,
                        passThroughListeningIOReactorManager.getServerIODispatch(operatingPort), prefix);

                ioReactor.setExceptionHandler(new IOReactorExceptionHandler() {

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

                    public boolean handle(RuntimeException runtimeException) {
                        log.warn("System may be unstable: " + namePrefix +
                                " ListeningIOReactor encountered a runtime exception : "
                                + runtimeException.getMessage(), runtimeException);
                        return true;
                    }
                });
            if(sourceConfiguration.getHttpGetRequestProcessor() != null){
                sourceConfiguration.getHttpGetRequestProcessor().init(sourceConfiguration.getConfigurationContext(), handler);
            }

            startEndpoints();

            state = BaseConstants.STARTED;
        }

        private void startEndpoints() throws AxisFault {

            Set<InetSocketAddress> addressSet = new HashSet<InetSocketAddress>();
            addressSet.addAll(connFactory.getBindAddresses());

            InetAddress bindAddress;
            Parameter bindParam = pttInDescription.getParameter("bind-address");
            if (bindParam != null) {
                try {
                    bindAddress = InetAddress.getByName((String) bindParam.getValue());
                } catch (UnknownHostException ex) {
                    throw AxisFault.makeFault(ex);
                }
                if (bindAddress != null) {
                    Parameter portParam = pttInDescription.getParameter("port");
                    int port = Integer.parseInt(portParam.getValue().toString());
                    addressSet.add(new InetSocketAddress(bindAddress, port));
                }
            }

            if(PassThroughConfiguration.getInstance().getMaxActiveConnections() != -1) {
                addMaxActiveConnectionCountController(PassThroughConfiguration.getInstance().getMaxActiveConnections());
            }

            if (addressSet.isEmpty()) {
                addressSet.add(new InetSocketAddress(Integer.parseInt((String) pttInDescription.getParameter("port").getValue())));
            }

            // 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) {
                passThroughListeningIOReactorManager.startPTTEndpoint(address, ioReactor, namePrefix);
            }
        }

        /**
         * Start specified end points for given set of bind addresses
         *
         * @param bindAddresses InetSocketAddress list to be started
         * @throws AxisFault
         */
        private void startSpecificEndpoints(Set<InetSocketAddress> bindAddresses) throws AxisFault {

            if (PassThroughConfiguration.getInstance().getMaxActiveConnections() != -1) {
                addMaxActiveConnectionCountController(PassThroughConfiguration.getInstance().getMaxActiveConnections());
            }

            List<InetSocketAddress> addressList = new ArrayList<InetSocketAddress>(bindAddresses);
            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) {
                passThroughListeningIOReactorManager.startPTTEndpoint(address, ioReactor, namePrefix);
            }
        }

        private void handleException(String s, Exception e) throws AxisFault {
            log.error(s, e);
            throw new AxisFault(s, e);
        }

        /**
         *  Pauses the IOReactor if maxActive count exceeds the configured value.
         * @param maxActiveConnections
         */
        private void addMaxActiveConnectionCountController(int maxActiveConnections) {
            ActiveConnectionMonitor activeConnectionMonitor = new ActiveConnectionMonitor(sourceConfiguration.getMetrics(), ioReactor, maxActiveConnections);
            activeConnectionMonitorScheduler.scheduleWithFixedDelay(activeConnectionMonitor, 0, ACTIVE_CONNECTION_MONITOR_DELAY, TimeUnit.MILLISECONDS);
        }

        public EndpointReference getEPRForService(String serviceName, String ip) throws AxisFault {
            String trailer = "";
            //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) {
                trailer += serviceName.substring(serviceName.indexOf("."));
                serviceName = serviceName.substring(0, serviceName.indexOf('.'));
            }

            if (serviceNameToEPRMap.containsKey(serviceName)) {
                return new EndpointReference(
                        sourceConfiguration.getCustomEPRPrefix() +
                                serviceNameToEPRMap.get(serviceName) + trailer);
            } else {
                return new EndpointReference(sourceConfiguration.getServiceEPRPrefix() +
                        serviceName + trailer);
            }
        }

        /**
         * 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];
            if (serviceNameToEPRMap.containsKey(serviceName)) {
                endpointReferences[0] = new EndpointReference(
                        sourceConfiguration.getCustomEPRPrefix() +
                                serviceNameToEPRMap.get(serviceName) + trailer);
            } else {
                endpointReferences[0]
                        = new EndpointReference(sourceConfiguration.getServiceEPRPrefix() +
                        serviceName + trailer);
            }
            return endpointReferences;
        }

        public SessionContext getSessionContext(MessageContext messageContext) {
            return null;
        }

        public void stop() throws AxisFault {
            if (state == BaseConstants.STOPPED) return;
            log.info("Stopping Pass-through " + namePrefix + " Listener..");
            try {
                int wait = PassThroughConfiguration.getInstance().getListenerShutdownWaitTime();
                if (wait > 0) {
                    passThroughListeningIOReactorManager.pauseIOReactor(operatingPort);
                    log.info("Waiting " + wait/1000 + " seconds to cleanup active connections...");
                    Thread.sleep(wait);
                    passThroughListeningIOReactorManager.shutdownIOReactor(operatingPort, wait);
                } else {
                    passThroughListeningIOReactorManager.shutdownIOReactor(operatingPort);
                }
                serviceTracker.stop();
                handler.stop();
            } catch (IOException e) {
                handleException("Error shutting down " + namePrefix + " listening IO reactor", e);
            } catch (InterruptedException e) {
                handleException("Error waiting for connection drain", e);
            } finally {
                state = BaseConstants.STOPPED;
            }
        }

        public void destroy() {
            log.info("Destroying PassThroughHttpListener");
           /* sourceConfiguration.getConfigurationContext().
                    getAxisConfiguration().getObserversList().remove(axisObserver);*/
    //        serviceTracker.stop();
            sourceConfiguration.getMetrics().destroy();
        }

        /**
         * Pause the listener - Stops accepting new connections, but continues processing existing
         * connections until they complete. This helps bring an instance into a maintenance mode
         *
         * @throws AxisFault if pausing fails
         */
        public void pause() throws AxisFault {
            if (state != BaseConstants.STARTED) return;
            try {
               passThroughListeningIOReactorManager.pauseIOReactor(operatingPort);

                state = BaseConstants.PAUSED;
                log.info(namePrefix + " Listener 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 if the resume fails
         */
        public void resume() throws AxisFault {
            if (state != BaseConstants.PAUSED) return;
            try {
                passThroughListeningIOReactorManager.resumeIOReactor(operatingPort);
                state = BaseConstants.STARTED;
                log.info(namePrefix + " Listener 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
           passThroughListeningIOReactorManager.closeAllPTTListenerEndpoints(operatingPort);

            // Rebuild connection factory
            HttpHost host = new HttpHost(
                    sourceConfiguration.getHostname(),
                    sourceConfiguration.getPort(),
                    sourceConfiguration.getScheme().getName());
            ServerConnFactoryBuilder connFactoryBuilder = initConnFactoryBuilder(transportIn, host,
                    this.configurationContext);
            connFactory = connFactoryBuilder.build(sourceConfiguration.getHttpParams());
            passThroughListeningIOReactorManager.getServerIODispatch(operatingPort).update(connFactory);

            startEndpoints();

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

        /**
         * Re-load specific end points given in Transport In Description
         *
         * @param transportIn TransportInDescriptions of the new configuration
         * @throws AxisFault
         */
        public void reloadSpecificEndPoints(final TransportInDescription transportIn) throws AxisFault {
            if (state != BaseConstants.STARTED) {
                return;
            }

            HttpHost host = new HttpHost(
                    sourceConfiguration.getHostname(),
                    sourceConfiguration.getPort(),
                    sourceConfiguration.getScheme().getName());
            // Rebuild connection factory
            ServerConnFactoryBuilder connFactoryBuilder = initConnFactoryBuilder(transportIn, host,
                    this.configurationContext);
            connFactory = connFactoryBuilder.build(sourceConfiguration.getHttpParams());

            // Close listener endpoints and stop accepting new connections
            passThroughListeningIOReactorManager.closeSpecificPTTListenerEndpoints(operatingPort, connFactory.getBindAddresses());
            passThroughListeningIOReactorManager.getServerIODispatch(operatingPort).update(connFactory);

            //start end points from new configuration
            startSpecificEndpoints(connFactory.getBindAddresses());

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

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

        /**
         * An AxisObserver which will start listening for newly deployed or started services,
         * and stop listening when services are un-deployed or stopped.
         */
        private class GenericAxisObserver implements AxisObserver {
            public void init(AxisConfiguration axisConfig) {}

            public void serviceUpdate(AxisEvent event, AxisService service) {
                if (!ignoreService(service)
                        && BaseUtils.isUsingTransport(service,
                        sourceConfiguration.getInDescription().getName())) {
                    switch (event.getEventType()) {
                        case AxisEvent.SERVICE_DEPLOY :
                            addToServiceURIMap(service);
                            System.out.println("SERVICE_DEPLOY");
                            break;
                        case AxisEvent.SERVICE_REMOVE :
                            removeServiceFfromURIMap(service);
                            System.out.println("SERVICE_REMOVE");
                            break;
                        case AxisEvent.SERVICE_START  :
                            addToServiceURIMap(service);
                            System.out.println("SERVICE_START");
                            break;
                        case AxisEvent.SERVICE_STOP   :
                            removeServiceFfromURIMap(service);
                            System.out.println("SERVICE_STOP");
                            break;
                    }
                }
            }

            public void moduleUpdate(AxisEvent event, AxisModule module) {}
            public void addParameter(Parameter parameter) throws AxisFault {}
            public void removeParameter(Parameter parameter) 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(PassThroughConstants.HIDDEN_SERVICE_PARAM_NAME));
        }

        private void addToServiceURIMap(AxisService service) {
            Parameter param = service.getParameter(PassThroughConstants.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());
        }

        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);
        }

        public String getTransportName() {
            return pttInDescription.getName();
        }

        /**
         * Reload SSL configurations from configurations and reset all connections
         *
         * @param transportInDescription TransportInDescription of the configuration
         * @throws AxisFault
         */
        public void reloadDynamicSSLConfig(TransportInDescription transportInDescription)
                throws AxisFault {
            log.info("PassThroughHttpListener reloading SSL Config..");
            Parameter oldParameter = transportInDescription.getParameter("SSLProfiles");
            Parameter profilePathParam = transportInDescription.getParameter("dynamicSSLProfilesConfig");

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

    }