com.safi.asterisk.handler.connection.AbstractConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.safi.asterisk.handler.connection.AbstractConnectionManager.java

Source

package com.safi.asterisk.handler.connection;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.asteriskjava.fastagi.AgiChannel;
import org.asteriskjava.fastagi.AgiException;
import org.asteriskjava.fastagi.AgiRequest;
import org.asteriskjava.fastagi.AgiScript;
import org.asteriskjava.fastagi.AgiServerThread;
import org.asteriskjava.fastagi.BaseAgiScript;
import org.asteriskjava.fastagi.MappingStrategy;
import org.asteriskjava.manager.ManagerConnection;
import org.asteriskjava.manager.ManagerEventListener;
import org.asteriskjava.manager.SafiManagerConnection;
import org.asteriskjava.manager.SafiManagerConnectionFactory;
import org.asteriskjava.manager.action.PingAction;
import org.asteriskjava.manager.event.HangupEvent;
import org.asteriskjava.manager.event.ManagerEvent;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.springframework.jmx.export.notification.UnableToSendNotificationException;
import org.tanukisoftware.wrapper.WrapperManager;
import com.safi.asterisk.handler.SafiFastAgiServerFactory;
import com.safi.asterisk.handler.SafletEngine;
import com.safi.asterisk.handler.SafletEngineException;
import com.safi.asterisk.handler.dispatch.SafletDispatch;
import com.safi.asterisk.handler.mbean.SafiServerMonitorImpl;
import com.safi.asterisk.initiator.AsteriskInitiatorInfo;
import com.safi.core.saflet.Saflet;
import com.safi.core.saflet.SafletConstants;
import com.safi.core.saflet.SafletContext;
import com.safi.db.DBConnection;
import com.safi.db.DBDriver;
import com.safi.db.SafiDriverManager;
import com.safi.db.manager.DBManager;
import com.safi.db.manager.DBManagerException;
import com.safi.db.manager.PooledDataSourceManager;
import com.safi.db.server.config.AsteriskServer;
import com.safi.db.server.config.SafiServer;
import com.safi.server.SafiAgiServerThread;
import com.safi.server.SafiFastAgiServer;
import com.safi.server.SafiServerListener;

public abstract class AbstractConnectionManager implements AsteriskConnectionManager {

    protected final static Logger log = Logger.getLogger(AbstractConnectionManager.class);
    // protected final static Logger debugLog = HandlerEngine.debuggerLog;
    protected static final long LOOPBACK_TIMEOUT = 120000;
    protected static final long DEBUG_LOOPBACK_TIMEOUT = 120000; // 2 min for
                                                                 // debugging

    public final static String PATTERN_IP = "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b";
    private static final long DEFAULT_MANAGER_RETRY = 180;
    // public static final String SHARED_JS_FILE = "shared.js";

    protected SafiFastAgiServer agiServer;
    protected Map<String, Object> loopbackCallLock;
    protected Map<String, KeepAlive> channelKeepaliveMap;
    protected SafiManagerEventListener listener;
    protected SafletDispatch dispatcher;
    protected Executor keepAliveExecutor;

    protected Set<SafiServerListener> listeners = new HashSet<SafiServerListener>();
    protected Map<Integer, SafiManagerConnection> managerConnectionMap = new Hashtable<Integer, SafiManagerConnection>();
    protected SafiServer safiServerConfig;
    protected boolean blocking = true;
    protected boolean testing;
    private AgiServerThread agiServerThread;
    private SafiFastAgiServerFactory fastAgiServerFactory;

    private SafiFastAgiServerFactory factory;
    // private String currentHostname;
    private int currentFastAgiPort;
    private int currentManagementPort;
    private Timer managerConnectionPoller;
    private volatile boolean stopping;
    private SafiServer oldSafiServerConfig;
    private int currentDBPort;
    private int numAgiRequests;
    private int numCustomInitiations;
    private boolean usePing = true;
    private long managerRetryPeriod = DEFAULT_MANAGER_RETRY; //in seconds

    public AbstractConnectionManager() {

        loopbackCallLock = new Hashtable<String, Object>();
        channelKeepaliveMap = new Hashtable<String, KeepAlive>();
        listener = new SafiManagerEventListener();
        keepAliveExecutor = Executors.newCachedThreadPool();
        numAgiRequests = numCustomInitiations = 0;
    }

    @Override
    public void handle(AgiRequest request, AgiChannel channel) {
        numAgiRequests++;

    }

    @Override
    public void handle(String saflet, String astIp, Properties properties, ExceptionHandler exceptionHandler) {
        numCustomInitiations++;
        if (log.isDebugEnabled())
            log.debug("Invoking custom initiator for saflet " + saflet + " for asterisk " + astIp);

    }

    /*
     * (non-Javadoc)
     * 
     * 
     * @seecom.safi.asterisk.handler.AsteriskConnectionManager#addListener(com.safi
     * .server. SafiServerListener)
     */
    public boolean addListener(SafiServerListener listener) {
        return listeners.add(listener);
    }

    @Override
    public void notifyServerStarted() {
        // TODO Auto-generated method stub
        if (listeners != null)
            for (SafiServerListener l : listeners) {
                l.serverStarted();
            }
    }

    @Override
    public void notifyServerStopped(String msg) {
        // TODO Auto-generated method stub
        if (listeners != null)
            for (SafiServerListener l : listeners) {
                l.serverStopped(msg);
            }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#removeListener(com.
     * safi.server .SafiServerListener)
     */
    public boolean removeListener(SafiServerListener listener) {
        return listeners.remove(listener);
    }

    //
    // public void setBindIP(String ip) {
    // this.currentHostname = ip;
    // }

    public void setManagementPort(int port) {
        this.currentManagementPort = port;
    }

    public AsteriskServer getAsteriskServerById(Integer id, boolean fresh) {
        // safiServerChanged();
        if (safiServerConfig == null)
            return null;
        synchronized (safiServerConfig) {
            AsteriskServer old = null;
            for (AsteriskServer s : safiServerConfig.getAsteriskServers()) {
                if (id == s.getId()) {
                    old = s;
                    if (!fresh)
                        return s;
                }
            }
            try {
                AsteriskServer newserver = DBManager.getInstance().getAsteriskServerConfig(id);
                if (old != null) {
                    EcoreUtil.replace(old, newserver);
                }
                return newserver;
            } catch (DBManagerException e) {
                log.error("Couldn't find Asterisk Server " + id + " in DB", e);
            }
        }
        return null;
    }

    public AsteriskServer getAsteriskServerByIP(String ip) {
        // safiServerChanged();
        if (StringUtils.isBlank(ip))
            return null;
        if (safiServerConfig == null)
            return null;
        synchronized (safiServerConfig) {
            for (AsteriskServer s : safiServerConfig.getAsteriskServers()) {
                if (ip.equals(s.getHostname()))
                    return s;
            }
            try {
                return DBManager.getInstance().getAsteriskServerConfigByIp(ip);
            } catch (DBManagerException e) {
                log.error("Couldn't find Asterisk Server with IP " + ip + " in DB", e);
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#getServerIpAddress()
     */
    public String getServerIpAddress() {
        if (safiServerConfig != null) {
            return safiServerConfig.getBindIP();
        }
        // if (agiServer != null && ((SafiFastAgiServer) agiServer).isRunning())
        // return ((SafiFastAgiServer) agiServer).getBoundIpAddress();
        return null;
    }

    public void beginProcessing() throws SafletEngineException {
        try {
            if (managerConnectionPoller == null) {
                createAgiServer();
                managerConnectionPoller = new Timer(true);
                setupManagerDaemon();
            }
            // if (debugLog.isDebugEnabled()) {
            // debugLog.debug("Safi Server starting...");
            // }
            // createManagerConnection();
            // createAgiServer();
        } catch (Exception e) {
            if (e instanceof SafletEngineException)
                throw (SafletEngineException) e;

            throw new SafletEngineException(e);
        }
    }

    private void shutdownAGIServer() {
        if (agiServerThread != null) {
            try {
                agiServerThread.shutdown();
                // agiServer.shutdown();
                log.info("FastAGI server was stopped");
            } catch (Exception e) {
                log.error("error caught during shutdown", e);
            }
            agiServerThread = null;
            agiServer = null;
        } else if (agiServer != null)
            agiServer.shutdown();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.safi.asterisk.handler.AsteriskConnectionManager#stopProcessing()
     */
    public synchronized void stopProcessing() {
        stopping = true;
        try {
            shutdownAGIServer();
            for (SafiManagerConnection managerConnection : managerConnectionMap.values()) {
                try {
                    managerConnection.removeEventListener(listener);
                    managerConnection.logoff();
                    log.info("Asterisk Manager connection was closed");
                } catch (Exception e) {
                    log.error("error caught during shutdown", e);
                }
            }
            managerConnectionMap.clear();

        } finally {
            stopping = false;
        }
    }

    @Override
    public SafiManagerConnection getManagerConnection(Integer key) {
        return managerConnectionMap.get(key);
    }

    protected void applyParameters(AgiRequest request, Saflet copy) {
        final Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> pmap = new HashMap<String, Object>();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String[] val = entry.getValue();
            if (val == null || val.length == 0)
                pmap.put(entry.getKey(), null);
            else if (val.length == 1)
                pmap.put(entry.getKey(), val[0]);
            else
                pmap.put(entry.getKey(), val);
        }
        applyParametersCustom(pmap, copy);
    }

    protected void applyParametersCustom(Map params, Saflet copy) {
        // List<Variable> variables = copy.getHandlerContext().getVariables();
        SafletContext context = copy.getSafletContext();
        context.setVariableRawValue(SafletConstants.VAR_KEY_PROPS, params);
    }

    protected boolean createManagerConnection(AsteriskServer server) throws Exception {
        synchronized (server) {
            if (testing || !server.isActive())
                return false;

            SafiManagerConnectionFactory factory = new SafiManagerConnectionFactory(server.getHostname(),
                    server.getManagerPort(), server.getManagerName(), server.getManagerPassword());
            SafiManagerConnection managerConnection = (SafiManagerConnection) factory.createManagerConnection();
            for (SafiServerListener listener : listeners)
                managerConnection.addListener(listener);
            managerConnection.addListener(new SafiServerListenerInternal(server.getName(), server.getHostname()));
            managerConnection.login();
            managerConnection.addEventListener(listener);
            managerConnectionMap.put(server.getId(), managerConnection);
            return true;
        }
    }

    protected void createAgiServer() throws IOException {
        // agiServer = blocking ? new SafiAgiServer() : new SafiMinaServer();
        if (fastAgiServerFactory == null)
            throw new IllegalStateException("No FastAgi server factory available");
        agiServer = fastAgiServerFactory.createAgiServer();
        agiServer.setPort(currentFastAgiPort);
        // ((SafiFastAgiServer) agiServer).setPoolSize(fastAgiPoolsize);
        // ((SafiFastAgiServer) agiServer).setMaximumPoolSize(fastAgiMaxPoolsize);
        agiServer.setPool(SafletEngine.getInstance().getThreadPool());
        agiServer.setMappingStrategy(new HandlerMappingStrategy());
        agiServer.setBindAddr("0.0.0.0");
        // String bindIP = safiServerConfig.getBindIP();
        // String bindIP = SafletEngine.getInstance().getBindIP();
        // if (bindIP != null) {
        // if (Pattern.matches(PATTERN_IP, bindIP))
        // agiServer.setBindAddr(bindIP);
        // else {
        // try {
        //
        // agiServer.setBindAddr(InetAddress.getByName(bindIP).getHostAddress());
        // } catch (UnknownHostException e) {
        // log.warn("Couldn't connect to host " + bindIP + ".  Defaulting...", e);
        // agiServer.setBindAddr(InetAddress.getLocalHost().getHostAddress());
        //
        // }
        // }
        // }

        for (SafiServerListener ls : listeners) {
            agiServer.addListener(ls);
        }
        /*
         * if (!HandlerEngine.getInstance().isServerMode()) agiServer.startup();
         * else
         */ {
            agiServerThread = new SafiAgiServerThread(agiServer, new AgiUncaughtExceptionHandler());
            agiServerThread.setDaemon(true);
            agiServerThread.startup();
        }
        // agiServer.startup();
    }

    protected void handleLoopback(AgiRequest request, AgiChannel channel) {
        try {
            String uuid = channel.getVariable("SafiUUID");
            if (uuid == null)
                return;
            Object obj = null;
            KeepAlive al = null;
            synchronized (loopbackCallLock) {
                obj = loopbackCallLock.get(uuid);
                if (obj == null) {
                    log.error("No lock found for channel " + channel.getName() + " with uuid " + uuid);
                    if (SafletEngine.debuggerLog.isEnabledFor(Level.ERROR))
                        SafletEngine.debuggerLog
                                .error("No lock found for channel " + channel.getName() + " with uuid " + uuid);
                    return;
                }
                al = new KeepAlive(channel);
                // keepAliveExecutor.execute(al);
                KeepAlive al2 = channelKeepaliveMap.put(channel.getUniqueId(), al);
                if (al2 != null)
                    al2.stop();
                if (obj != null)
                    loopbackCallLock.put(uuid, new Object[] { channel, request });
            }
            if (obj != null) {
                // loopbackCallLock.put(uuid, new Object[] { channel, request });
                synchronized (obj) {
                    obj.notifyAll();
                }

                if (al != null)
                    al.run();
            }
        } catch (Exception e) {
            log.error("Error caught during handleLoopback", e);
            if (SafletEngine.debuggerLog.isEnabledFor(Level.ERROR))
                SafletEngine.debuggerLog.error("Error caught during handleLoopback", e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#setLoopbackLock(java
     * .lang.String)
     */
    public Object setLoopbackLock(String uuid, Object lock) {
        if (lock == null)
            lock = new Object();
        synchronized (loopbackCallLock) {
            if (loopbackCallLock.containsKey(uuid)) {
                log.error("Lock already exists uuid " + uuid);
                if (SafletEngine.debuggerLog.isEnabledFor(Level.ERROR))
                    SafletEngine.debuggerLog.error("Lock already exists uuid " + uuid);
                return null;
            }
            if (SafletEngine.debuggerLog.isDebugEnabled())
                SafletEngine.debuggerLog.error("Setting lock for uuid " + uuid);
            loopbackCallLock.put(uuid, lock);
        }
        return lock;
    }

    // TODO: strong this up
    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#getLoopbackCall(java
     * .lang.String)
     */
    public Object getLoopbackCall(String uuid) {
        Object lock = null;
        synchronized (loopbackCallLock) {
            lock = loopbackCallLock.get(uuid);
            if (lock instanceof Object[]) {
                if (SafletEngine.debuggerLog.isDebugEnabled())
                    SafletEngine.debuggerLog.error("Got my loopbacklock info for " + uuid);
                return (Object[]) loopbackCallLock.remove(uuid);
            }
        }
        if (lock == null) {
            if (SafletEngine.debuggerLog.isDebugEnabled())
                SafletEngine.debuggerLog.error("Loopbacklock info for " + uuid + " was null!");
            return null;
        }
        synchronized (lock) {
            try {
                if (SafletEngine.debuggerLog.isDebugEnabled())
                    SafletEngine.debuggerLog.error("Loopback hasn't arrived for " + uuid + " so i'm waiting!");
                if (lock instanceof Long)
                    lock.wait(((Long) lock).longValue());
                else
                    lock.wait(LOOPBACK_TIMEOUT);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (SafletEngine.debuggerLog.isDebugEnabled())
            SafletEngine.debuggerLog.error("I've woken up for " + uuid + " so returning");
        return loopbackCallLock.remove(uuid);
    }

    @Override
    public void initManagerConnections() throws SafletEngineException {
        if (safiServerConfig == null)
            return;
        synchronized (safiServerConfig) {
            Runnable runna = new Runnable() {
                @Override
                public void run() {

                    AsteriskServer[] astServers = new AsteriskServer[safiServerConfig.getAsteriskServers().size()];
                    safiServerConfig.getAsteriskServers().toArray(astServers);
                    for (int i = 0; i < astServers.length; i++) {
                        AsteriskServer astServer = astServers[i];
                        if (!astServer.isActive())
                            continue;
                        if (managerConnectionMap.containsKey(astServer.getId())) {
                            log.warn("Asterisk server with id " + astServer.getId() + " already exists");
                            postInfoMessage("Warning!",
                                    "Asterisk server with id " + astServer.getId() + " already exists");
                            continue;
                        }
                        if (StringUtils.isNotBlank(astServer.getHostname())
                                && StringUtils.isNotBlank(astServer.getManagerName()))
                            try {
                                createManagerConnection(astServer);
                            } catch (Exception e) {
                                log.error("Couldn't create manager connection for AsteriskServer "
                                        + astServer.getName() + " at " + astServer.getHostname(), e);

                                postErrorMessage("License Error",
                                        "Couldn't create manager connection for AsteriskServer "
                                                + astServer.getName() + " at " + astServer.getHostname());
                            }
                    }
                }
            };

            SafletEngine.getInstance().getThreadPool().execute(runna);

        }
    }

    @Override
    public void initDBResources() throws DBManagerException {
        SafiDriverManager mgr = DBManager.getInstance().getDriverManager();
        if (mgr == null)
            return;
        synchronized (mgr) {
            for (DBDriver drv : mgr.getDrivers())
                for (DBConnection conn : drv.getConnections())
                    PooledDataSourceManager.getInstance().connectionChanged(conn);
        }
    }

    @Override
    public void initSafiServer() {
        safiServerChanged(true);

    }

    protected synchronized void updateSafiServer(SafiServer newConfig) throws DBManagerException {

        boolean isDirty = false;

        if (!StringUtils.equalsIgnoreCase(SafletEngine.getInstance().getBindIP(), newConfig.getBindIP())) {
            newConfig.setBindIP(SafletEngine.getInstance().getBindIP());
            isDirty = true;
        }
        if (newConfig.getPort() != SafletEngine.getInstance().getFastagiPort()) {
            isDirty = true;
            newConfig.setPort(SafletEngine.getInstance().getFastagiPort());
        }
        if (newConfig.getManagementPort() != SafletEngine.getInstance().getManagementPort()) {
            isDirty = true;
            newConfig.setManagementPort(SafletEngine.getInstance().getManagementPort());
        }

        if (newConfig.getDbPort() != SafletEngine.getInstance().getDatabasePort()) {
            isDirty = true;
            newConfig.setDbPort(SafletEngine.getInstance().getDatabasePort());
        }

        if (isDirty) {
            DBManager.getInstance().saveOrUpdateServerResource(newConfig);
        }
        this.oldSafiServerConfig = this.safiServerConfig;
        this.safiServerConfig = newConfig;
    }

    @Override
    public synchronized void safiServerChanged(boolean startup) {
        // SafiServer oldConfig = safiServerConfig;
        boolean shouldReloadAgi = true;
        SafiServer newConfig = null;

        try {
            newConfig = DBManager.getInstance()
                    .getSafiServerConfig(safiServerConfig != null ? safiServerConfig.getId() : -1);
            // if (newConfig == safiServerConfig)
            // return;

        } catch (DBManagerException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
            log.error("Couldn't create FastAgi connection", e1);
            postErrorMessage("FastAGI Error", "Couldn't create FastAgi connection: " + e1.getLocalizedMessage());
            return;
        }

        if (newConfig != null && startup) {
            try {
                updateSafiServer(newConfig);
            } catch (Exception e) {
                log.error("Fatal SafiServer initialization error", e);
                WrapperManager.stop(1);
                return;
            }
        }

        if (startup
                || (newConfig != null && safiServerConfig != null && currentFastAgiPort == newConfig.getPort())) {
            shouldReloadAgi = false;
        } else
            SafletEngine.getInstance().setFastagiPort(newConfig.getPort());
        int oldMgrPort = currentManagementPort;
        // String oldIp = currentHostname;
        int oldDBPort = currentDBPort;
        setSafiServerConfig(newConfig);

        // restart RMI
        if (!startup && newConfig != null && (oldMgrPort != newConfig.getManagementPort())) {
            // should restart MBean server
            try {

                SafletEngine.getInstance().setManagementPort(newConfig.getManagementPort());
                SafletEngine.getInstance().initJMXServer(newConfig.getManagementPort());
            } catch (Exception e) {
                e.printStackTrace();
                // log.error("Couldn't init JMX server on new settings...rolling back",
                // e);
                // if (oldSafiServerConfig != safiServerConfig)
                // rollbackServerSettings(oldSafiServerConfig);
                // else
                WrapperManager.restartAndReturn();
                return;
            }
        }

        // restart DB
        if (!startup && newConfig != null && DBManager.getInstance().getPort() != newConfig.getDbPort()) {
            try {
                SafletEngine.getInstance().setDatabasePort(newConfig.getDbPort());
                SafletEngine.getInstance().restartDB(newConfig.getDbPort());
            } catch (DBManagerException e) {
                log.error("Critical Error. Restarting...", e);
                postErrorMessage("Critical Error",
                        "Couldn't restart SafiServer database: " + e.getLocalizedMessage());
                // if (oldSafiServerConfig != safiServerConfig)
                // rollbackServerSettings(oldSafiServerConfig);
                // else
                WrapperManager.restartAndReturn();
                return;
            }
        }

        // restart AGI
        if (shouldReloadAgi) {
            try {
                // SafletEngine.getInstance().setBindIP(newConfig.getBindIP());
                // HandlerEngine.getInstance().setDatabasePort(newConfig.getDbPort());

                shutdownAGIServer();
                if (newConfig != null) {
                    createAgiServer();
                    postInfoMessage("FastAGI Server", "FastAGI server was reloaded");
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("Couldn't create FastAgi server from config " + newConfig, e);

                try {
                    shutdownAGIServer();
                } catch (Exception ignore) {
                }
                postErrorMessage("FastAGI Error", "Couldn't create FastAgi connection: " + e.getLocalizedMessage());

                // if (oldSafiServerConfig != safiServerConfig)
                // rollbackServerSettings(oldSafiServerConfig);
                // else
                WrapperManager.restartAndReturn();
            }
        }

    }

    private void postInfoMessage(String title, String message) {
        if (SafletEngine.getInstance().getServerMonitor() == null) {
            log.info(title + " -- " + message);
            return;
        }
        try {
            SafletEngine.getInstance().getServerMonitor().postInfo(title, message);
        } catch (UnableToSendNotificationException e1) {
            e1.printStackTrace();
            log.error("Unable to send info notification via JMX", e1);
        }
    }

    private void postErrorMessage(String title, String message) {
        if (SafletEngine.getInstance().getServerMonitor() == null) {
            log.error(title + " -- " + message);
            return;
        }
        try {
            SafletEngine.getInstance().getServerMonitor().postError(title, message);
        } catch (UnableToSendNotificationException e1) {
            e1.printStackTrace();
            log.error("Unable to send info notification via JMX", e1);
        }

    }

    /*
     * ReentrantLock rollbackLock = new ReentrantLock();
     * 
     * private void rollbackServerSettings(SafiServer oldConfig) { if
     * (!rollbackLock.tryLock()) {
     * 
     * if (log.isDebugEnabled())
     * log.debug("Someone is rolling back and there are " +
     * rollbackLock.getQueueLength() + " in queue. I'm returning..."); return; }
     * // rollbackLock.lock(); log.debug("Lock is held by " +
     * Thread.currentThread().getName()); try { if (log.isDebugEnabled()) {
     * log.debug("Rolling back current config " + safiServerConfig + " to " +
     * oldConfig); } SafiServer newConfig =
     * DBManager.getInstance().getSafiServerConfig(oldConfig.getId());
     * newConfig.setBindIP(oldConfig.getBindIP());
     * newConfig.setDbPort(oldConfig.getDbPort());
     * newConfig.setManagementPort(oldConfig.getManagementPort());
     * newConfig.setPort(oldConfig.getPort());
     * 
     * safiServerConfig = oldSafiServerConfig = newConfig;
     * DBManager.getInstance().saveOrUpdateServerResource(newConfig); } catch
     * (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
     * finally { rollbackLock.unlock(); log.debug("Lock is released by " +
     * Thread.currentThread().getName()); } }
     */
    @Override
    public synchronized void asteriskServerAdded(AsteriskServer astServer) {
        if (managerConnectionMap.containsKey(astServer.getId())) {
            log.warn("Asterisk server with id " + astServer.getId() + " already exists");
            return;
        }
        try {

            if (createManagerConnection(astServer))
                this.postInfoMessage("AsteriskServer added and connection created",
                        astServer.getName() + " with IP " + astServer.getHostname());
        } catch (Exception e) {
            log.error("Couldn't create manager connection for AsteriskServer " + astServer.getName() + " at "
                    + astServer.getHostname(), e);
            postErrorMessage("Add Asterisk Server Error", "Couldn't create manager connection for AsteriskServer "
                    + astServer.getName() + " at " + astServer.getHostname() + ": " + e.getLocalizedMessage());

        }

    }

    @Override
    public synchronized void asteriskServerModified(Integer id) {
        AsteriskServer s = getAsteriskServerById(id, true);
        if (s == null) {
            log.error("Couldn't find existing Asterisk server with ID " + id);
            return;
        }
        SafiManagerConnection conn = managerConnectionMap.get(id);
        boolean connChanged = true;
        if (conn != null) {
            connChanged = false;
            if (!StringUtils.equals(s.getHostname(), conn.getHostname()) || s.getManagerPort() != conn.getPort()
                    || !StringUtils.equals(s.getManagerName(), conn.getUsername())
                    || !StringUtils.equals(s.getManagerPassword(), conn.getPassword()))
                connChanged = true;

        }
        if (connChanged) {
            try {
                removeSafiManagerConnection(id);
            } catch (Exception e) {
                log.warn("Error caught while removing manager connection for Asterisk server " + s.getName(), e);
            }
            try {
                createManagerConnection(s);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("Couldn't create manager connection for AsteriskServer " + s.getName() + " at "
                        + s.getHostname(), e);
                postErrorMessage("Modify Asterisk Server Error", "Asterisk server " + s.getName() + " with IP "
                        + s.getHostname() + " could not be connected: " + e.getLocalizedMessage());

            }
        }
    }

    @Override
    public synchronized void asteriskServerRemoved(Integer id) {
        if (removeSafiManagerConnection(id)) {
            try {
                SafletEngine.getInstance().getServerMonitor().postInfo("Asterisk Server Removed",
                        "An active AsteriskServer with id " + id + " was shutdown and removed");
            } catch (UnableToSendNotificationException e1) {
                e1.printStackTrace();
                log.error("Unable to send info notification via JMX", e1);
            }
        }
    }

    protected boolean removeSafiManagerConnection(Integer id) {
        SafiManagerConnection managerConnection = null;
        if ((managerConnection = managerConnectionMap.remove(id)) != null) {
            managerConnection.removeEventListener(listener);
            managerConnection.logoff();
            return true;
        }
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.safi.asterisk.handler.AsteriskConnectionManager#getDispatcher()
     */
    public SafletDispatch getDispatcher() {
        return dispatcher;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#setDispatcher(com.safi
     * .asterisk .handler.SafiApplicationDispatch)
     */
    public void setDispatcher(SafletDispatch dispatcher) {
        this.dispatcher = dispatcher;
    }

    /*
     * (non-Javadoc)
     * 
     * @seecom.safi.asterisk.handler.AsteriskConnectionManager#setAgiServer(org.
     * asteriskjava .fastagi.AgiServer)
     */
    private void setAgiServer(SafiFastAgiServer agiServer) {
        this.agiServer = agiServer;
    }

    /*
     * / (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#setUseBlocking(boolean)
     */
    public void setUseBlocking(boolean blocking) {
        this.blocking = blocking;

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.safi.asterisk.handler.AsteriskConnectionManager#setTesting(boolean)
     */
    public void setTesting(boolean b) {
        this.testing = b;

    }

    @Override
    public SafiServer getSafiServerConfig() {
        return safiServerConfig;
    }

    @Override
    public void setSafiServerConfig(SafiServer safiServerConfig) {
        this.oldSafiServerConfig = this.safiServerConfig;
        this.safiServerConfig = safiServerConfig;
        if (safiServerConfig == null) {
            currentManagementPort = -1;
            currentFastAgiPort = -1;
            currentDBPort = -1;
            // currentHostname = null;
        } else {
            currentManagementPort = safiServerConfig.getManagementPort();
            currentFastAgiPort = safiServerConfig.getPort();
            // currentHostname = safiServerConfig.getBindIP();
            currentDBPort = safiServerConfig.getDbPort();
        }
    }

    class HandlerMappingStrategy implements MappingStrategy {

        public AgiScript determineScript(AgiRequest request) {
            return new HandlerAgiScript();
        }

    }

    class SafiManagerEventListener implements ManagerEventListener {
        public void onManagerEvent(ManagerEvent event) {
            // if (HandlerEngine.debuggerLog.isDebugEnabled())
            // HandlerEngine.debuggerLog.debug("There was an event of some import: "+event);
            if (event instanceof HangupEvent) {
                String uid = ((HangupEvent) event).getUniqueId();
                synchronized (channelKeepaliveMap) {
                    KeepAlive ka = channelKeepaliveMap.remove(uid);
                    if (ka != null) {
                        if (SafletEngine.debuggerLog.isDebugEnabled())
                            SafletEngine.debuggerLog.debug("Loopback channel hangup event detected for chan "
                                    + ((HangupEvent) event).getChannel());
                        ka.stop();
                    }
                }
            }
        }

    }

    class KeepAlive implements Runnable {
        AgiChannel channel;
        Object lock = new Object();

        KeepAlive(AgiChannel channel) {
            this.channel = channel;
        }

        @Override
        public void run() {
            int status = -1;
            try {
                while ((status = channel.getChannelStatus()) != 1 && status != 0) {
                    synchronized (lock) {
                        try {
                            lock.wait(1000 * 60); // wait for a minute
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (AgiException e) {
            }
            if (SafletEngine.debuggerLog.isDebugEnabled())
                SafletEngine.debuggerLog.debug("KeepAlive for channel " + channel.getName() + " stopped...");
        }

        public void stop() {
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }

    class HandlerAgiScript extends BaseAgiScript {

        public HandlerAgiScript() {
        }

        public void service(AgiRequest request, AgiChannel channel) throws AgiException {
            handle(request, channel);
        }

    }

    public void setFactory(SafiFastAgiServerFactory factory) {
        this.factory = factory;
    }

    public SafiFastAgiServerFactory getAgiServerFactory() {
        return fastAgiServerFactory;
    }

    public void setAgiServerFactory(SafiFastAgiServerFactory fastAgiServerFactory) {
        this.fastAgiServerFactory = fastAgiServerFactory;
    }

    private void setupManagerDaemon() {
        if (managerRetryPeriod > 0)
            managerConnectionPoller.schedule(new ManagerDaemon(), 10000, managerRetryPeriod * 1000);

    }

    private class ManagerDaemon extends TimerTask {

        @Override
        public void run() {
            // safiServerChanged();
            synchronized (safiServerConfig) {
                AsteriskServer[] astServers = new AsteriskServer[safiServerConfig.getAsteriskServers().size()];
                safiServerConfig.getAsteriskServers().toArray(astServers);
                int j = 0;
                for (int i = 0; i < astServers.length; i++) {
                    AsteriskServer astServer = astServers[i];
                    if (!astServer.isActive())
                        continue;

                    SafiManagerConnection conn = managerConnectionMap.get(astServer.getId());
                    if (conn == null) {
                        try {
                            if (createManagerConnection(astServer))
                                postInfoMessage("Manager Connection created",
                                        astServer.getName() + " with IP " + astServer.getHostname());
                        } catch (Exception e) {
                            e.printStackTrace();
                            log.error("Couldn't create manager connection for server " + astServer.getId(), e);
                        }
                    } else {
                        if (usePing) {
                            try {
                                org.asteriskjava.manager.response.ManagerResponse res = conn
                                        .sendAction(new PingAction());
                            } catch (Exception e) {
                                log.warn("Ping failed to manager connection, renewing connection.", e);
                                handleLostConnection(astServer);

                            }
                        } else {
                            if (!conn.isConnected()) {
                                log.warn("Manager connection not connected");
                                handleLostConnection(astServer);
                            }
                        }
                    }
                    j++;

                }
            }
        }

        private void handleLostConnection(AsteriskServer astServer) {
            managerConnectionMap.remove(astServer.getId());
            try {
                if (createManagerConnection(astServer))
                    postInfoMessage("Manager Connection re-created",
                            astServer.getName() + " with IP " + astServer.getHostname());
            } catch (Exception ex) {
                ex.printStackTrace();
                log.error("Couldn't create manager connection for server " + astServer.getId(), ex);
                postErrorMessage("Manager Connection Lost",
                        "Couldn't create manager connection for Asterisk server " + astServer.getName()
                                + " with IP " + astServer.getHostname());
            }
        }
    }

    // private class ManagerDaemon extends TimerTask {
    //
    // @Override
    // public void run() {
    // // safiServerChanged();
    // synchronized (safiServerConfig) {
    // for (AsteriskServer astServer : safiServerConfig.getAsteriskServers()) {
    // if (!astServer.isActive())
    // continue;
    // SafiManagerConnection conn = managerConnectionMap.get(astServer.getId());
    // if (conn == null) {
    // try {
    // createManagerConnection(astServer);
    // } catch (Exception e) {
    // e.printStackTrace();
    // log.error("Couldn't create manager connection for server " +
    // astServer.getId(), e);
    // }
    // } else {
    // try {
    // org.asteriskjava.manager.response.ManagerResponse res = conn
    // .sendAction(new PingAction());
    // } catch (Exception e) {
    // log.warn("Ping failed to manager connection, renewing connection.", e);
    // managerConnectionMap.remove(astServer.getId());
    // try {
    // createManagerConnection(astServer);
    // } catch (Exception ex) {
    // ex.printStackTrace();
    // log.error("Couldn't create manager connection for server " +
    // astServer.getId(), ex);
    // postErrorMessage("Manager Connection Lost",
    // "Couldn't create manager connection for Asterisk server " +
    // astServer.getName()
    // + " with IP " + astServer.getHostname());
    // }
    //
    // }
    // }
    // }
    // }
    // }
    // }

    public boolean isStopping() {
        return stopping;
    }

    class AgiUncaughtExceptionHandler implements java.lang.Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            log.error("Safi FastAGI server error", e);
            postErrorMessage("FastAGI Error", "Uncaught FastAGI Error: " + e.getLocalizedMessage());
            // if (oldSafiServerConfig != null && oldSafiServerConfig !=
            // safiServerConfig) {
            // rollbackServerSettings(oldSafiServerConfig);
            // } else {
            // WrapperManager.restart();
            // log.warn("We've already rolled back. This error may be unrecoverable");
            // }

        }
    }

    class SafiServerListenerInternal implements SafiServerListener {
        private String name;
        private String ip;

        SafiServerListenerInternal(String name, String ip) {
            this.name = name;
            this.ip = ip;
        }

        @Override
        public void serverStarted() {
            // postInfoMessage("Asterisk Connection Created", "Connection to
            // AsteriskServer at
            // "+ip+" was successfully created.");

        }

        @Override
        public void serverStopped(String reason) {
            postInfoMessage("Asterisk Connection Lost",
                    "Connection to AsteriskServer named " + name + " at " + ip + " was lost. Reason: " + reason);
        }
    }

    @Override
    public int getNumAgiRequests() {
        return numAgiRequests;
    }

    public int getNumCustomInitiations() {
        return numCustomInitiations;
    }

    public static class AstInitInfo extends EObjectImpl implements AsteriskInitiatorInfo {
        private AgiRequest request;
        private AgiChannel channel;
        private ManagerConnection managerConnection;
        private AsteriskServer server;

        public AgiRequest getRequest() {
            return request;
        }

        public void setRequest(AgiRequest request) {
            this.request = request;
        }

        public AgiChannel getChannel() {
            return channel;
        }

        public void setChannel(AgiChannel channel) {
            this.channel = channel;
        }

        public ManagerConnection getManagerConnection() {
            return managerConnection;
        }

        public void setManagerConnection(ManagerConnection managerConnection) {
            this.managerConnection = managerConnection;
        }

        public void setAsteriskServer(AsteriskServer server) {
            this.server = server;

        }

        public AsteriskServer getAsteriskServer() {
            return server;
        }

    }

    public boolean isUsePing() {
        return usePing;
    }

    public void setUsePing(boolean usePing) {
        this.usePing = usePing;
    }

    @Override
    public long getManagerRetryPeriod() {
        return managerRetryPeriod;
    }

    @Override
    public void setManagerRetryPeriod(long managerRetryPeriod) {
        this.managerRetryPeriod = managerRetryPeriod;
    }
}