Package com.ericsson.ssa.container

Source Code of com.ericsson.ssa.container.GrizzlyNetworkManager$TlsSharedAsyncWriteCallbackHandler

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.container;

import com.ericsson.ssa.config.ConfigFactory;
import com.ericsson.ssa.config.annotations.Configuration;
import com.ericsson.ssa.config.annotations.UpdatePolicy;
import com.ericsson.ssa.config.annotations.UsagePolicy;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.OutboundInterface;
import com.ericsson.ssa.sip.SipServletMessageImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.utils.ByteBufferPool;
import com.ericsson.ssa.utils.Pair;

import com.sun.grizzly.CallbackHandler;
import com.sun.grizzly.CallbackHandlerDescriptor;
import com.sun.grizzly.ConnectorHandler;
import com.sun.grizzly.Context;
import com.sun.grizzly.Context.KeyRegistrationState;
import com.sun.grizzly.Context.OpType;
import com.sun.grizzly.Controller;
import com.sun.grizzly.Controller.Protocol;
import com.sun.grizzly.DefaultPipeline;
import com.sun.grizzly.DefaultProtocolChain;
import com.sun.grizzly.DefaultSelectionKeyHandler;
import com.sun.grizzly.IOEvent;
import com.sun.grizzly.Pipeline;
import com.sun.grizzly.PipelineFullException;
import com.sun.grizzly.ProtocolChain;
import com.sun.grizzly.ProtocolChainContextTask;
import com.sun.grizzly.ProtocolChainInstanceHandler;
import com.sun.grizzly.ProtocolFilter;
import com.sun.grizzly.SSLCallbackHandler;
import com.sun.grizzly.SSLConnectorHandler;
import com.sun.grizzly.SSLSelectorHandler;
import com.sun.grizzly.SelectionKeyHandler;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.TCPConnectorHandler;
import com.sun.grizzly.TCPSelectorHandler;
import com.sun.grizzly.UDPConnectorHandler;
import com.sun.grizzly.UDPSelectorHandler;
import com.sun.grizzly.async.AsyncQueueWritable;
import com.sun.grizzly.async.AsyncWriteCallbackHandler;
import com.sun.grizzly.async.AsyncWriteQueueRecord;
import com.sun.grizzly.async.AsyncQueueDataProcessor;
import com.sun.grizzly.filter.ReadFilter;
import com.sun.grizzly.filter.SSLReadFilter;
import com.sun.grizzly.util.ByteBufferFactory.ByteBufferType;
import com.sun.grizzly.util.SelectionKeyAttachment;
import com.sun.grizzly.util.SelectionKeyOP;
import com.sun.grizzly.util.SelectorFactory;
import com.sun.grizzly.util.ThreadAttachment;
import com.sun.grizzly.util.ThreadAttachment.Mode;
import com.sun.grizzly.util.WorkerThread;
import com.sun.grizzly.util.net.SSLImplementation;
import com.sun.grizzly.util.net.ServerSocketFactory;
import com.sun.grizzly.Role;

import com.sun.grizzly.util.State;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.net.ConnectException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;

/**
* This class is responsible for initializing the Grizzly based SIP protocol
* support in Sailfin.
*
* @author ekrigro
* @author Jeanfrancois Arcand
*/
public class GrizzlyNetworkManager extends NetworkManager implements Runnable {

    private final static String IS_CLIENT_EXECUTION = "isClientExecution";
    public final static int DEFAULT_BB_SIZE = 8192;
    public final static String UDP_BUFFER = "UDP_BUFFER";
    public final static int DEFAULT_SSL_BB_SIZE = 20480;
    public final static int DEFAULT_RECEIVE_BB_SIZE = 16 * 8192;
    private static final String SERVER_PIPELINE_NAME = "SipContainer-servers"; //Only used for name of Pipelines.
    private static final String CLIENT_PIPELINE_NAME = "SipContainer-clients"; //Only used for name of Pipelines.
    private static final int DEFAULT_PIPELINE_DISTINCTION_PORT = 5060; //Only used for unique ID of the default Pipeline.
    public final static String SIP_CERTS = "Sip.Certs";
    protected final AsyncWriteCallbackHandler tcpUdpAsyncWriteCallbackHandler =
            new SharedAsyncWriteCallbackHandler();
    protected final AsyncWriteCallbackHandler tlsAsyncWriteCallbackHandler =
            new TlsSharedAsyncWriteCallbackHandler();
    public static final Boolean useDefaultUDPSelectorHandler = Boolean.getBoolean(
            "org.jvnet.glassfish.comms.useDefaultUDPSelectorHandler");
    private static final int DEFAULT_REQUEST_TIMEOUT = 15;
    private int receiveBufferSizeInBytes = DEFAULT_RECEIVE_BB_SIZE;
    private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
    private static final Logger smi_logger = LogUtil.SMI_LOGGER.getLogger();
    private final ByteBufferPool _bbPool = new ByteBufferPool(DEFAULT_BB_SIZE);
    private int sendBufferSize = DEFAULT_BB_SIZE;
    private static final Queue<ByteBuffer> secureByteBuffersPool =
            new ConcurrentLinkedQueue<ByteBuffer>();
    private Controller clientController = new Controller();
    private Controller serverController = new Controller();
    private boolean serverControllerStarted = false;   
    private boolean clientControllerStarted = false;
    private Pipeline<Callable> serverPipeline = null;
    private Pipeline<Callable> clientPipeline = null;
    private Layer nextLayer = null;
    private SSLContext sslContext;
    private String keypass = "changeit";
  
    private static final String KEYSTORE_PASS_PROP = "javax.net.ssl.keyStorePassword";

    //initialized
    private ConnectionManager connectionManager;

    //dont expire by default
    private int keepAliveTimeoutInSeconds = -1;
    private String keyStore = null;
    private String trustStore = null;
    private int maxPipelineThreads = 10; //100
    private int minPipelineThreads = 10; //20
    private int pipelineThreadsIncrement = 1;
    private int pipelineInitialByteBufferSize = 8192; //same as DefaultPipeline
    private boolean clientsShareServerThreadPool = true;
    private int maxPendingCount = 50;
    private InetSocketAddress localUDPSocketAddress;
    private Boolean disableUDPPortForClient = Boolean.getBoolean(
            "org.jvnet.glassfish.comms.disableUDPSourcePort");
    private int queueSize = -1;
    private int requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
    //[Eltjo] Sorry for this monster
    /**
     * The model for keeping track of resources that are allocated for a specific
     * SipBindingCtx (sip-listener).
     * The structure is such:
     * Map-+-+-...
     *     |
     *     context -> Pair-+
     *                    / \
     *       SipBindingCtx  Map-+-+...
     *                          |
     *                          protocol -> Pair-+
     *                                          / \
     *                     server SelectorHandler client SelectorHandler
     *
     * As you can see it assumes that a SipBindingCtx can only have one
     * TargetTuple per protocol, meaning only one port per protocol.
     */
    private HashMap<String, Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>>> ctxs =
            new HashMap<String, Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>>>();
    private final ConcurrentHashMap<SelectionKey, TargetTuple> outboundConnectionsTuple =
            new ConcurrentHashMap<SelectionKey, TargetTuple>();
    private final ConcurrentHashMap<TargetTuple, TargetTuple> externalListeners =
            new ConcurrentHashMap<TargetTuple, TargetTuple>();
    private final List<TargetTuple> internalListeners = new ArrayList<TargetTuple>();
    private int maxTmpSelectors = 20;  
    SipBindingResolver sipBindingResolver;
    private final ConcurrentHashMap<TargetTuple, Long>  staleUDPConnections =
            new ConcurrentHashMap<TargetTuple, Long>(8);

    //using by default 0 to avoid FT failures
    static final long STALE_CONNECTIONS_TIMEOUT = 0;

    long staleConnectionsTimeout = STALE_CONNECTIONS_TIMEOUT;

    private static final int DEFAULT_SSL_HANDSHAKE_TIMEOUT = 10;
   
    int sslHandshakeTimeout = DEFAULT_SSL_HANDSHAKE_TIMEOUT;
   
    public GrizzlyNetworkManager() {
          keypass = getKeyStorePass();
          sipBindingResolver = SipBindingResolver.instance();              
    }

    public synchronized void start() {
        initPipeline();
       
        for (String ctx : sipBindingResolver.getContexts()) { 
            startCtxServers(ctx);
        }

        SipBindingResolver.instance().registerSipBindingListener(new SipBindingListener() {

            public void newSipBindingCtxAvaliable(String context) {
                startCtxServers(context);
                updateListeners();
            }

            public void sipBindingCtxUpdated(String context) {
                restartCtxServers(context);
                updateListeners();
            }

            public void publicSipBindingCtxUpdated() {
            //ignore
            }

            public void sipBindingCtxRemoved(String context) {
                stopCtxServers(context);
                updateListeners();
            }
            });

        //Initiate the outbound connections pool
        connectionManager = new ConnectionManager();
        updateListeners();

    }

    public void startCtxServers(String ctx) {
        SipBindingCtx sipBindingCtx =
                sipBindingResolver.getContext(ctx);    
        if ((sipBindingCtx != null) && !sipBindingCtx.isStale()) {
            Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair =
                    new Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>>(sipBindingCtx,
                    new HashMap<SipTransports, Pair<SelectorHandler, SelectorHandler>>());

            ctxs.put(ctx, pair);
            registerForMonitoring(sipBindingCtx.getId(), sipBindingCtx.getTargetTuples()[0]);
            if (sipBindingCtx.isEnabled()) {
                final ProtocolChain protocolChain = getProtocolChain();

                ProtocolChainInstanceHandler instanceHandler =
                        new SimpleProtocolChainInstanceHandler(protocolChain);

                serverController.setPipeline(serverPipeline);

                clientController.setPipeline(clientPipeline);

                for (TargetTuple targetTuple : sipBindingCtx.getTargetTuples()) {
                    if (logger.isLoggable(Level.INFO)) {
                        logger.log(Level.INFO,
                                "sip.stack.network.starting_sip_binding",
                                new Object[]{targetTuple.toString()});
                    }

                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "sip.network.grizzly.adding.server.context",
                                new Object[]{sipBindingCtx.getContext()});

                        logger.log(Level.FINEST,
                                "sip.network.grizzly.adding.client.context",
                                new Object[]{sipBindingCtx.getContext()});
                    }
                    Pair<SelectorHandler, SelectorHandler> selHdlPair =
                            new Pair<SelectorHandler, SelectorHandler>();
                    startSelectorHandlers(targetTuple, selHdlPair,
                            instanceHandler, sipBindingCtx);
                    pair.getRight().put(targetTuple.getProtocol(),
                            selHdlPair);
                }
               
            } else {
            // Just putting the ctx is enough.
            }
        } else {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                        "sip.network.grizzly.adding.context.stale",
                        new Object[]{ctx});
            }
        }
    }

    private synchronized void updateListeners() {
        // binding resolver listener would have updated the state there
        String[] pvtctxs = sipBindingResolver.getPrivateContexts();
        internalListeners.clear();
        if (pvtctxs != null && pvtctxs.length > 0) {
            for (String pvtctx : pvtctxs) {
                SipBindingCtx bctx =
                        sipBindingResolver.getPrivateContext(pvtctx);
                for (TargetTuple tt : bctx.getTargetTuples()) {
                    internalListeners.add(tt);
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "Added internal listener " +
                                tt);
                    }
                }
            }
        }
        String[] externalctxs = sipBindingResolver.getLocalContexts();
        externalListeners.clear();
        if (externalctxs != null && externalctxs.length > 0) {
            for (String extctx : externalctxs) {
                SipBindingCtx bctx = sipBindingResolver.getLocalContext(extctx);
                for (TargetTuple tt : bctx.getTargetTuples()) {
                    SipBindingCtx publiccontext =
                            sipBindingResolver.getPublicContext(bctx.getContext());
                    if (publiccontext == null ||
                            publiccontext.getTargetTuples() == null ||
                            publiccontext.getTargetTuples().length == 0) {
                        externalListeners.put(tt, tt);
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE, "Added external listener " +
                                    tt + " for external address " + tt);
                        }
                    } else {
                        TargetTuple exttuple =
                                publiccontext.getTargetTupleForProtocol(tt.getProtocol());
                        externalListeners.put(tt, exttuple);
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE, "Added external listener " +
                                    tt + " for external address " + exttuple);
                        }
                    }
                }
            }
        }
    }
  
   
    public void startSelectorHandlers(TargetTuple targetTuple,
            Pair<SelectorHandler, SelectorHandler> selHdlPair,
            ProtocolChainInstanceHandler instanceHandler,
            SipBindingCtx sipBindingCtx) {
        runNetworkManager();       
        switch (targetTuple.getProtocol().ordinal()) {
            case SipTransports.UDP: {
                if (!useDefaultUDPSelectorHandler) {
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "Initializing Reading UDP Selecor handler");
                    }

                    final ProtocolChain udpProtocolChain =
                            getUDPProtocolChain(sipBindingCtx);

                    ProtocolChainInstanceHandler udpInstanceHandler =
                            new SimpleProtocolChainInstanceHandler(udpProtocolChain);

                    selHdlPair.setLeft(startUDPServer(targetTuple,
                            udpInstanceHandler));

                    selHdlPair.setRight(startUDPClient(udpInstanceHandler));
                } else {
                    selHdlPair.setLeft(startUDPServer(targetTuple,
                            instanceHandler));

                    selHdlPair.setRight(startUDPClient(instanceHandler));
                }

                break;
            }

            case SipTransports.TCP: {
                selHdlPair.setLeft(startTCPServer(targetTuple, instanceHandler));

                selHdlPair.setRight(startTCPClient(instanceHandler));

                break;
            }

            case SipTransports.TLS: {
                final ProtocolChain tlsProtocolChain =
                        getTLSProtocolChain(sipBindingCtx);

                ProtocolChainInstanceHandler tlsInstanceHandler =
                        new SimpleProtocolChainInstanceHandler(tlsProtocolChain);

                selHdlPair.setLeft(startTLSServer(targetTuple,
                        tlsInstanceHandler));

                selHdlPair.setRight(startTLSClient(tlsInstanceHandler));

                break;
            }

            case SipTransports.UNDEFINED: //Hmm, should not happen
                break;
        }
     }

    private void restartCtxServers(String ctx) {
        SipBindingCtx sipBindingCtx =
                SipBindingResolver.instance().getContext(ctx);

        Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair =
                ctxs.remove(ctx);

        if (pair != null) {
            SipBindingCtx.UpdateHint hint =
                    pair.getLeft().getUpdateHints(sipBindingCtx);

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                        "sip.network.grizzly.adding.context.update",
                        new Object[]{ctx, hint});
            }

            if (updateCtx(pair, sipBindingCtx, hint)) {
                pair.setLeft(sipBindingCtx);
            }

            ctxs.put(ctx, pair);
        } else {
            //Weird case. somehow we are updating an unknown context.

            //Lets just start it.

            //TODO does this make sense?
            startCtxServers(ctx);
        }
    }

    /**
     *
     * @param pair
     * @param sipBindingCtx
     * @param hint
     * @return true when an update was needed and performed.
     */
    private boolean updateCtx(
            Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair,
            SipBindingCtx sipBindingCtx, SipBindingCtx.UpdateHint hint) {
        //A SipBindingCtx can be updated in several ways, it can be:

        //- disabled / enabled

        //- altered wrt ssl attributes

        //- altered wrt the TargetTuples

        //Or a combination

        //The update procedure does little assumptions on the SipBindingCtx

        //I.e. the fact that in the current config model a SipBindingCtx either

        //contains a UDP and TCP TargetTuple or only a TLS TargetTuple is not

        //taken into account. Assuming that any mix of TargetTuples can be

        //present makes that the procedure is prepared for any changes in the

        //config model.
        boolean updated = false;

        switch (hint) {
            case UP_TO_DATE:

                //Okidoki
                break;

            case ENABLE_DISABLED:
                enOrDisableCtx(pair, sipBindingCtx);
                updated = true;

                break;

            case SSL_UPDATE:
                updateSSLCtx(pair, sipBindingCtx);
                updated = true;

                break;

            case TARGET_TUPLE_UPDATE:
                updateTargetTuplesCtx(pair, sipBindingCtx);
                updated = true;

                break;

            case MULTIPLE:

                //The approach for disable/enable is to crudely remove / add the

                //resources
                if (hint.contains(SipBindingCtx.UpdateHint.ENABLE_DISABLED)) {
                    enOrDisableCtx(pair, sipBindingCtx);
                } else {
                    if (hint.contains(SipBindingCtx.UpdateHint.SSL_UPDATE)) {
                        updateSSLCtx(pair, sipBindingCtx);
                    }

                    if (hint.contains(SipBindingCtx.UpdateHint.TARGET_TUPLE_UPDATE)) {
                        updateTargetTuplesCtx(pair, sipBindingCtx);
                    }
                }

                updated = true;

                break;

            case NOT_SAME:

                //Should not happen
                break;
        }

        return updated;
    }

    private void enOrDisableCtx(
            Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair,
            SipBindingCtx sipBindingCtx) {
        if (pair.getLeft().isEnabled()) {
            Iterator<Map.Entry<SipTransports, Pair<SelectorHandler, SelectorHandler>>> i =
                    pair.getRight().entrySet().iterator();

            while (i.hasNext()) {
                Map.Entry<SipTransports, Pair<SelectorHandler, SelectorHandler>> e =
                        i.next();

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.adding.context.disable",
                            new Object[]{
                        e.getKey().name(),
                        sipBindingCtx.getContext()
                    });
                }

                String timeout = ConfigFactory.getConfig().get(pair.getLeft().
                        getContext(),
                        "DisableTimeout");

                String closeSocket = ConfigFactory.getConfig().get(pair.getLeft().
                        getContext(),
                        "CloseServerSocketDuringDisable");

                int timeoutinseconds = -1;

                try {
                    timeoutinseconds = Integer.parseInt(timeout);
                } catch (Exception ie) {
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "sip.stack.network.listener.timeout.error");
                    }
                }

                if (timeoutinseconds > 0) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE,
                                " sip-.network.grizzly.listener.disable.timeout",
                                new Object[]{timeoutinseconds});
                    }

                    /* default dont close
                     */
                    boolean close = false;

                    close = Boolean.parseBoolean(closeSocket);

                    disableListener(e, sipBindingCtx, timeoutinseconds, close);
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.shutdown.selectors");
                }

                shutdownAndRemoveSelectorHandlers(e.getKey(),
                        e.getValue().getLeft(), e.getValue().getRight());

                //Clean up the entry, a disabled ctx is expected to have

                //no resources. This makes it easier to align with the case

                //were a new but disabled ctx is started.
                i.remove();
            }
        } else {
            for (TargetTuple targetTuple : sipBindingCtx.getTargetTuples()) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.adding.server.context.enable",
                            new Object[]{
                        targetTuple.getProtocol().name(),
                        sipBindingCtx.getContext()
                    });

                    logger.log(Level.FINE,
                            "sip.network.grizzly.adding.client.context.enable",
                            new Object[]{
                        targetTuple.getProtocol().name(),
                        sipBindingCtx.getContext()
                    });
                }

                final ProtocolChain udpProtocolChain = getProtocolChain();

                ProtocolChainInstanceHandler instanceHandler =
                        new SimpleProtocolChainInstanceHandler(udpProtocolChain);

                Pair<SelectorHandler, SelectorHandler> selHdlPair =
                        new Pair<SelectorHandler, SelectorHandler>();

                startSelectorHandlers(targetTuple, selHdlPair, instanceHandler,
                        sipBindingCtx);

                pair.getRight().put(targetTuple.getProtocol(), selHdlPair);
            }
        }
    }

    private void disableListener(
            Map.Entry<SipTransports, Pair<SelectorHandler, SelectorHandler>> e,
            SipBindingCtx sipBindingCtx, int timeinseconds, boolean closesocket) {
        /*
         * We can close this server socket here and not accept any more
         * connections, but there could be cases where a client is connecting
         * to send a response back. We anyway block new requests using the);
         * logic below, so closing the server socket may not be necessary.
         * The VIP/CLB might use the presence of the socket to detect if
         * a listener/instance is alive. So let us close the server socket
         * depending on  a property.
         */
        SelectorHandler serverHandler = e.getValue().getLeft();

        if (closesocket) {
            try {
                Set<SelectionKey> keys = serverHandler.keys();

                for (SelectionKey key : keys) {
                    if ((key.interestOps() & SelectionKey.OP_ACCEPT) ==
                            SelectionKey.OP_ACCEPT) {
                        serverHandler.closeChannel(key.channel());
                    }
                }
            } catch (Exception ce) {
                logger.log(Level.SEVERE,
                        "sip.stack.network.listener.disable.close.error");
            }
        }

        /*
         * Modify the filter chain to add a filter which blocks
         * new SIP requests.
         */
        DefaultProtocolChain protocolChain = new DefaultProtocolChain();

        if (serverHandler.protocol() == Controller.Protocol.TLS) {
            SSLReadFilter sslfilter = getSSLReadFilter(sipBindingCtx);

            protocolChain.addFilter(sslfilter);

            protocolChain.addFilter(new MessageProcessorFilter(this,
                    requestTimeOut, true, _bbPool, tlsAsyncWriteCallbackHandler));
        } else {
            protocolChain.setContinuousExecution(false);

            ReadFilter readFilter = new SharedReadFilter();

            readFilter.setContinuousExecution(false);

            protocolChain.addFilter(readFilter);

            protocolChain.addFilter(new MessageProcessorFilter(this,
                    requestTimeOut, true, _bbPool,
                    tcpUdpAsyncWriteCallbackHandler));
        }

        ProtocolChainInstanceHandler instanceHandler =
                new SimpleProtocolChainInstanceHandler(protocolChain);

        serverHandler.setProtocolChainInstanceHandler(instanceHandler);

        /*
         * Wait until the diable timeout is over, this method will
         * block until disable timeout, Each listener will wait for this much
         * time. A periodic check for incomplete transactions will be more efficient
         * but there is no way to check if there are pending transactions
         * which have to use the channels in this selector.
         */
        try {
            Thread.sleep(timeinseconds * 1000);
        } catch (Exception e1) {
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST,
                        "sip.network.grizzly.interuppt.disable.timeout", e1);
            }
        }
    }

    private ProtocolFilter createMessageProcessorFilter(boolean disablerequests,
            AsyncWriteCallbackHandler asyncWriteCallbackHandler) {
        /*    SipParserErrorHandlerImpl sipParserErrorHandler =
        new SipParserErrorHandlerImpl(this);
        sipParserErrorHandler.setErrorResponseEnabled(
        this.isErrorResponseEnabled()); */
        ProtocolFilter filter = new MessageProcessorFilter(this,
                requestTimeOut, disablerequests, _bbPool,
                asyncWriteCallbackHandler);

        return filter;
    }

    private void updateSSLCtx(
            Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair,
            SipBindingCtx sipBindingCtx) {
        //Consider the following cases:

        //- TLS exist in old and new

        //--and they equal

        //--->update the selectionHandler according SSL update       

        //--and they differ

        //--->ignore, the new TLS will take it       

        //-TLS exist in old or new only

        //-->ignore, the new TLS will take it

        //-TLS doesn't exist in old or new

        //-->ignore, if no TLS the SSL attributes are not used.

        //This is tollerated to allow admin to create the SSL before adding

        //or updating to TLS.       
        TargetTuple oldTlsTT =
                pair.getLeft().getTargetTupleForProtocol(SipTransports.TLS_PROT);

        TargetTuple newTlsTT =
                sipBindingCtx.getTargetTupleForProtocol(SipTransports.TLS_PROT);

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "sip.network.grizzly.update.sslcontext",
                    new Object[]{pair});
        }

        if (((oldTlsTT != null) && (newTlsTT != null)) &&
                oldTlsTT.equals(newTlsTT)) {
            Pair<SelectorHandler, SelectorHandler> selHdlPair = pair.getRight().
                    get(SipTransports.TLS_PROT);

            if (selHdlPair != null) {
                //disable can cause the pair to be absent.
                SSLSelectorHandler serverTlsSH =
                        (SSLSelectorHandler) selHdlPair.getLeft();

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.update.sslcontext",
                            new Object[]{serverTlsSH});

                    logger.log(Level.FINE,
                            "sip.network.grizzly.update.sslcontext",
                            serverTlsSH.getProtocolChainInstanceHandler());

                    logger.log(Level.FINE,
                            "sip.network.grizzly.update.sslcontext",
                            ((SimpleProtocolChainInstanceHandler) serverTlsSH.getProtocolChainInstanceHandler()).getProtocolChain());
                }

                ProtocolChain protocolChain =
                        ((SimpleProtocolChainInstanceHandler) serverTlsSH.getProtocolChainInstanceHandler()).getProtocolChain();

                updateTLSProtocolChain(protocolChain, sipBindingCtx);
            }
        }
    }

    private void updateTargetTuplesCtx(
            Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair,
            SipBindingCtx sipBindingCtx) {
        //Consider the following cases:

        //- A TT exist in old and new

        //--and they differ

        //--->remove the old one add the new one       

        //--and they equal

        //--->okidoki       

        //-A TT exist in new only

        //-->Add it

        //-A TT exist in old only

        //-->Remove it       
        List<TargetTuple> oldTTs = new ArrayList<TargetTuple>(Arrays.asList(
                pair.getLeft().getTargetTuples())); //Make sure remove is supported      

        List<TargetTuple> newTTs =
                Arrays.asList(sipBindingCtx.getTargetTuples());

        for (TargetTuple newTT : newTTs) {
            TargetTuple oldTT =
                    pair.getLeft().getTargetTupleForProtocol(newTT.getProtocol());

            if (oldTT != null) {
                if (!oldTT.equals(newTT)) {
                    //remove the old one add the new one
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "sip.network.grizzly.update.tt",
                                new Object[]{
                            newTT.getProtocol().name(),
                            sipBindingCtx.getContext()
                        });
                    }

                    //Remove
                    Pair<SelectorHandler, SelectorHandler> selHdlPair = pair.getRight().
                            get(newTT.getProtocol());

                    if (selHdlPair != null) { //disable can cause the pair to be absent.                     
                        shutdownAndRemoveSelectorHandlers(newTT.getProtocol(),
                                selHdlPair.getLeft(), selHdlPair.getRight());

                        //Add
                        final ProtocolChain udpProtocolChain =
                                getProtocolChain();

                        ProtocolChainInstanceHandler instanceHandler =
                                new SimpleProtocolChainInstanceHandler(udpProtocolChain);

                        startSelectorHandlers(newTT, selHdlPair,
                                instanceHandler, sipBindingCtx);
                    }

                    oldTTs.remove(oldTT);
                } else {
                //okidoki

                //Check updateSSL, there the SSL changes are taken into

                //account.
                }
            } else {
                //Add it
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "sip.network.grizzly.update.tt",
                            new Object[]{
                        newTT.getProtocol().name(),
                        sipBindingCtx.getContext()
                    });
                }

                final ProtocolChain udpProtocolChain = getProtocolChain();

                ProtocolChainInstanceHandler instanceHandler =
                        new SimpleProtocolChainInstanceHandler(udpProtocolChain);

                Pair<SelectorHandler, SelectorHandler> selHdlPair =
                        new Pair<SelectorHandler, SelectorHandler>();
                startSelectorHandlers(newTT, selHdlPair, instanceHandler,
                        sipBindingCtx);

                pair.getRight().put(newTT.getProtocol(), selHdlPair);
            }
        }

        for (TargetTuple oldTT : oldTTs) {
            //Remove it       
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "sip.network.grizzly.update.remove.tt",
                        new Object[]{
                    oldTT.getProtocol().name(),
                    sipBindingCtx.getContext()
                });
            }

            Pair<SelectorHandler, SelectorHandler> selHdlPair = pair.getRight().
                    remove(oldTT.getProtocol());

            shutdownAndRemoveSelectorHandlers(oldTT.getProtocol(),
                    selHdlPair.getLeft(), selHdlPair.getRight());
        }
    }

    private void stopCtxServers(String ctx) {
        //Crude approach to stopping SipBindingCtx

        //TODO make gracefull
        Pair<SipBindingCtx, Map<SipTransports, Pair<SelectorHandler, SelectorHandler>>> pair =
                ctxs.remove(ctx);

        if (pair != null) {
            if (pair.getLeft().isEnabled()) {
                for (Map.Entry<SipTransports, Pair<SelectorHandler, SelectorHandler>> e : pair.getRight().
                        entrySet()) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE,
                                "sip.network.grizzly.stop.context",
                                new Object[]{e.getKey().name(), ctx});
                    }

                    shutdownAndRemoveSelectorHandlers(e.getKey(),
                            e.getValue().getLeft(), e.getValue().getRight());
                }
            } else {
                //Just removing the ctx is enough.
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.stop.disabled.context",
                            new Object[]{ctx});
                }
            }
        }
    }

    private void shutdownAndRemoveSelectorHandlers(SipTransports protocol,
            SelectorHandler server, SelectorHandler client) {
        removeSelectorHander(serverController, server);
        removeSelectorHander(clientController, client);
        clearCachedHandlers(server, client);
    }

    /*
     * Concurrent modification exception is thrown by controller.remove.... 
     * If this exception is not caught it would cause the admin event to fail
     * and sets the restart required flag to true. Issue 491 in Grizzly
     * is worked around by extending the TcpSelectorHanlder 
     */
    private void removeSelectorHander(Controller controller,
            SelectorHandler handler) {
        controller.removeSelectorHandler(handler);
    }
// version without logging
//    private synchronized void clearCachedHandlers(SelectorHandler server, SelectorHandler client) {     
//      Collection<ConnectorHandler> cache = connectionManager.streams.values();
//      for (ConnectorHandler connectorHandler : cache) {
//        // remove all that are related to the specific handler
//        SelectorHandler selHandler = connectorHandler.getSelectorHandler();
//        if (selHandler == null || selHandler.equals(client) || selHandler.equals(server)) {
//          cache.remove(connectorHandler);
//        }
//    }
//  }

// version with logging...
    private synchronized void clearCachedHandlers(SelectorHandler server, SelectorHandler client) {
      ConcurrentHashMap<TargetTuple, ConnectorHandler> cache = connectionManager.streams;
    Set<TargetTuple> tuples = cache.keySet();
         for (TargetTuple tt : tuples) {
            ConnectorHandler connectorHandler = cache.get(tt);
            SelectorHandler selHandler = connectorHandler.getSelectorHandler();
            if (selHandler == null) {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST,
                            "sip.network.grizzly.clear.handlers",
                            new Object[]{tt});
                }
                cache.remove(tt);
            } else if (selHandler.equals(client) || selHandler.equals(server)) {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST,
                            "sip.network.grizzly.clear.handlers",
                            new Object[]{tt});
                }
                cache.remove(tt);
            }
        }
    }
 
 
  private UDPSelectorHandler startUDPServer(TargetTuple targetTuple,
            ProtocolChainInstanceHandler instanceHandler) {
        UDPSelectorHandler udpSelector =
                createUdpEndpoint(targetTuple.getSocketAddress());

        localUDPSocketAddress = targetTuple.getSocketAddress();

        SelectionKeyHandler keyHandler = createSelectionKeyHandler(-1);

        udpSelector.setSelectionKeyHandler(keyHandler);

        udpSelector.setProtocolChainInstanceHandler(instanceHandler);

        serverController.addSelectorHandler(udpSelector);

        return udpSelector;
    }

    private TCPSelectorHandler startTCPServer(TargetTuple targetTuple,
            ProtocolChainInstanceHandler instanceHandler) {
        TCPSelectorHandler tcpSelector =
                createTcpEndpoint(targetTuple.getSocketAddress());

        SelectionKeyHandler keyHandler =
                createSelectionKeyHandler(keepAliveTimeoutInSeconds);

        tcpSelector.setSelectionKeyHandler(keyHandler);

        tcpSelector.setProtocolChainInstanceHandler(instanceHandler);

        tcpSelector.setSsBackLog(maxPendingCount);

        serverController.addSelectorHandler(tcpSelector);

        return tcpSelector;
    }

    private SSLSelectorHandler startTLSServer(TargetTuple targetTuple,
            ProtocolChainInstanceHandler tlsInstanceHandler) {
        SSLSelectorHandler tlsSelector =
                createTLSEndpoint(targetTuple.getSocketAddress());

        SelectionKeyHandler keyHandler =
                createSelectionKeyHandler(keepAliveTimeoutInSeconds);

        tlsSelector.setSelectionKeyHandler(keyHandler);

        tlsSelector.setProtocolChainInstanceHandler(tlsInstanceHandler);

        tlsSelector.setSsBackLog(maxPendingCount);

        serverController.addSelectorHandler(tlsSelector);

        return tlsSelector;
    }

    private UDPSelectorHandler startUDPClient(
            ProtocolChainInstanceHandler instanceHandler) {
        UDPSelectorHandler udpClientSelectorHandler =
                useDefaultUDPSelectorHandler
                ? new UDPSelectorHandler(true) : new ReadUDPSelectorHandler(true);

        SelectionKeyHandler keyHandler =
                createSelectionKeyHandler(keepAliveTimeoutInSeconds);

        udpClientSelectorHandler.setSelectionKeyHandler(keyHandler);

        udpClientSelectorHandler.setProtocolChainInstanceHandler(instanceHandler);

        clientController.addSelectorHandler(udpClientSelectorHandler);

        return udpClientSelectorHandler;
    }

    private TCPSelectorHandler startTCPClient(
            ProtocolChainInstanceHandler instanceHandler) {
        TCPSelectorHandler tcpClientSelectorHandler =
                new TCPSelectorHandler(true);

        tcpClientSelectorHandler.setProtocolChainInstanceHandler(instanceHandler);

        SelectionKeyHandler keyHandler =
                createSelectionKeyHandler(keepAliveTimeoutInSeconds);

        tcpClientSelectorHandler.setSelectionKeyHandler(keyHandler);

        tcpClientSelectorHandler.setProtocolChainInstanceHandler(instanceHandler);

        clientController.addSelectorHandler(tcpClientSelectorHandler);

        return tcpClientSelectorHandler;
    }

    private SSLSelectorHandler startTLSClient(
            ProtocolChainInstanceHandler tlsInstanceHandler) {
        SSLSelectorHandler tlsClientSelectorHandler =
                new SSLSelectorHandler(true);

        tlsClientSelectorHandler.setProtocolChainInstanceHandler(tlsInstanceHandler);

        SelectionKeyHandler keyHandler =
                createSelectionKeyHandler(keepAliveTimeoutInSeconds);

        tlsClientSelectorHandler.setSelectionKeyHandler(keyHandler);

        tlsClientSelectorHandler.setProtocolChainInstanceHandler(tlsInstanceHandler);

        clientController.addSelectorHandler(tlsClientSelectorHandler);

        return tlsClientSelectorHandler;
    }

    private SelectionKeyHandler createSelectionKeyHandler(int expire) {
        DefaultSelectionKeyHandler skh = new SharedSelectionKeyHandler();

        skh.setTimeout(expire * 1000);

        skh.setLogger(logger);

        return skh;
    }

    public synchronized void stop() throws IOException {
        if (clientControllerStarted) {
            clientController.stop();
            clientControllerStarted = false;
        }
        if (serverControllerStarted) {
            serverController.stop();
            serverControllerStarted = false;
        }
    }

    private ProtocolChain getProtocolChain() {
        DefaultProtocolChain protocolChain = new DefaultProtocolChain();

        protocolChain.setContinuousExecution(false);

        ReadFilter readFilter = new SharedReadFilter();

        readFilter.setContinuousExecution(false);

        protocolChain.addFilter(readFilter);

        protocolChain.addFilter(createMessageProcessorFilter(false,
                tcpUdpAsyncWriteCallbackHandler));

        return protocolChain;
    }

    private ProtocolChain getTLSProtocolChain(SipBindingCtx sipBindingCtx) {
        SSLReadFilter sslReadFilter = getSSLReadFilter(sipBindingCtx);

        //Carefull dynamic reconfig update of SSL relies on the sslReadFilter

        //to be the first filter in the chain.
        ProtocolChain tlsProtocolChain = new DefaultProtocolChain();

        tlsProtocolChain.addFilter(sslReadFilter);

        tlsProtocolChain.addFilter(createMessageProcessorFilter(false,
                tlsAsyncWriteCallbackHandler));

        return tlsProtocolChain;
    }

    private ProtocolChain getUDPProtocolChain(SipBindingCtx sipBindingCtx) {
        DefaultProtocolChain udpProtocolChain = new DefaultProtocolChain();

        udpProtocolChain.setContinuousExecution(false);

        udpProtocolChain.addFilter(createMessageProcessorFilter(false,
                tcpUdpAsyncWriteCallbackHandler));

        return udpProtocolChain;
    }

    private SSLReadFilter getSSLReadFilter(SipBindingCtx sipBindingCtx) {
        SSLReadFilter sslReadFilter = new ClientCertSSLReadFilter();

        sslReadFilter.setSSLContext(getSSLContext(sipBindingCtx));

        Boolean clientAuth = Boolean.parseBoolean(sipBindingCtx.getSSLAttribute(
                "ClientAuthEnabled"));

        sslReadFilter.setNeedClientAuth(clientAuth);

        return sslReadFilter;
    }

    private void updateTLSProtocolChain(ProtocolChain aProtocolChain,
            SipBindingCtx sipBindingCtx) {
        if (aProtocolChain instanceof DefaultProtocolChain) {
            SSLReadFilter sslReadFilter = getSSLReadFilter(sipBindingCtx);

            //Carefull this relies on the sslReadFilter

            //to be the first filter in the chain.
            ((DefaultProtocolChain) aProtocolChain).setProtocolFilter(0,
                    sslReadFilter);
        }
    }
   
    private static String getKeyStorePass () {
        //XXX need to revisit if the value should be cached
        return System.getProperty(KEYSTORE_PASS_PROP, "changeit");
    }

    private SSLContext getSSLContext(SipBindingCtx sipBindingCtx) {
        ServerSocketFactory serverSF = null;

        try {
            //TODO This expects that the ServerSocketFactory initialized

            //is a seperated instance. A brief check of the grizzly

            //classes involved reveals that it is for the default JSSE14 factories
            SSLImplementation sslHelper = SSLImplementation.getInstance();

            serverSF = sslHelper.getServerSocketFactory();

            serverSF.setAttribute("keystoreType", "JKS");

            serverSF.setAttribute("keystore", keyStore);
            serverSF.setAttribute("keystorePass", keypass);
            serverSF.setAttribute("keypass", keypass);

            serverSF.setAttribute("truststoreType", "JKS");

            serverSF.setAttribute("truststore", trustStore);

            String keyAlias = sipBindingCtx.getSSLAttribute("CertNickname");

            serverSF.setAttribute("keyAlias",
                    ((keyAlias != null) ? keyAlias : "s1as")); //Default GF s1sa

            serverSF.init();
        } catch (IOException e) {
        //TODO Logging
        } catch (ClassNotFoundException e) {
        //TODO Logging
        }

        return serverSF.getSSLContext();
    }

    void initPipeline() {
        serverPipeline = createPipeline(SERVER_PIPELINE_NAME,
                DEFAULT_PIPELINE_DISTINCTION_PORT);

        clientPipeline = clientsShareServerThreadPool ? serverPipeline
                : createPipeline(CLIENT_PIPELINE_NAME,
                DEFAULT_PIPELINE_DISTINCTION_PORT);

        SipContainerThreadPool tp = SipContainerThreadPool.getInstance();

        tp.initialize(serverPipeline);

        try {
            SelectorFactory.setMaxSelectors(maxTmpSelectors);
        } catch (IOException ex) {
            logger.log(Level.SEVERE,
                    "sip.stack.network.grizzly.maxtmpselectors.failed");
        }
    }

    private void reinitPipeline() {
        //TODO reinit the pipeline according to the reconfigured settings.
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "sip.network.grizzly.reinit.notimplemented");
        }
    }

    private DefaultPipeline createPipeline(String pipelineName,
            int distinctionPort) {
        DefaultPipeline pipeline = new DefaultPipeline(maxPipelineThreads,
                minPipelineThreads, pipelineName, distinctionPort);

        pipeline.setThreadsIncrement(pipelineThreadsIncrement);

        pipeline.setInitialByteBufferSize(pipelineInitialByteBufferSize);

        pipeline.setByteBufferType(ByteBufferType.HEAP);

        // No limits for now.
        pipeline.setMaxQueueSize(queueSize);

        return pipeline;
    }

    public void next(SipServletRequestImpl req) {
        SipServletResponseImpl resp = validateAndModifyIncomingVia(req);
        if (resp != null) {
            // VIA validation failed, send the error response back
            resp.popDispatcher().dispatch(resp);
            return;
        }
        LayerHelper.next(req, this, nextLayer);
    }

    public void next(SipServletResponseImpl resp) {
        LayerHelper.next(resp, this, nextLayer);
    }

    public void registerNext(Layer layer) {
        nextLayer = layer;
    }

    public void dispatch(final SipServletRequestImpl req) {
        try {
            Reporter reporter = getReporter();
            if (reporter != null) {
                reporter.logOutgoingRequest(Reporter.InterceptionType.LAYER, req, GrizzlyNetworkManager.class.getSimpleName());
            }

            connectionManager.send(req);

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                incrEasSentSipRequests(req.getRemote().getBindIP(),
                        req.getRemote().getBindPort());
                incrReqOutMethodCounter(req.getMethod());
            }
        } catch (IOException e) {
            Reporter reporter = getReporter();
            if (reporter != null) {
                smiLogWriteFailure(req, e);
            }

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                        "sip.stack.network.failed_to_send_request",
                        new Object[]{req.getRemote(), e.getMessage()});
            }
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST,
                        "sip.stack.network.failed_to_send_request", e);
            }

            try {
                if (req.getMethod().equals("ACK")) {
                    return;
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "sip.stack.network.send_503");
                }

                clientPipeline.execute(new Callable() {

                    public Object call() throws Exception {
                        SipServletResponseImpl resp =
                                req.createTerminatingResponse(503);
                        resp.setInternalTransportFailure(true);                               
                        LayerHelper.next(resp, nextLayer, nextLayer);

                        return null;
                    }
                    });
            } catch (PipelineFullException ex) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE,
                            "sip.stack.network.failed_to_send_503");

                    logger.log(Level.SEVERE, ex.getMessage());
                }

                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST, ex.getMessage(), ex);
                }
            }
        }
    }

    //Default path for UDP

    //Failback, StreamResponseDispatchern is the normal path for TCP/TLS
    public void dispatch(SipServletResponseImpl resp) {
        try {
            if (!resp.isRemoteResolved()) {
                resolve(resp);
            }

            if (resp.getRemote().getProtocol() == SipTransports.UDP_PROT
                    && resp.getNeedSerialization() && !resp.isSerialized()){
                resp.serializeForReTransmission(sendBufferSize);
            }

            Reporter reporter = getReporter();
            if (reporter != null) {
                reporter.logOutgoingResponse(Reporter.InterceptionType.LAYER, resp, GrizzlyNetworkManager.class.getSimpleName());
            }

            // Cannot use recursive call here because it will loop.
            connectionManager.send(resp);

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {               
                incrEasSentSipResponses(resp.getRemote().getBindIP(), -1);               
                incrRespOutStatCodeCounter(resp.getStatus());
            }
        } catch (Exception e) {
            Reporter reporter = getReporter();
            if (reporter != null) {
              smiLogWriteFailure(resp, e);
            }

            logSendFailed(resp, e);
        }
    }

    public static void smiLogWriteFailure(SipServletMessageImpl msg, Exception ex){
        if (smi_logger.isLoggable(Level.FINER)) {
            smi_log(logger.getLevel(), "smi.writefailure", new Object[] { ex.getMessage(), msg.toString() });         
        }
    }

    public static void smiLogWriteFailure(String failedMessage, Exception ex){
        if (smi_logger.isLoggable(Level.FINER)) {
            smi_log(logger.getLevel(), "smi.writefailure", new Object[] { ex.getMessage(), failedMessage });         
        }
    }
   
    protected static void smi_log(Level level, String resourceName, Object params[]) {
        // The plan was to let Java Logging API do the formatting
        // however there are a extra storing of parameters which is not wanted
        // when smi logging is active (it duplicates the message.toString() in the log record

        ResourceBundle rb = smi_logger.getResourceBundle();
        String logMessage = resourceName;
        if (rb != null) {
            try {
                logMessage = MessageFormat.format(rb.getString(logMessage), params);
            } catch (java.util.MissingResourceException e) {
                // If we don't find an entry, then we are covered
                // because the logMessage is intialized already
            }
        }

        smi_logger.log(level, logMessage);
    }


    private void logSendFailed(SipServletResponseImpl resp, Exception e) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                    "sip.stack.network.failed_to_send_response",
                    new Object[]{resp.getRemote(), e.getMessage()});
        }
        if (logger.isLoggable(Level.FINEST)){
            logger.log(Level.FINEST,
                    "sip.stack.network.failed_to_send_response",
                    resp.toString());
            logger.log(Level.FINEST, e.getMessage(), e);
        }
    }

    private void resolve(SipServletResponseImpl resp) throws Exception {
        if (resp.getNeedSerialization() && resp.isSerialized()){
            return;
        }
        boolean isClbEndpoint = (resp.getRemote() == null) ? false :
            resp.getRemote().getIsCLBEndpoint();
        TargetTuple newTupple = TargetResolver.getInstance().resolveResponse(
                resp);

        if ((newTupple != null) && (newTupple != resp.getRemote())) {
            newTupple.setCLBEndpoint(isClbEndpoint);
            resp.setRemote(newTupple);
        }
    }

    TCPSelectorHandler createTcpEndpoint(InetSocketAddress addr) {
        int linger = -1;

        final TCPSelectorHandler selectorHandler = new TCPSelectorHandler() {

            /**
             * Intercept the accept operations and cache the associated connection.
             */
            @Override
            public boolean onAcceptInterest(SelectionKey key, Context ctx)
                    throws IOException {

                SelectableChannel channel = acceptWithoutRegistration(key);

                if (channel != null) {
                    configureChannel(channel);

                    SelectionKey readKey = channel.register(selector,
                            SelectionKey.OP_READ);

                    // Cache the connection.
                    TargetTuple tt = connectionManager.add(readKey, this);

                    outboundConnectionsTuple.put(readKey, tt);
                }

                return false;
            }

            /*
             * Need this until the fix is available as part of 1.8.6.x grizzly.
             * See issue 491 in Grizzly,
             */
            @Override
            public void shutdown() {
                // If shutdown was called for this SelectorHandler
                if (isShutDown.getAndSet(true)) {
                    return;
                }

                stateHolder.setState(State.STOPPED);

                if (selector != null) {
                    try {
                        boolean isContinue = true;
                        while (isContinue) {
                            try {
                                for (SelectionKey selectionKey : selector.keys()) {
                                    selectionKeyHandler.close(selectionKey);
                                }

                                isContinue = false;
                            } catch (ConcurrentModificationException e) {
                            // ignore
                            }
                        }
                    } catch (ClosedSelectorException e) {
                    // If Selector is already closed - OK
                    }
                }

                try {
                    if (serverSocket != null) {
                        serverSocket.close();
                    }
                } catch (Throwable ex) {
                    logger.log(Level.SEVERE,
                            "Close server socket", ex);
                }

                try {
                    if (serverSocketChannel != null) {
                        serverSocketChannel.close();
                    }
                } catch (Throwable ex) {
                    logger.log(Level.SEVERE,
                            "Close server socket channel", ex);
                }

                try {
                    if (selector != null) {
                        selector.close();
                    }
                } catch (Throwable ex) {
                   logger.log(Level.SEVERE,
                            "Close selector", ex);
                }

                if (asyncQueueReader != null) {
                    asyncQueueReader.close();
                    asyncQueueReader = null;
                }

                if (asyncQueueWriter != null) {
                    asyncQueueWriter.close();
                    asyncQueueWriter = null;
                }

                opToRegister.clear();
                attributes = null;
            }
           
            @Override
            protected void onConnectOp(Context ctx,
                    SelectionKeyOP.ConnectSelectionKeyOP selectionKeyOp)
                    throws IOException {
                SocketAddress remoteAddress = selectionKeyOp.getRemoteAddress();

                SocketAddress localAddress = selectionKeyOp.getLocalAddress();

                CallbackHandler callbackHandler =
                        selectionKeyOp.getCallbackHandler();

                SocketChannel socketChannel = SocketChannel.open();

                socketChannel.socket().setReuseAddress(reuseAddress);

                if (localAddress != null) {
                    socketChannel.socket().bind(localAddress);
                }

                socketChannel.configureBlocking(false);

                SelectionKey key = socketChannel.register(selector,
                        SelectionKey.OP_CONNECT);

                key.attach(ExpiringCallbackHandlerSelectionKeyAttachment.create(
                        key, callbackHandler));

                boolean isConnected;

                try {
                    isConnected = socketChannel.connect(remoteAddress);
                } catch (Exception e) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE,
                                "Exception occured when tried to connect socket",
                                e.getMessage());
                    }

                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "Exception occured when tried to connect socket",
                                e);
                    }

                    // set isConnected to true to let callback handler to know about the problem happened
                    isConnected = true;
                }

                // if channel was connected immediately or exception occured
                if (isConnected) {
                    onConnectInterest(key, ctx);
                }
            }
            };

        selectorHandler.setPort(addr.getPort());

        selectorHandler.setInet(addr.getAddress());

        selectorHandler.setLinger(linger);

        selectorHandler.setLogger(logger);

        selectorHandler.setReuseAddress(true);

        selectorHandler.setSocketTimeout(keepAliveTimeoutInSeconds * 1000);

        return selectorHandler;
    }

    protected void writeMessage(ByteBuffer bb, ConnectorHandler handler,
            TargetTuple tt) throws IOException {
        if (tt.getProtocol() == SipTransports.TLS_PROT) {
            /* Make 2 configurable TODO */
            ByteBuffer secureBuffer = acquireSecureBuffer(
                    ((SSLConnectorHandler) handler).getSSLEngine().getSession().
                    getPacketBufferSize() * 2);
            secureBuffer.limit(0);
            SSLWritePreProcessor preProcessor = new SSLWritePreProcessor(
                    ((SSLConnectorHandler) handler).getSSLEngine(),
                    secureBuffer);
            ((AsyncQueueWritable) handler).writeToAsyncQueue(bb,
                    tlsAsyncWriteCallbackHandler, preProcessor);
        } else {
            ((AsyncQueueWritable) handler).writeToAsyncQueue(bb,
                    tcpUdpAsyncWriteCallbackHandler);
        }
    }

    SSLSelectorHandler createTLSEndpoint(InetSocketAddress addr) {
        int linger = -1;

        final SSLSelectorHandler selectorHandler = new SSLSelectorHandler() {

            /**
             * Intercept the accept operations and cache the associated
             * connection. This is duplication of code from the TCPSelectorhandler
             * above. TODO to refactor this.
             */
            @Override
            public boolean onAcceptInterest(SelectionKey key, Context ctx)
                    throws IOException {
                try {
                    SelectableChannel channel = acceptWithoutRegistration(key);

                    if (channel != null) {
                        configureChannel(channel);

                        SelectionKey readKey = channel.register(selector,
                                SelectionKey.OP_READ);

                        // Cache the connection.
                        TargetTuple tt = connectionManager.add(readKey, this);

                        outboundConnectionsTuple.put(readKey, tt);
                    }
                } catch (Exception e) {
                    if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "Exception onAccept ", e);
                    }
                }
                return false;
            }

            @Override
            protected void onConnectOp(Context ctx,
                    SelectionKeyOP.ConnectSelectionKeyOP selectionKeyOp)
                    throws IOException {
                try {
                    SocketAddress remoteAddress =
                            selectionKeyOp.getRemoteAddress();

                    SocketAddress localAddress =
                            selectionKeyOp.getLocalAddress();

                    CallbackHandler callbackHandler =
                            selectionKeyOp.getCallbackHandler();

                    SocketChannel socketChannel = SocketChannel.open();

                    socketChannel.socket().setReuseAddress(reuseAddress);

                    if (localAddress != null) {
                        socketChannel.socket().bind(localAddress);
                    }

                    socketChannel.configureBlocking(false);

                    SelectionKey key = socketChannel.register(selector,
                            SelectionKey.OP_CONNECT);

                    key.attach(ExpiringCallbackHandlerSelectionKeyAttachment.create(
                            key, callbackHandler));

                    boolean isConnected;

                    try {
                        isConnected = socketChannel.connect(remoteAddress);
                    } catch (Exception e) {
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE,
                                    "Exception occured when tried to connect socket",
                                    e.getMessage());
                        }
                        if (logger.isLoggable(Level.FINEST)) {
                            logger.log(Level.FINEST,
                                    "Exception occured when tried to connect socket",
                                    e);
                        }

                        // set isConnected to true to let callback handler to know about the problem happened
                        isConnected = true;
                    }

                    // if channel was connected immediately or exception occured
                    if (isConnected) {
                        onConnectInterest(key, ctx);
                    }
                } catch (Exception e) {
                    if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "Exception on connect ", e);
                    }
                    if (logger.isLoggable(Level.FINEST)){
                        logger.log(Level.FINEST, "Exception on connect ",
                                e.getMessage());
                    }
                }
            }
        };

        selectorHandler.setPort(addr.getPort());

        selectorHandler.setInet(addr.getAddress());

        selectorHandler.setLinger(linger);

        selectorHandler.setLogger(logger);

        selectorHandler.setReuseAddress(true);

        selectorHandler.setSocketTimeout(keepAliveTimeoutInSeconds * 1000);

        return selectorHandler;
    }

    UDPSelectorHandler createUdpEndpoint(InetSocketAddress addr) {
        UDPSelectorHandler selectorHandler = useDefaultUDPSelectorHandler
                ? new UDPSelectorHandler() : new ReadUDPSelectorHandler();

        selectorHandler.setPort(addr.getPort());

        selectorHandler.setInet(addr.getAddress());

        selectorHandler.setLogger(logger);

        selectorHandler.setReuseAddress(true);

        return selectorHandler;
    }

    /*
     * Thread for starting the controller
     */
    private class ControllerThread extends Thread {

        private Controller thisController;

        public ControllerThread(Controller controller) {
            thisController = controller;
        }

        public void run() {
            try {
                thisController.start();
            } catch (Exception e) {
                logger.log(Level.SEVERE,
                        "sip.stack.network.controller_death",
                        new Object[]{"tcp", "controller"});
            }
        }
    }

    /*
     * We will not use the GrizzlYnetworkmanager thread here because there
     * are use cases when the NM has to be started under non-as-start conditions,
     * For e.g listeners are disabled during startup and enabled later, in such
     * a case the NM should not start the controllers during startup but only
     * when the listeners are enabled.
     */ 
    public void run() {
        runNetworkManager();    

    }
 
    /*
     * This method starts the client and server controller in separate
     * threads. This is required to fix 1654.
     */
    public synchronized void runNetworkManager() {
        if ((clientController.getSelectorHandlers().size() > 0) &&
                !clientController.isStarted() && (!clientControllerStarted)) {
            clientPipeline.initPipeline();
            clientPipeline.startPipeline();
            (new ControllerThread(clientController)).start();
            clientControllerStarted = true;
        }
        if ((serverController.getSelectorHandlers().size() > 0) &&
                !serverController.isStarted() && (!serverControllerStarted)) {
            serverPipeline.initPipeline();
            serverPipeline.startPipeline();
            (new ControllerThread(serverController)).start();
            serverControllerStarted = true;
        }
    }

    /**
     * Return a pooled <code>ByteBuffer</code> containing the
     * SipMessage bytes.
     */
    protected final ByteBuffer toBuffer(
            SipServletMessageImpl sipServletMessage)
            throws UnsupportedEncodingException {
        ByteBuffer bb = acquireBuffer();

        sipServletMessage.toBuffer(bb);

        bb.flip();

        return bb;
    }

    protected ByteBuffer acquireBuffer() {
        return _bbPool.acquireBuffer();
    }

    protected void releaseBuffer(ByteBuffer byteBuffer) {
        _bbPool.releaseBuffer(byteBuffer);
    }

    /*
     * We need separate pool for secure buffers, because
     * the current ByteBufferPool class does not
     * allow us to have buffers of varying sizes
     */
    protected static ByteBuffer acquireSecureBuffer(int expectedsize) {
        ByteBuffer securebb = secureByteBuffersPool.poll();
        if ((securebb == null) || (securebb.capacity() < expectedsize)) {
            securebb = ByteBuffer.allocate(expectedsize);
        }
        return securebb;
    }

    protected static void releaseSecureBuffer(ByteBuffer securebb) {
        if (securebb != null) {
            securebb.clear();
            secureByteBuffersPool.offer(securebb);
        }
    }

    @Configuration(key = "javax.net.ssl.keyStore", update = UpdatePolicy.STARTUP)
    public void setSslKeyStore(String aKeyStore) {
        keyStore = aKeyStore;
    }

    @Configuration(key = "javax.net.ssl.trustStore", update =
    UpdatePolicy.STARTUP)
    public void setSslTrustStore(String aTrustStore) {
        trustStore = aTrustStore;
    }

    //grizzly pipelines and pools
    @Configuration(key = "TimeoutInSeconds", node = "/SipService/KeepAlive", update =
    UpdatePolicy.STARTUP, usage = UsagePolicy.IGNORE)
    public void setKeepAliveTimeoutInSeconds(int aKeepAliveTimeoutInSeconds) {
        keepAliveTimeoutInSeconds = aKeepAliveTimeoutInSeconds;
    }

    @Configuration(key = "ThreadCount", node = "/SipService/RequestProcessing", usage =
    UsagePolicy.IGNORE)
    public void setMaxPipelineThreads(int aMaxThreads) {
        maxPipelineThreads = aMaxThreads;

        reinitPipeline();
    }

    @Configuration(key = "sip.network.grizzly.useServerThreadPool", update =
    UpdatePolicy.STARTUP)
    public void setClientsShareServerThreadPool(boolean isShared) {
        clientsShareServerThreadPool = isShared;
    }

    //Is checked with Jeanfrancios, can be mapped to the initial-thread-count.

    //This means that the min and initial are the same!
    @Configuration(key = "InitialThreadCount", node =
    "/SipService/RequestProcessing", usage = UsagePolicy.IGNORE)
    public void setMinPipelineThreads(int aMinThreads) {
        minPipelineThreads = aMinThreads;

        reinitPipeline();
    }

    @Configuration(key = "ThreadIncrement", node =
    "/SipService/RequestProcessing", usage = UsagePolicy.IGNORE)
    public void setPipelineThreadsIncrement(int aThreadsIncrement) {
        pipelineThreadsIncrement = aThreadsIncrement;
    }

    @Configuration(key = "HeaderBufferLengthInBytes", node =
    "/SipService/RequestProcessing", usage = UsagePolicy.IGNORE)
    public void setPipelineInitialByteBufferSize(int anInitialByteBufferSize) {
        pipelineInitialByteBufferSize = anInitialByteBufferSize;
    }

    //grizzly buffers

    //@Configuration (key="sip.network.grizzly.maxCachedByteBuffer", update=UpdatePolicy.STARTUP)   
    @Configuration(key = "MaxPendingCount", node = "/SipService/ConnectionPool", usage =
    UsagePolicy.IGNORE)
    public void setMaxPendingCount(int amaxPendingCount) {
        maxPendingCount = amaxPendingCount;
    }

    @Configuration(key = "QueueSizeInBytes", node = "/SipService/ConnectionPool", usage =
    UsagePolicy.IGNORE)
    public void setQueueSizeInBytes(int aQueueSize) {
        queueSize = aQueueSize;
    }

    @Configuration(key = "RequestTimeoutInSeconds", node =
    "/SipService/RequestProcessing", usage = UsagePolicy.IGNORE)
    public void setRequestTimeoutInSeconds(int aRequestTimeOut) {
        requestTimeOut = aRequestTimeOut;
    }

    @Configuration(key = "SendBufferSizeInBytes", node =
    "/SipService/ConnectionPool", usage = UsagePolicy.IGNORE)
    public void setSendBufferSizeInBytes(int aSendBufferSize) {
        sendBufferSize = aSendBufferSize;
        _bbPool.setSendBufferSizeInBytes(aSendBufferSize);
    }

    @Configuration(key = "maxTempSelectors", node = "/SipService", usage =
    UsagePolicy.IGNORE)
    public void setMaxTmpSelectors(int maxselectors) {
        maxTmpSelectors = maxselectors;
    }

    @Configuration(key = "udpStaleConnectionsTimeout", node = "/SipService", usage = UsagePolicy.IGNORE)
    public void setUdpStaleConnectionsTimeout(long timeout) {

        staleConnectionsTimeout = timeout;
    }

    @Configuration(key = "sslHandshakeTimeout", node = "/SipService", usage = UsagePolicy.IGNORE)
    public void setSslHandshakeTimeout(int sslhandshaketimeout) {
        sslHandshakeTimeout = sslhandshaketimeout;               
    }


    @Configuration(key = "ReceiveBufferSizeInBytes", node =
    "/SipService/ConnectionPool", usage = UsagePolicy.IGNORE)
    public void setReceiveBufferSizeInBytes(int aReceiveBufferSize) {
        receiveBufferSizeInBytes = aReceiveBufferSize;
    }  
 
    /**
     * Gets the internal access to the streams handled by this connection manager.
     *
     * @return Readonly view of the existing connections.
     */
    public Set<TargetTuple> getConnectionView() {
        return connectionManager.getConnectionView();
    }
   
    class ConnectionManager {

        private ConcurrentHashMap<TargetTuple, ConnectorHandler> streams =
                new ConcurrentHashMap<TargetTuple, ConnectorHandler>();

        public ConnectionManager() {
        }

        /**
         * Gets the internal access to the streams handled by this connection manager.
         *
         * @return Readonly view of the existing connections.
         */
        public Set<TargetTuple> getConnectionView() {
            return (Set<TargetTuple>) Collections.unmodifiableSet(streams.keySet());
        }

        private TargetTuple add(SelectionKey key,
                TCPSelectorHandler selectorHandler) {
            SocketChannel socketChannel = (SocketChannel) key.channel();

            // TODO Cache object instead of invoking new on every request.
            TCPOutboundConnectorHandler outboundConnector =
                    new TCPOutboundConnectorHandler();

            outboundConnector.setSelectorHandler(selectorHandler);

            outboundConnector.setUnderlyingChannel(socketChannel);

            Socket s = socketChannel.socket();

            InetSocketAddress remoteAddress =
                    (InetSocketAddress) s.getRemoteSocketAddress();

            TargetTuple remote = new TargetTuple(SipTransports.TCP_PROT,
                    remoteAddress, s.getLocalAddress().getHostAddress());

            outboundConnector.setCallbackHandler(new SharedCallbackHandler(
                    outboundConnector, remote));

            streams.put(remote, outboundConnector);         

            return remote;
        }

        private TargetTuple add(SelectionKey key,
                SSLSelectorHandler selectorHandler) {
            SocketChannel socketChannel = (SocketChannel) key.channel();

            // TODO Cache object instead of invoking new on every request.
            TLSOutboundConnectorHandler outboundConnector =
                    new TLSOutboundConnectorHandler();

            outboundConnector.setSelectorHandler(selectorHandler);

            outboundConnector.setUnderlyingChannel(socketChannel);

            Socket s = socketChannel.socket();

            InetSocketAddress remoteAddress =
                    (InetSocketAddress) s.getRemoteSocketAddress();

            TargetTuple remote = new TargetTuple(SipTransports.TLS_PROT,
                    remoteAddress, s.getLocalAddress().getHostAddress());

            outboundConnector.setCallbackHandler(new SSLSharedCallbackHandler(
                    outboundConnector, remote));        

            streams.put(remote, outboundConnector);

            return remote;
        }

        public ConnectorHandler get(TargetTuple tt) {
            return streams.get(tt);
        }

        private void remove(TargetTuple tt) {
            ConnectorHandler handler = streams.remove(tt);

            if (handler != null) {
                try {
                    handler.close();
                } catch (Exception e) {
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "sip.network.grizzly.handler.close.error",
                                new Object[]{handler});
                    }
                }
            }
        }
   

        private TargetTuple selectBindInterface(TargetTuple tt,
                SipServletMessageImpl sipServletMessage) {           
            TargetTuple bindTT = null;
            OutboundInterface oi = sipServletMessage.getOutboundInterface();
            if (oi != null){
                bindTT = oi.getTargetTuple();
            }
            if (bindTT != null) {
                /*
                 * Resolve the external address to the corresponding
                 * bind address, note that the external address could be a
                 * local address or a LB address according to the 289 Spec
                 */
                if (externalListeners != null && bindTT.getBindIP() != null &&
                        !bindTT.getBindIP().equals("")) {
                    for (TargetTuple listener : externalListeners.keySet()) {
                        if (externalListeners.get(listener).equals(bindTT) ||
                                listener.equals(bindTT)) {
                            return listener;
                        }
                    }
                }
            }
            // pick the first external listener as default
            return sipBindingResolver.getActiveLocalContext(tt.getProtocol()).
                    getTargetTupleForProtocol(tt.getProtocol());
           
        }

        private void send(SipServletMessageImpl sipServletMessage)
                throws IOException {
            send(sipServletMessage, true, null);
        }
       
        private TargetTuple getBindTuple(SipServletMessageImpl sipServletMessage) {
            TargetTuple bindtuple = null;
            TargetTuple tt = sipServletMessage.getRemote();
            if (!tt.getIsCLBEndpoint()) {
                // rely on clb to set the bind ip address always
                bindtuple = selectBindInterface(tt, sipServletMessage);
                if (bindtuple != null) {
                    tt.setBindIp(bindtuple.getIP());
                    if (tt.getProtocol() == SipTransports.UDP_PROT) {
                        tt.setBindPort(bindtuple.getPort());
                    }
                } else {
                    if (logger.isLoggable(Level.FINEST))
                    logger.log(Level.FINEST, "Network Manager Routing without" +
                            "bind interface");
                }
            }
            return tt;
        }

        private void send(SipServletMessageImpl sipServletMessage,
                boolean useCache, ByteBuffer bb) throws IOException {

            TargetTuple tt = getBindTuple(sipServletMessage);
            //added this check to prevent FT failures
            if(staleConnectionsTimeout > 0 ) {
                if ((tt.getProtocol() == SipTransports.UDP_PROT)
                        && (staleUDPConnections.get(tt) != null)) {
                    Long time = staleUDPConnections.get(tt);
                    if ((System.currentTimeMillis() - time)
                            >= staleConnectionsTimeout) {
                        staleUDPConnections.remove(tt);
                    } else {
                        throw new ConnectException("Connection refused to: " +
                                tt.getSocketAddress());
                    }
                }
            }
            ConnectorHandler handler = null;
            if (useCache) {
                handler = streams.get(tt);
                if ((handler != null) &&
                        (handler.getUnderlyingChannel() == null ||
                        !handler.getUnderlyingChannel().isOpen() ||
                        handler.getSelectorHandler() == null ||
                        handler.getSelectorHandler().getAsyncQueueWriter() ==
                        null)) {
                    // none of these warning should ever happen
                    /*
                     * The following checks are to ensure that we do not
                     * use stale connections after a listener has been
                     * disabled and then enabled.
                     */
                    if (handler.getUnderlyingChannel() == null) {
                        if (logger.isLoggable(Level.FINEST)) {
                            logger.log(Level.FINEST,
                                    "discarding a connector with a null channel " +
                                    "for " + tt.toString());
                        }
                    }
                    if (handler.getSelectorHandler() == null) {
                        if (logger.isLoggable(Level.FINEST)) {
                            logger.log(Level.FINEST,
                                    "discarding a connector with a null " +
                                    "selector handler for " + tt.toString());
                        }
                    } else if (handler.getSelectorHandler().getAsyncQueueWriter() ==
                            null) {
                        if (logger.isLoggable(Level.FINEST)) {

                            logger.log(Level.FINEST,
                                    "discarding a connector with a null async " +
                                    "writer for " + tt.toString());
                        }
                    }

                    try {
                        handler.close();
                    } catch (Exception e) {
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE,
                                    "sip.network.grizzly.handler.close.error",
                                    new Object[]{handler});
                        }
                    }

                    connectionManager.remove(tt); //Cleanup a broken connection

                    handler = null;
                }
            }

            if (handler == null) {
                handler = createConnectorHandler(tt, sipServletMessage);
            }

            // The connection failed, returning null.
            if (handler == null && useCache) {
                if (sipServletMessage.getRetryPolicy() ==
                        SipServletMessageImpl.RetryPolicy.RESOLVE_AND_RETRY) {
                    if (sipServletMessage.getMessageType() ==
                            SipServletMessageImpl.SipMessageType.SipResponse) {
                        try {
                            resolve((SipServletResponseImpl) sipServletMessage);
                            //try again
                            tt = getBindTuple(sipServletMessage);
                            handler = createConnectorHandler(tt, sipServletMessage);
                        } catch (Exception e) {
                            logSendFailed((SipServletResponseImpl) sipServletMessage,
                                    e);
                        }
                    }
                } else {
                //then we have already tried with the first createConnectorHandler(...) invocation
                }
            }

            //after any potential retry
            if (handler == null) {
                throw new ConnectException("Connection refused to: " +
                        tt.getSocketAddress());
            }

            boolean cacheBuffer = false;
            if (bb == null) {
                sipServletMessage.toBufferInit();

                bb = toBuffer(sipServletMessage);
                cacheBuffer = true;
            }

            if (!bb.hasRemaining()) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE,
                            "sip.stack.network.invalid_bytebuffer");
                }

                return;
            }

            try {
                if (sipServletMessage.toBufferHasRemaining()) {
                    synchronized (handler.getUnderlyingChannel()) {
                        writeMessage(bb, handler, tt);
                        while (sipServletMessage.toBufferHasRemaining()) {
                            //This is a big message
                            bb = toBuffer(sipServletMessage);
                            writeMessage(bb, handler, tt);
                        }
                    }
                } else {
                    writeMessage(bb, handler, tt);
                }
            } catch (Exception ex) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE, "sip.network.grizzly.write.failed",
                            new Object[]{tt, ex.getMessage()});
                }

                try {
                    handler.close();
                } catch (Exception e) {
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST,
                                "sip.network.grizzly.handler.close.error",
                                new Object[]{handler});
                    }
                }

                connectionManager.remove(tt); //Cleanup a broken connection

                if (cacheBuffer) {
                    releaseBuffer(bb);
                }

                // Try with a non cached connection as the connection is

                // broken.
                if (useCache) {
                    if (logger.isLoggable(Level.SEVERE)) {
                        logger.log(Level.SEVERE,
                                "sip.network.grizzly.write.retry",
                                new Object[]{tt});
                    }
                    //Changed to null since message can be multiple buffers
                    messageDeliveryFailure(sipServletMessage);
                } else {
                  // ugly fix but prevents a lot of refactoring
                  if (ex instanceof RuntimeException) {
                    throw (RuntimeException)ex;
                  } else if (ex instanceof IOException) {
                    throw (IOException)ex;
                  } else {
                    throw new RuntimeException("unexpected exception type",ex);
                  }
                }
            }
        }

        /**
         * Prepares the message for retrying to deliver the message.
         *
         * @param sipServletMessage message that has failed to be delivered to UA
         * @throws IOException
         */
        private void messageDeliveryFailure(SipServletMessageImpl sipServletMessage)
                throws IOException {
            if (sipServletMessage.getRetryPolicy() ==
                    SipServletMessageImpl.RetryPolicy.RESOLVE_AND_RETRY) {
                if (sipServletMessage.getMessageType() ==
                        SipServletMessageImpl.SipMessageType.SipResponse) {
                    try {
                        resolve((SipServletResponseImpl) sipServletMessage);
                    } catch (Exception e) {
                        logSendFailed((SipServletResponseImpl) sipServletMessage,
                                e);
                    }
                }
                send(sipServletMessage, false, null);
            } else {
                if (sipServletMessage.getRetryPolicy() ==
                        SipServletMessageImpl.RetryPolicy.RETRY) {
                    //Changed to null since message can be multiple buffers
                    send(sipServletMessage, false, null);
                }
            }
        }

        /**
         * Create client <code>ConnectorHandler</code> depending on <code>TargetTuple</code>
         * @param tt <code>TargetTuple</code>
         * @return <code>ConnectorHandler</code>, or null if error happened
         * @throws java.io.IOException
         */
        private ConnectorHandler createConnectorHandler(TargetTuple tt,
                SipServletMessageImpl sipServletMessage)
                throws IOException {
            ConnectorHandler handler = null;

            int protocol = tt.getProtocol().ordinal();          
            synchronized (tt.key().intern()) {
                handler = streams.get(tt);
                       
                if (handler != null) {
                    return handler;
                }
                if (protocol == SipTransports.TCP) {
                    handler = createHandlerTCP(tt);
                } else if ((handler == null) &&
                        (protocol == SipTransports.TLS)) {
                    handler = createHandlerTLS(tt);
                } else if (handler == null) {
                    handler = createHandlerUDP(tt);
                }

                if (handler != null) {
                    SelectableChannel channel = handler.getUnderlyingChannel();

                    if ((channel != null) && channel.isOpen() && (handler.getSelectorHandler() != null) && (handler.getSelectorHandler().getAsyncQueueWriter() != null)) {
                        streams.putIfAbsent(tt, handler);
                    } else {
                      if (handler.getUnderlyingChannel() == null) {
                            if (logger.isLoggable(Level.FINEST)){
                                logger.log(
                                        Level.FINEST, "not returning a " +
                                        "connector with a null channel " +
                                        tt.toString());
                            }
                      }
                      if (handler.getSelectorHandler() == null) {
                            if (logger.isLoggable(Level.FINEST)){
                        logger.log(Level.FINEST,
                                        "not rerturning a connector with a null " +
                                        "selector handler " + tt.toString());
                            }
                      } else if (handler.getSelectorHandler().getAsyncQueueWriter() == null) {
                            if (logger.isLoggable(Level.FINEST)){
                        logger.log(Level.FINEST, "not returning a " +
                                        "connector with a null async " +
                                        "writer " + tt.toString());
                            }
                      }
                
                        return null;
                    }
                }
            }

            return handler;
        }

        private ConnectorHandler createHandlerTCP(final TargetTuple tt)
                throws IOException {
            final InetSocketAddress remote = tt.getSocketAddress();

            final TCPConnectorHandler connectorHandler =
                    (TCPConnectorHandler) clientController.acquireConnectorHandler(Protocol.TCP);

            try {
                if (tt.getBindIP() == null || tt.getBindIP().equals("")) {
                    connectorHandler.connect(remote,
                            new SharedCallbackHandler(connectorHandler, tt));
                } else {
                    connectorHandler.connect(remote, tt.getSocketBindAddress(),
                            new SharedCallbackHandler(connectorHandler, tt));
                }

                return connectorHandler;
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t.getMessage()});
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t});
                }

                return null;
            }
        }

        private final void waitOnLatch(CountDownLatch latch, int timeout,
                TimeUnit timeUnit) throws InterruptedException {
            latch.await(timeout, timeUnit);
        }

        private ConnectorHandler createHandlerTLS(final TargetTuple tt)
                throws IOException {
            final InetSocketAddress remote = tt.getSocketAddress();

            final SSLConnectorHandler connectorHandler =
                    (SSLConnectorHandler) new SSLConnectorHandler(sslContext);

            connectorHandler.setController(clientController);

            final CountDownLatch handshakeDoneLatch = new CountDownLatch(1);

            SSLCallbackHandler<Context> sslCallbackHander = new SSLSharedCallbackHandler(connectorHandler,
                    tt, handshakeDoneLatch);
            try {
                if (tt.getBindIP() == null || tt.getBindIP().equals("")){
                    connectorHandler.connect(remote, sslCallbackHander);
                } else {
                    connectorHandler.connect(remote, tt.getSocketBindAddress(),
                            sslCallbackHander);
                }

                // This makes sure the handshake operations is completed

                // before marking this connectorHandler as ready. Might

                // be a performance bottleneck.
                waitOnLatch(handshakeDoneLatch, sslHandshakeTimeout, TimeUnit.SECONDS);
                return connectorHandler;
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t.getMessage()});
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t});
                }

                return null;
            }
        }

        private ConnectorHandler createHandlerUDP(final TargetTuple tt)
                throws IOException {
            final InetSocketAddress remote = tt.getSocketAddress();

            final UDPConnectorHandler connectorHandler =
                    (UDPConnectorHandler) clientController.acquireConnectorHandler(Protocol.UDP);

            try {
                if (!disableUDPPortForClient) {
                    if (tt.getBindIP() == null || tt.getBindIP().equals("")){
                         connectorHandler.connect(remote, localUDPSocketAddress,
                            new SharedCallbackHandler(connectorHandler, tt));
                    } else {
                         connectorHandler.connect(remote, tt.getSocketBindAddress(),
                            new SharedCallbackHandler(connectorHandler, tt));
                    }
                } else {
                    if (tt.getBindIP() == null || tt.getBindIP().equals("")){
                        connectorHandler.connect(remote,
                            new SharedCallbackHandler(connectorHandler, tt));
                    } else {
                        connectorHandler.connect(remote, tt.getSocketBindAddress(),
                            new SharedCallbackHandler(connectorHandler, tt));
                    }
                }

                return connectorHandler;
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t.getMessage()});
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{remote, t});
                }

                return null;
            }
        }
    }

    /**
     *  Selector Handler for UDP which does the following
     *
     *  1. Sets the datagramchannel receive buffer size.
     *  2. Reads data in the selector handler.
     *  3. Continuously reads UDP datagrams from the datagram
     *  channel until there are none left in the buffer.
     *  This is to match the UDP behaviour which is bursty in
     *  nature. Routers typically buffer UDPDatagrams before
     *  dispatching them and the UDP stack in the OS also
     *  waits generates the read event only after certain
     *  number of datagrams are accumulated in the buffer.
     */
    class ReadUDPSelectorHandler extends UDPSelectorHandler {

        private static final int DEFAULT_BUFFER_SIZE = 8 * 1024 * 1024;
        private SocketAddress socketAddress = null;
        private boolean setBufferSize = false;

        public ReadUDPSelectorHandler() {
        }

        public ReadUDPSelectorHandler(boolean isclient) {
            super(isclient);
        }

        @Override
        public void preSelect(Context ctx) throws IOException {
            super.preSelect(ctx);

            if (!setBufferSize && (getRole() != Role.CLIENT)) {
                resizeSocketBuffer(datagramSocket);

                setBufferSize = true;
            }
        }

        protected void resizeSocketBuffer(DatagramSocket dgsocket) {
            try {
                int buffsize = (receiveBufferSizeInBytes <= 0)
                        ? DEFAULT_BUFFER_SIZE : receiveBufferSizeInBytes;

                dgsocket.setReceiveBufferSize(buffsize);

                dgsocket.setSendBufferSize(buffsize);

                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST,
                            "UDP buffer sizes set to " + buffsize + " for " +
                            dgsocket);
                }
            } catch (Exception e) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "Unable to set buffer size ",
                            e.getMessage());
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER, "Unable to set buffer size ", e);
                }
            }
        }

        @Override
        public boolean onReadInterest(final SelectionKey key,
                final Context ctx)
                throws IOException {
            Object attach = SelectionKeyAttachment.getAttachment(key);

            ByteBuffer buff = null;

            final Context context = pollContext(ctx, key,
                    Context.OpType.OP_READ);

            try {
                buff = acquireBuffer();

                socketAddress = ((DatagramChannel) key.channel()).receive(buff);

                if (socketAddress != null) {
                    context.setAttribute(ReadFilter.UDP_SOCKETADDRESS,
                            socketAddress);

                    context.setAttribute(GrizzlyNetworkManager.UDP_BUFFER, buff);

                    context.setPipeline(pipeline());
                } else {
                    ctx.getController().returnContext(context);

                    releaseBuffer(buff);

                    return false;
                }
            } catch (Exception e) {
                ctx.getController().returnContext(context);

                releaseBuffer(buff);

                /*
                 * Mostly this will be a port undreacahable exception
                 * and the key has to be cancelled
                 */
                // added this check to prevent FT failures
                if(staleConnectionsTimeout > 0 ) {
                    if (getRole() == Role.CLIENT) {
                        DatagramChannel channel = ((DatagramChannel) key.channel());                    TargetTuple tt = new TargetTuple(SipTransports.UDP_PROT,
                            channel.socket().getInetAddress().getHostAddress(),
                            channel.socket().getPort());
                        if (staleUDPConnections.get(tt) == null){
                            staleUDPConnections.put(tt, System.currentTimeMillis());
                        }
                    }
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "UDP read exception ", e.getMessage());
                }

                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST, "UDP read exception ", e);
                }

                return false;
            }

            if (asyncQueueReader.isAsyncQueueReaderEnabledFor(key)) {
                invokeAsyncQueueReader(context);
            } else if (attach instanceof CallbackHandler) {
                invokeCallbackHandler((CallbackHandler) attach, context);
            } else {
                try {
                    context.execute(ProtocolChainContextTask.poll());
                } catch (Exception r) {
                    ctx.getController().returnContext(context);

                    releaseBuffer(buff);

                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "Cannot execute ",
                                r.getMessage());
                    }

                    if (logger.isLoggable(Level.FINER)) {
                        logger.log(Level.FINER, "Cannot execute ", r);
                    }
                }
            }

            return false;
        }

        @Override
        public boolean onConnectInterest(final SelectionKey key, Context ctx)
                throws IOException {
            if (key != null) {
                resizeSocketBuffer(((DatagramChannel) key.channel()).socket());
            }

            return super.onConnectInterest(key, ctx);
        }

        @Override
        protected void onConnectOp(Context ctx,
                SelectionKeyOP.ConnectSelectionKeyOP selectionKeyOp)
                throws IOException {
            SocketAddress remoteAddress = selectionKeyOp.getRemoteAddress();

            SocketAddress localAddress = selectionKeyOp.getLocalAddress();

            CallbackHandler callbackHandler =
                    selectionKeyOp.getCallbackHandler();

            final DatagramChannel datagramChnl = DatagramChannel.open();

            datagramChnl.socket().setReuseAddress(reuseAddress);

            if (localAddress != null) {
                datagramChnl.socket().bind(localAddress);
            }

            datagramChnl.configureBlocking(false);

            datagramChnl.connect(remoteAddress);

            SelectionKey key = datagramChnl.register(selector,
                    SelectionKey.OP_READ | SelectionKey.OP_WRITE);

            key.attach(ExpiringCallbackHandlerSelectionKeyAttachment.create(
                    key, callbackHandler));

            onConnectInterest(key, ctx);
        }
    }

    class SharedSelectionKeyHandler extends DefaultSelectionKeyHandler {

        @Override
        public void cancel(SelectionKey key) {
            super.cancel(key);

            TargetTuple tt = outboundConnectionsTuple.remove(key);

            if (tt != null) {
                connectionManager.remove(tt);
            }
        }
    }

    class SharedReadFilter extends ReadFilter {

        private int bufferSize = DEFAULT_RECEIVE_BB_SIZE;

        @Override
        public boolean execute(Context ctx) throws IOException {
            SelectableChannel channel = ctx.getSelectionKey().channel();

            // Make sure we remove that attribute in case the

            // ProtocolChain is not the default one.
            ctx.removeAttribute(ProtocolFilter.SUCCESSFUL_READ);

            /*
             * Read filter should be executed only for READ or READ_WRITE
             * operations and never for WRITE. Printing a fine message.
             */
            if (ctx.getCurrentOpType() == Context.OpType.OP_WRITE) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.filter.write.warning");
                }

                return false;
            }

            if (!channel.isOpen()) {
                ctx.setKeyRegistrationState(KeyRegistrationState.CANCEL);

                return false;
            }

            WorkerThread workerThread = (WorkerThread) Thread.currentThread();

            // Re-size the ByteBuffer based on the underlying OS buffer.
            if (workerThread.getByteBuffer().capacity() != bufferSize) {
                if (receiveBufferSizeInBytes <= 0) {
                    try {
                        if (ctx.getProtocol() == Protocol.TCP) {
                            bufferSize = ((SocketChannel) channel).socket().
                                    getReceiveBufferSize();
                        } else {
                            bufferSize = ((DatagramChannel) channel).socket().
                                    getReceiveBufferSize();
                        }
                    } catch (IOException ex) {
                        if (logger.isLoggable(Level.WARNING)) {
                            logger.log(Level.WARNING,
                                    "sip.stack.network.resize_buffer_failed_bad_Socket_state");

                            logger.log(Level.WARNING, ex.getMessage(), ex);
                        }

                        bufferSize = DEFAULT_RECEIVE_BB_SIZE;
                    }
                } else {
                    bufferSize = receiveBufferSizeInBytes;
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "sip.stack.network.receivee_buffer",
                            bufferSize);
                }

                workerThread.setByteBuffer(ByteBuffer.allocate(bufferSize));
            }

            boolean invokeNextFilter = super.execute(ctx);

            if (!invokeNextFilter) {
                TargetTuple tt = (TargetTuple) ctx.getAttribute("tt");

                if (tt != null) {
                    connectionManager.remove(tt);
                }
            }

            return invokeNextFilter;
        }

        @Override
        public boolean postExecute(Context ctx) throws IOException {
            Boolean isClientExecution =
                    (Boolean) ctx.removeAttribute(IS_CLIENT_EXECUTION);

            /*
             * Read filter should be executed only for READ or READ_WRITE
             * operations and never for WRITE.Print log in fine.
             */
            if (ctx.getCurrentOpType() == Context.OpType.OP_WRITE) {
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.filter.write.warning");
                }

                return true;
            }

            // isClientExecution will be null when this filter is used

            // as a server side component, and not null when used from a

            // client side component. When used by a client side component,

            // do not override the decision of closing or not the SelectionKey
            if ((isClientExecution == null) &&
                    (ctx.getProtocol() == Controller.Protocol.UDP) &&
                    (ctx.getKeyRegistrationState() !=
                    Context.KeyRegistrationState.REGISTER)) {
                // Main UDP listener should never be closed!
                ctx.setKeyRegistrationState(Context.KeyRegistrationState.REGISTER);
            } else if ((isClientExecution != null) &&
                    (ctx.getProtocol() == Controller.Protocol.UDP) &&
                    (!disableUDPPortForClient)) {
                /**
                 *  When the UDP client socket is bound to the local address to
                 *  ensure that outgoing requests are from the same UDP (server)
                 *  port, the response also have to be read from the same UDP
                 *  datagram channel from which the request was sent. The response
                 *  does not come to the main UDP channel. NIO seems to be
                 *  processing datagrams based on the soure port.
                 *  The problem here is that since this filter (chain) was
                 *  executed from the callback
                 *  handler and the interest ops cleared, we have to register the
                 *  read ops here so that we are able to read further messages
                 *  from the client.
                 */
                ctx.getSelectorHandler().register(ctx.getSelectionKey(),
                        SelectionKey.OP_READ);
            }

            return super.postExecute(ctx);
        }
    }

    class ClientCertSSLReadFilter extends SSLReadFilter {

        private int bufferSize = DEFAULT_RECEIVE_BB_SIZE;

        @Override
        public boolean execute(Context ctx) throws IOException {
            // Make sure we remove that attribute in case the
            // ProtocolChain is not the default one.
            ctx.removeAttribute(ProtocolFilter.SUCCESSFUL_READ);

            SelectableChannel channel = ctx.getSelectionKey().channel();

            if (ctx.getCurrentOpType() == Context.OpType.OP_WRITE) {
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.filter.write.warning");
                }

                return false;
            }

            if (!channel.isOpen()) {
                ctx.setKeyRegistrationState(KeyRegistrationState.CANCEL);

                return false;
            }
            WorkerThread workerThread = (WorkerThread) Thread.currentThread();
            if (workerThread.getByteBuffer().capacity() != bufferSize) {
                if (receiveBufferSizeInBytes <= 0) {
                    try {
                        bufferSize = ((SocketChannel) channel).socket().
                                getReceiveBufferSize();
                    } catch (IOException ex) {
                        if (logger.isLoggable(Level.WARNING)) {
                            logger.log(Level.WARNING,
                                    "sip.stack.network.resize_buffer_failed_bad_Socket_state");
                            logger.log(Level.WARNING, ex.getMessage(), ex);
                        }
                        bufferSize = DEFAULT_RECEIVE_BB_SIZE;
                    }
                } else {
                    bufferSize = receiveBufferSizeInBytes;
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "sip.stack.network.receivee_buffer",
                            bufferSize);
                }
                workerThread.setByteBuffer(ByteBuffer.allocate(bufferSize));
                inputBBSize = bufferSize;
            }

            boolean continueExecution = super.execute(ctx);
            if (continueExecution) {
                Object[] x509Cert = null;
                ByteBuffer byteBuffer =
                        ((WorkerThread) Thread.currentThread()).getByteBuffer();

                ByteBuffer bb = acquireBuffer();

                ((WorkerThread) Thread.currentThread()).setByteBuffer(bb);

                try {
                    x509Cert = doPeerCertificateChain(ctx.getSelectionKey(),
                            isNeedClientAuth());
                } catch (IOException ioe) {
                    if (logger.isLoggable(Level.WARNING)) {
                        logger.log(Level.WARNING,
                                "sip.stack.network.client_cert_error");

                        logger.log(Level.WARNING, ioe.getMessage(), ioe);
                    }
                } finally {
                    releaseBuffer(bb);

                    ((WorkerThread) Thread.currentThread()).setByteBuffer(byteBuffer);
                    
                }

                ctx.setAttribute(SIP_CERTS, x509Cert);
            }
            try {
                TargetTuple tt =
                        outboundConnectionsTuple.get(ctx.getSelectionKey());
                if (tt != null) {
                    TLSOutboundConnectorHandler oHandler =
                            (TLSOutboundConnectorHandler) connectionManager.get(tt);
                    if ((oHandler != null) && (oHandler.getSSLEngine() == null)) {
                        oHandler.setSSLEngine(((WorkerThread) Thread.currentThread()).getSSLEngine());
                    }
                }
            } catch (Exception ssle) {
                if (logger.isLoggable(Level.FINE)){
                    logger.log(Level.FINE, "Cannot set ssl engine " +
                            ssle.getMessage());
                }
            }
            return continueExecution;
        }

        @Override
        public boolean postExecute(Context ctx) throws IOException {
            if (ctx.getCurrentOpType() == Context.OpType.OP_WRITE) {
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.filter.write.warning");
                }

                return true;
            }

            return super.postExecute(ctx);
        }
    }

    class SimpleProtocolChainInstanceHandler
            implements ProtocolChainInstanceHandler {

        ProtocolChain protocolChain = null;

        public SimpleProtocolChainInstanceHandler(ProtocolChain aProtocolChain) {
            protocolChain = aProtocolChain;
        }

        public ProtocolChain getProtocolChain() {
            return protocolChain;
        }

        public ProtocolChain poll() {
            return protocolChain;
        }

        public boolean offer(ProtocolChain instance) {
            return true;
        }
    }

    class SharedCallbackHandler implements CallbackHandler<Context>,
            CallbackHandlerDescriptor {

        protected ConnectorHandler connectorHandler;
        protected TargetTuple targetTuple;

        public SharedCallbackHandler(ConnectorHandler connectorHandler,
                TargetTuple targetTuple) {
            this.connectorHandler = connectorHandler;

            this.targetTuple = targetTuple;
        }

        public void onConnect(IOEvent<Context> ioEvent) {
            SelectionKey key = ioEvent.attachment().getSelectionKey();

            try {
                connectorHandler.finishConnect(key);

                key.interestOps(SelectionKey.OP_READ);
            } catch (Exception ex) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{targetTuple.getSocketAddress(), ex.getMessage()});
                }
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{targetTuple.getSocketAddress(), ex});
                }

                connectionManager.remove(targetTuple);

                ioEvent.attachment().getSelectorHandler().getSelectionKeyHandler().
                        cancel(key);
            }
        }

        public void onRead(IOEvent<Context> ioEvent) {
            try {
                Context ctx = ioEvent.attachment();

                ctx.setAttribute(IS_CLIENT_EXECUTION, true);

                SelectionKey key = ctx.getSelectionKey();

                if (!key.isValid()) {
                    connectionManager.remove(targetTuple);

                    return;
                }

                ctx.setAttribute("tt", targetTuple);

                // disable OP_READ on key before doing anything else
                if ((targetTuple.getProtocol() != SipTransports.UDP_PROT) ||
                        useDefaultUDPSelectorHandler) {
                    key.interestOps(key.interestOps() &
                            (~SelectionKey.OP_READ));
                }
                if (!key.channel().isOpen()) {
                    if (logger.isLoggable(Level.FINER)) {
                        logger.log(Level.FINER, "ChannelClosed " + key.channel());
                    }
                    return;
                }
                ctx.getProtocolChain().execute(ioEvent.attachment());
            } catch (Throwable e) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE,
                            "sip.stack.network.connection_read_failed",
                            new Object[]{"tcp"});
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER, e.getMessage(), e);
                }
            }
        }

        public void onWrite(IOEvent<Context> ioEvent) {
        }

        public boolean isRunInSeparateThread(OpType opType) {
            return opType != OpType.OP_CONNECT;
        }
    }

    class SSLSharedCallbackHandler extends SharedCallbackHandler
            implements SSLCallbackHandler<Context> {

        protected ByteBuffer handshakeAppBuffer;
        protected CountDownLatch handshakeLatch;

        public SSLSharedCallbackHandler(SSLConnectorHandler connectorHandler,
                TargetTuple targetTuple) {
            super(connectorHandler, targetTuple);
        }

        public SSLSharedCallbackHandler(ConnectorHandler connectorHandler,
                TargetTuple targetTuple, CountDownLatch handshakeLatch) {
            super(connectorHandler, targetTuple);

            this.handshakeLatch = handshakeLatch;
        }

        @Override
        public void onConnect(IOEvent<Context> ioEvent) {
            SSLConnectorHandler sslConnectorHandler =
                    (SSLConnectorHandler) this.connectorHandler;

            SelectionKey key = ioEvent.attachment().getSelectionKey();

            try {
                sslConnectorHandler.finishConnect(key);

                WorkerThread workerThread =
                        (WorkerThread) Thread.currentThread();

                workerThread.setSSLEngine(sslConnectorHandler.getSSLEngine());

                ThreadAttachment attachment =
                        workerThread.updateAttachment(Mode.SSL_ENGINE);

                key.attach(attachment);

                handshakeAppBuffer = acquireBuffer();
                boolean shake =
                        sslConnectorHandler.handshake(handshakeAppBuffer, true);
                if (shake) {                                    
                    ioEvent.attachment().getSelectorHandler().register(key,
                        SelectionKey.OP_READ);
                else {
                    ioEvent.attachment().getSelectorHandler().
                            getSelectionKeyHandler().cancel(key);
                }
                onHandshake(ioEvent);
            } catch (Exception ex) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{targetTuple.getSocketAddress(), ex.getMessage()});
                }
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER,
                            "sip.network.grizzly.connect.failed",
                            new Object[]{targetTuple.getSocketAddress(), ex});
                }

                releaseBuffer(handshakeAppBuffer);

                handshakeAppBuffer = null;

                connectionManager.remove(targetTuple);

                ioEvent.attachment().getSelectorHandler().getSelectionKeyHandler().
                        cancel(key);
                handshakeLatch.countDown();
            }
        }

        @Override
        public void onRead(IOEvent<Context> ioEvent) {
            try {
                Context ctx = ioEvent.attachment();
                ctx.setAttribute(IS_CLIENT_EXECUTION, true);

                SelectionKey key = ctx.getSelectionKey();

                if (!key.isValid()) {
                    connectionManager.remove(targetTuple);

                    return;
                }

                ctx.setAttribute("tt", targetTuple);

                // disable OP_READ on key before doing anything else
                key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));

                ctx.getProtocolChain().execute(ioEvent.attachment());
            } catch (Throwable e) {
                if (logger.isLoggable(Level.SEVERE)) {
                    logger.log(Level.SEVERE,
                            "sip.stack.network.connection_read_failed",
                            new Object[]{"tcp"});
                }

                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER, e.getMessage(), e);
                }
            }
        }

        // After handshake completes
        public void onHandshake(IOEvent<Context> ioEvent) {
            releaseBuffer(handshakeAppBuffer);

            handshakeAppBuffer = null;

            handshakeLatch.countDown();
        }
    }

    /**
     * AsyncWriteCallbackHandler, which releases <code>ByteBuffer</code>s, allocated
     * to be used with AsyncWriteQueue
     */
    class SharedAsyncWriteCallbackHandler
            implements AsyncWriteCallbackHandler {

        public void onWriteCompleted(SelectionKey key,
                AsyncWriteQueueRecord record) {

            if (logger.isLoggable(Level.FINEST)) {
                log(key, record.getByteBuffer(), null);
            }
            releaseBuffer(record.getByteBuffer());
        }

        protected void log(SelectionKey key, ByteBuffer byteBuffer, Exception ex) {
            // Paranoid null check, we do not want to fail in a fine log.
            if ((key != null) && (key.channel() != null) && (byteBuffer != null)) {
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < byteBuffer.position(); i++) {
                    sb.append((char) byteBuffer.get(i));
                }
                SelectableChannel channel = key.channel();
                if (channel instanceof DatagramChannel) {
                    DatagramSocket sock = ((DatagramChannel) channel).socket();
                    if (sock != null) {
                      if (ex == null) {
                            if (logger.isLoggable(Level.FINEST)){
                        logger.log(Level.FINEST, "sip.network.grizzly.out",
                            new Object[]{sock.getRemoteSocketAddress(),
                            sock.getLocalSocketAddress(),
                            sb.toString()
                        });
                            }
                      } else {
                            if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "sip.network.udp.send.failed", // move to logstrings
                            new Object[]{sock.getRemoteSocketAddress(),
                            sock.getLocalSocketAddress(),
                            sb.toString(),
                            ex
                        });
                            }
                      }
                    } else {
                      // What should we log if we ever get here?
                      if (ex == null) {
                            if (logger.isLoggable(Level.FINEST)){
                        logger.log(Level.FINEST, "sip.network.grizzly.out",
                            new Object[]{"",
                            "",
                            sb.toString()
                        });
                            }
                      } else {
                            if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "sip.network.send.failed.socket_null", // move to logstrings
                            new Object[]{
                            sb.toString(),
                            ex
                        });
                            }
                      }
                    }
                } else {
                    Socket sock = ((SocketChannel) channel).socket();
                    if (sock != null) {
                      if (ex == null) {
                            if (logger.isLoggable(Level.FINEST)){

                        logger.log(Level.FINEST, "sip.network.grizzly.out",
                            new Object[]{sock.getRemoteSocketAddress(),
                            sock.getLocalSocketAddress(),
                            sb.toString()
                        });
                            }
                      } else {
                            if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "sip.network.tcp.send.failed", // move to logstrings
                            new Object[]{sock.getRemoteSocketAddress(),
                            sock.getLocalSocketAddress(),
                            sb.toString(),
                            ex
                        });
                            }
                      }
                    } else {
                        if (ex == null) {
                            if (logger.isLoggable(Level.FINEST)){
                            logger.log(Level.FINEST, "sip.network.grizzly.out",
                                    new Object[]{"",
                                "",
                                sb.toString()
                            });
                            }
                        } else {
                            if (logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "sip.network.send.failed.socket_null", // move to logstrings
                            new Object[]{
                            sb.toString(),
                            ex
                        });
                            }
                      }                     
                    }
                }
            }
        }

        public void onIOException(IOException exception, SelectionKey key,
                ByteBuffer byteBuffer, Queue<AsyncWriteQueueRecord> queue) {
            releaseBuffer(byteBuffer);

            for (AsyncWriteQueueRecord record : queue) {
                log(key, record.getByteBuffer(), exception); // or should this move up??

                Reporter reporter = getReporter();
                if (reporter != null) {
                    ByteBuffer queueByteBuffer = record.getByteBuffer();
                    StringBuffer sb = new StringBuffer();
                    if (queueByteBuffer != null) {
                        for (int i = 0; i < queueByteBuffer.position(); i++) {
                            sb.append((char) queueByteBuffer.get(i));
                        }
                    }
                    smiLogWriteFailure(sb.toString(), exception);
                }

                releaseBuffer(record.getByteBuffer());
            }
           
            // What about releasing the handler itself here????
            // but how???
        }
    }

    class TlsSharedAsyncWriteCallbackHandler extends SharedAsyncWriteCallbackHandler {

        @Override
        public void onWriteCompleted(SelectionKey key,
                AsyncWriteQueueRecord record) {
            super.onWriteCompleted(key, record);
            AsyncQueueDataProcessor preprocessor = record.getWritePreProcessor();
            if (preprocessor != null) {
                releaseSecureBuffer(preprocessor.getInternalByteBuffer());
            }
        }

        @Override
        public void onIOException(IOException exception, SelectionKey key,
                ByteBuffer byteBuffer, Queue<AsyncWriteQueueRecord> queue) {
            releaseBuffer(byteBuffer);
            for (AsyncWriteQueueRecord record : queue) {
                releaseBuffer(record.getByteBuffer());
                AsyncQueueDataProcessor preprocessor =
                        record.getWritePreProcessor();
                if (preprocessor != null) {
                    releaseSecureBuffer(preprocessor.getInternalByteBuffer());
                }
            }
        }
    }
}
TOP

Related Classes of com.ericsson.ssa.container.GrizzlyNetworkManager$TlsSharedAsyncWriteCallbackHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.