/*
* 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());
}
}
}
}
}