/***************************************************************************
* Copyright (C) 2006-2009 by Fabrizio Montesi <famontesi@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
package jolie.net;
import jolie.net.ports.OutputPort;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import jolie.Interpreter;
import jolie.JolieThread;
import jolie.lang.Constants;
import jolie.net.ext.CommChannelFactory;
import jolie.net.ext.CommListenerFactory;
import jolie.net.ext.CommProtocolFactory;
import jolie.net.ports.InputPort;
import jolie.net.protocols.CommProtocol;
import jolie.process.Process;
import jolie.runtime.FaultException;
import jolie.runtime.InputOperation;
import jolie.runtime.InvalidIdException;
import jolie.runtime.OneWayOperation;
import jolie.runtime.TimeoutHandler;
import jolie.runtime.Value;
import jolie.runtime.VariablePath;
import jolie.runtime.correlation.CorrelationError;
import jolie.runtime.typing.TypeCheckingException;
/**
* Handles the communications mechanisms for an Interpreter instance.
*
* Each CommCore is related to an Interpreter, and each Interpreter owns one and only CommCore instance.
*
* @author Fabrizio Montesi
*/
public class CommCore
{
private final Map< String, CommListener > listenersMap = new HashMap< String, CommListener >();
private final ThreadGroup threadGroup;
private static final Logger logger = Logger.getLogger( "JOLIE" );
private final int connectionsLimit;
// private final int connectionCacheSize;
private final Interpreter interpreter;
// Location URI -> Protocol name -> Persistent CommChannel object
private final Map< URI, Map< String, CommChannel > > persistentChannels =
new HashMap< URI, Map< String, CommChannel > >();
private void removePersistentChannel( URI location, String protocol, Map< String, CommChannel > protocolChannels )
{
protocolChannels.remove( protocol );
if ( protocolChannels.isEmpty() ) {
persistentChannels.remove( location );
}
}
private void removePersistentChannel( URI location, String protocol, CommChannel channel )
{
if ( persistentChannels.containsKey( location ) ) {
if ( persistentChannels.get( location ).get( protocol ) == channel ) {
removePersistentChannel( location, protocol, persistentChannels.get( location ) );
}
}
}
public CommChannel getPersistentChannel( URI location, String protocol )
{
CommChannel ret = null;
synchronized( persistentChannels ) {
Map< String, CommChannel > protocolChannels = persistentChannels.get( location );
if ( protocolChannels != null ) {
ret = protocolChannels.get( protocol );
if ( ret != null ) {
if ( ret.lock.tryLock() ) {
if ( ret.isOpen() ) {
/*
* We are going to return this channel, but first
* check if it supports concurrent use.
* If not, then others should not access this until
* the caller is finished using it.
*/
//if ( ret.isThreadSafe() == false ) {
removePersistentChannel( location, protocol, protocolChannels );
//} else {
// If we return a channel, make sure it will not timeout!
ret.setTimeoutHandler( null );
//if ( ret.timeoutHandler() != null ) {
//interpreter.removeTimeoutHandler( ret.timeoutHandler() );
// ret.setTimeoutHandler( null );
//}
//}
ret.lock.unlock();
} else { // Channel is closed
removePersistentChannel( location, protocol, protocolChannels );
ret.lock.unlock();
ret = null;
}
} else { // Channel is busy
removePersistentChannel( location, protocol, protocolChannels );
ret = null;
}
}
}
}
return ret;
}
private void setTimeoutHandler( final CommChannel channel, final URI location, final String protocol )
{
/*if ( channel.timeoutHandler() != null ) {
interpreter.removeTimeoutHandler( channel.timeoutHandler() );
}*/
final TimeoutHandler handler = new TimeoutHandler( interpreter.persistentConnectionTimeout() ) {
@Override
public void onTimeout()
{
try {
synchronized( persistentChannels ) {
if ( channel.timeoutHandler() == this ) {
removePersistentChannel( location, protocol, channel );
channel.close();
channel.setTimeoutHandler( null );
}
}
} catch( IOException e ) {
interpreter.logSevere( e );
}
}
};
channel.setTimeoutHandler( handler );
interpreter.addTimeoutHandler( handler );
}
public void putPersistentChannel( URI location, String protocol, final CommChannel channel )
{
synchronized( persistentChannels ) {
Map< String, CommChannel > protocolChannels = persistentChannels.get( location );
if ( protocolChannels == null ) {
protocolChannels = new HashMap< String, CommChannel >();
persistentChannels.put( location, protocolChannels );
}
// Set the timeout
setTimeoutHandler( channel, location, protocol );
// Put the protocol in the cache (may overwrite another one)
protocolChannels.put( protocol, channel );
/*if ( protocolChannels.size() <= connectionCacheSize && protocolChannels.containsKey( protocol ) == false ) {
// Set the timeout
setTimeoutHandler( channel );
// Put the protocol in the cache
protocolChannels.put( protocol, channel );
} else {
try {
if ( protocolChannels.get( protocol ) != channel ) {
channel.close();
} else {
setTimeoutHandler( channel );
}
} catch( IOException e ) {
interpreter.logWarning( e );
}
}*/
}
}
/**
* Returns the Interpreter instance this CommCore refers to.
* @return the Interpreter instance this CommCore refers to
*/
public Interpreter interpreter()
{
return interpreter;
}
/**
* Constructor.
* @param interpreter the Interpreter to refer to for this CommCore operations
* @param connectionsLimit if more than zero, specifies an upper bound to the connections handled in parallel.
* @param connectionsCacheSize specifies an upper bound to the persistent output connection cache.
* @throws java.io.IOException
*/
public CommCore( Interpreter interpreter, int connectionsLimit /*, int connectionsCacheSize */ )
throws IOException
{
this.interpreter = interpreter;
this.localListener = new LocalListener( interpreter );
this.connectionsLimit = connectionsLimit;
// this.connectionCacheSize = connectionsCacheSize;
this.threadGroup = new ThreadGroup( "CommCore-" + interpreter.hashCode() );
if ( connectionsLimit > 0 ) {
executorService = Executors.newFixedThreadPool( connectionsLimit, new CommThreadFactory() );
} else {
executorService = Executors.newCachedThreadPool( new CommThreadFactory() );
}
//TODO make socket an extension, too?
CommListenerFactory listenerFactory = new SocketListenerFactory( this );
listenerFactories.put( "socket", listenerFactory );
CommChannelFactory channelFactory = new SocketCommChannelFactory( this );
channelFactories.put( "socket", channelFactory );
}
/**
* Returns the Logger used by this CommCore.
* @return the Logger used by this CommCore
*/
public Logger logger()
{
return logger;
}
/**
* Returns the connectionsLimit of this CommCore.
* @return the connectionsLimit of this CommCore
*/
public int connectionsLimit()
{
return connectionsLimit;
}
public ThreadGroup threadGroup()
{
return threadGroup;
}
private final Collection< Process > protocolConfigurations = new LinkedList< Process > ();
public Collection< Process > protocolConfigurations()
{
return protocolConfigurations;
}
public CommListener getListenerByInputPortName( String serviceName )
{
return listenersMap.get( serviceName );
}
private final Map< String, CommChannelFactory > channelFactories =
new HashMap< String, CommChannelFactory > ();
private CommChannelFactory getCommChannelFactory( String name )
throws IOException
{
CommChannelFactory factory = channelFactories.get( name );
if ( factory == null ) {
factory = interpreter.getClassLoader().createCommChannelFactory( name, this );
if ( factory != null ) {
channelFactories.put( name, factory );
}
}
return factory;
}
public CommChannel createCommChannel( URI uri, OutputPort port )
throws IOException
{
String medium = uri.getScheme();
CommChannelFactory factory = getCommChannelFactory( medium );
if ( factory == null ) {
throw new UnsupportedCommMediumException( medium );
}
return factory.createChannel( uri, port );
}
private final Map< String, CommProtocolFactory > protocolFactories =
new HashMap< String, CommProtocolFactory > ();
public CommProtocolFactory getCommProtocolFactory( String name )
throws IOException
{
CommProtocolFactory factory = protocolFactories.get( name );
if ( factory == null ) {
factory = interpreter.getClassLoader().createCommProtocolFactory( name, this );
if ( factory != null ) {
protocolFactories.put( name, factory );
}
}
return factory;
}
public CommProtocol createOutputCommProtocol( String protocolId, VariablePath configurationPath, URI uri )
throws IOException
{
CommProtocolFactory factory = getCommProtocolFactory( protocolId );
if ( factory == null ) {
throw new UnsupportedCommProtocolException( protocolId );
}
return factory.createOutputProtocol( configurationPath, uri );
}
public CommProtocol createInputCommProtocol( String protocolId, VariablePath configurationPath, URI uri )
throws IOException
{
CommProtocolFactory factory = getCommProtocolFactory( protocolId );
if ( factory == null ) {
throw new UnsupportedCommProtocolException( protocolId );
}
return factory.createInputProtocol( configurationPath, uri );
}
private final Map< String, CommListenerFactory > listenerFactories =
new HashMap< String, CommListenerFactory > ();
private final LocalListener localListener;
public LocalCommChannel getLocalCommChannel()
{
return new LocalCommChannel( interpreter, localListener );
}
public LocalCommChannel getLocalCommChannel( CommListener listener )
{
return new LocalCommChannel( interpreter, listener );
}
private CommListenerFactory getCommListenerFactory( String name )
throws IOException
{
CommListenerFactory factory = listenerFactories.get( name );
if ( factory == null ) {
factory = interpreter.getClassLoader().createCommListenerFactory( name, this );
if ( factory != null ) {
listenerFactories.put( name, factory );
}
}
return factory;
}
public LocalListener localListener()
{
return localListener;
}
public void addLocalInputPort( InputPort inputPort )
throws IOException
{
localListener.mergeInterface( inputPort.getInterface() );
localListener.addAggregations( inputPort.aggregationMap() );
localListener.addRedirections( inputPort.redirectionMap() );
listenersMap.put( inputPort.name(), localListener );
}
/**
* Adds an input port to this <code>CommCore</code>.
* This method is not thread-safe.
* @param inputPortName the name of the input port to add
* @param uri the <code>URI</code> of the input port to add
* @param protocolFactory the <code>CommProtocolFactory</code> to use for the input port
* @param protocolConfigurationPath the protocol configuration variable path to use for the input port
* @param protocolConfigurationProcess the protocol configuration process to execute for configuring the created protocols
* @param operationNames the operation names the input port can handle
* @param aggregationMap the aggregation mapping of the input port
* @param redirectionMap the redirection mapping of the input port
* @throws java.io.IOException in case of some underlying implementation error
* @see URI
* @see CommProtocolFactory
*/
public void addInputPort(
InputPort inputPort,
CommProtocolFactory protocolFactory,
Process protocolConfigurationProcess
)
throws IOException
{
protocolConfigurations.add( protocolConfigurationProcess );
CommListener listener = null;
String medium = inputPort.location().getScheme();
CommListenerFactory factory = getCommListenerFactory( medium );
if ( factory == null ) {
throw new UnsupportedCommMediumException( medium );
}
listener = factory.createListener(
interpreter,
protocolFactory,
inputPort
);
listenersMap.put( inputPort.name(), listener );
}
private final ExecutorService executorService;
private class CommThreadFactory implements ThreadFactory {
public Thread newThread( Runnable r )
{
return new CommChannelHandler( interpreter, r );
}
}
private static Pattern pathSplitPattern = Pattern.compile( "/" );
private class CommChannelHandlerRunnable implements Runnable {
private final CommChannel channel;
private final InputPort port;
public CommChannelHandlerRunnable( CommChannel channel, InputPort port )
{
this.channel = channel;
this.port = port;
}
private void forwardResponse( CommMessage message )
throws IOException
{
message = new CommMessage(
channel.redirectionMessageId(),
message.operationName(),
message.resourcePath(),
message.value(),
message.fault()
);
try {
try {
channel.redirectionChannel().send( message );
} finally {
try {
if ( channel.redirectionChannel().toBeClosed() ) {
channel.redirectionChannel().close();
} else {
channel.redirectionChannel().disposeForInput();
}
} finally {
channel.setRedirectionChannel( null );
}
}
} finally {
channel.closeImpl();
}
}
private void handleRedirectionInput( CommMessage message, String[] ss )
throws IOException, URISyntaxException
{
// Redirection
String rPath = "";
if ( ss.length <= 2 ) {
rPath = "/";
} else {
StringBuilder builder = new StringBuilder();
for( int i = 2; i < ss.length; i++ ) {
builder.append( '/' );
builder.append( ss[ i ] );
}
rPath = builder.toString();
}
OutputPort oPort = port.redirectionMap().get( ss[1] );
if ( oPort == null ) {
String error = "Discarded a message for resource " + ss[1] +
", not specified in the appropriate redirection table.";
interpreter.logWarning( error );
throw new IOException( error );
}
try {
CommChannel oChannel = oPort.getNewCommChannel();
CommMessage rMessage =
new CommMessage(
message.id(),
message.operationName(),
rPath,
message.value(),
message.fault()
);
oChannel.setRedirectionChannel( channel );
oChannel.setRedirectionMessageId( rMessage.id() );
oChannel.send( rMessage );
oChannel.setToBeClosed( false );
oChannel.disposeForInput();
} catch( IOException e ) {
channel.send( CommMessage.createFaultResponse( message, new FaultException( Constants.IO_EXCEPTION_FAULT_NAME, e ) ) );
channel.disposeForInput();
throw e;
}
}
private void handleAggregatedInput( CommMessage message, AggregatedOperation operation )
throws IOException, URISyntaxException
{
operation.runAggregationBehaviour( message, channel );
}
private void handleDirectMessage( CommMessage message )
throws IOException
{
try {
InputOperation operation =
interpreter.getInputOperation( message.operationName() );
try {
operation.requestType().check( message.value() );
interpreter.correlationEngine().onMessageReceive( message, channel );
if ( operation instanceof OneWayOperation ) {
// We need to send the acknowledgement
channel.send( CommMessage.createEmptyResponse( message ) );
//channel.release();
}
} catch( TypeCheckingException e ) {
interpreter.logWarning( "Received message TypeMismatch (input operation " + operation.id() + "): " + e.getMessage() );
try {
channel.send( CommMessage.createFaultResponse( message, new FaultException( jolie.lang.Constants.TYPE_MISMATCH_FAULT_NAME, e.getMessage() ) ) );
} catch( IOException ioe ) {
Interpreter.getInstance().logSevere( ioe );
}
} catch( CorrelationError e ) {
interpreter.logWarning( "Received a non correlating message for operation " + message.operationName() + ". Sending CorrelationError to the caller." );
channel.send( CommMessage.createFaultResponse( message, new FaultException( "CorrelationError", "The message you sent can not be correlated with any session and can not be used to start a new session." ) ) );
}
} catch( InvalidIdException e ) {
interpreter.logWarning( "Received a message for undefined operation " + message.operationName() + ". Sending IOException to the caller." );
channel.send( CommMessage.createFaultResponse( message, new FaultException( "IOException", "Invalid operation: " + message.operationName() ) ) );
} finally {
channel.disposeForInput();
}
}
private void handleMessage( CommMessage message )
throws IOException
{
try {
String[] ss = pathSplitPattern.split( message.resourcePath() );
if ( ss.length > 1 ) {
handleRedirectionInput( message, ss );
} else {
if ( port.canHandleInputOperationDirectly( message.operationName() ) ) {
handleDirectMessage( message );
} else {
AggregatedOperation operation = port.getAggregatedOperation( message.operationName() );
if ( operation == null ) {
interpreter.logWarning(
"Received a message for operation " + message.operationName() +
", not specified in the input port at the receiving service. Sending IOException to the caller."
);
channel.send( CommMessage.createFaultResponse( message, new FaultException( "IOException", "Invalid operation: " + message.operationName() ) ) );
channel.disposeForInput();
} else {
handleAggregatedInput( message, operation );
}
}
}
} catch( URISyntaxException e ) {
interpreter.logSevere( e );
}
}
public void run()
{
CommChannelHandler thread = CommChannelHandler.currentThread();
thread.setExecutionThread( interpreter().initThread() );
channel.lock.lock();
try {
if ( channel.redirectionChannel() == null ) {
assert( port != null );
CommMessage message = channel.recv();
if ( message != null ) {
handleMessage( message );
}
} else {
channel.lock.unlock();
CommMessage response = channel.recvResponseFor( new CommMessage( channel.redirectionMessageId(), "", "/", Value.UNDEFINED_VALUE, null ) );
if ( response != null ) {
forwardResponse( response );
}
}
} catch( IOException e ) {
interpreter.logSevere( e );
} finally {
if ( channel.lock.isHeldByCurrentThread() ) {
channel.lock.unlock();
}
thread.setExecutionThread( null );
}
}
}
/**
* Schedules the receiving of a message on this <code>CommCore</code> instance.
* @param channel the <code>CommChannel</code> to use for receiving the message
* @param port the <code>Port</code> responsible for the message receiving
*/
public void scheduleReceive( CommChannel channel, InputPort port )
{
executorService.execute( new CommChannelHandlerRunnable( channel, port ) );
}
/**
* Runs an asynchronous task in this CommCore internal thread pool.
* @param r the Runnable object to execute
*/
public void execute( Runnable r )
{
executorService.execute( r );
}
protected void startCommChannelHandler( Runnable r )
{
executorService.execute( r );
}
/**
* Initializes the communication core, starting its communication listeners.
* This method is asynchronous. When it returns, every communication listener has
* been issued to start, but they are not guaranteed to be ready to receive messages.
* This method throws an exception if some listener can not be issued to start;
* other errors will be logged by the listener through the interpreter logger.
*
* @throws IOException in case of some underlying <code>CommListener</code> initialization error
* @see CommListener
*/
public void init()
throws IOException
{
for( Entry< String, CommListener > entry : listenersMap.entrySet() ) {
entry.getValue().start();
}
active = true;
}
private PollingThread pollingThread = null;
private PollingThread pollingThread()
{
synchronized( this ) {
if ( pollingThread == null ) {
pollingThread = new PollingThread();
pollingThread.start();
}
}
return pollingThread;
}
private class PollingThread extends Thread {
private final Set< CommChannel > channels = new HashSet< CommChannel >();
private PollingThread()
{
super( threadGroup, interpreter.programFilename() + "-PollingThread" );
}
@Override
public void run()
{
Iterator< CommChannel > it;
CommChannel channel;
while( active ) {
synchronized( this ) {
if ( channels.isEmpty() ) {
// Do not busy-wait for no reason
try {
this.wait();
} catch( InterruptedException e ) {}
}
it = channels.iterator();
while( it.hasNext() ) {
channel = it.next();
try {
if ( ((PollableCommChannel)channel).isReady() ) {
it.remove();
scheduleReceive( channel, channel.parentInputPort() );
}
} catch( IOException e ) {
e.printStackTrace();
}
}
}
try {
Thread.sleep( 50 ); // msecs
} catch( InterruptedException e ) {}
}
for( CommChannel c : channels ) {
try {
c.closeImpl();
} catch( IOException e ) {
interpreter.logWarning( e );
}
}
}
public void register( CommChannel channel )
throws IOException
{
if ( !(channel instanceof PollableCommChannel) ) {
throw new IOException( "Channels registering for polling must implement PollableCommChannel interface");
}
synchronized( this ) {
channels.add( channel );
if ( channels.size() == 1 ) { // set was empty
this.notify();
}
}
}
}
/**
* Registers a <code>CommChannel</code> for input polling.
* The registered channel must implement the {@link PollableCommChannel <code>PollableCommChannel</code>} interface.
* @param channel the channel to register for polling
* @throws java.io.IOException in case the channel could not be registered for polling
* @see CommChannel
* @see PollableCommChannel
*/
public void registerForPolling( CommChannel channel )
throws IOException
{
pollingThread().register( channel );
}
private SelectorThread selectorThread = null;
private SelectorThread selectorThread()
throws IOException
{
synchronized( this ) {
if ( selectorThread == null ) {
selectorThread = new SelectorThread( interpreter );
selectorThread.start();
}
}
return selectorThread;
}
private class SelectorThread extends JolieThread {
private final Selector selector;
private final Object selectingMutex = new Object();
public SelectorThread( Interpreter interpreter )
throws IOException
{
super( interpreter, threadGroup, interpreter.programFilename() + "-SelectorThread" );
this.selector = Selector.open();
}
@Override
public void run()
{
SelectableStreamingCommChannel channel;
while( active ) {
try {
synchronized( selectingMutex ) {
selector.select();
}
synchronized( this ) {
for( SelectionKey key : selector.selectedKeys() ) {
if ( key.isValid() ) {
channel = (SelectableStreamingCommChannel)key.attachment();
try {
if ( channel.lock.tryLock() ) {
try {
key.cancel();
key.channel().configureBlocking( true );
if ( channel.isOpen() ) {
/*if ( channel.selectionTimeoutHandler() != null ) {
interpreter.removeTimeoutHandler( channel.selectionTimeoutHandler() );
}*/
scheduleReceive( channel, channel.parentInputPort() );
} else {
channel.closeImpl();
}
} catch( IOException e ) {
throw e;
} finally {
channel.lock.unlock();
}
}
} catch( IOException e ) {
if ( channel.lock.isHeldByCurrentThread() ) {
channel.lock.unlock();
}
if ( interpreter.verbose() ) {
interpreter.logSevere( e );
}
}
}
}
synchronized( selectingMutex ) {
selector.selectNow(); // Clean up the cancelled keys
}
}
} catch( IOException e ) {
interpreter.logSevere( e );
}
}
for( SelectionKey key : selector.keys() ) {
try {
((SelectableStreamingCommChannel)key.attachment()).closeImpl();
} catch( IOException e ) {
interpreter.logWarning( e );
}
}
}
public boolean register( SelectableStreamingCommChannel channel )
{
try {
if ( channel.inputStream().available() > 0 ) {
scheduleReceive( channel, channel.parentInputPort() );
return false;
}
synchronized( this ) {
if ( isSelecting( channel ) == false ) {
SelectableChannel c = channel.selectableChannel();
c.configureBlocking( false );
selector.wakeup();
synchronized( selectingMutex ) {
c.register( selector, SelectionKey.OP_READ, channel );
selector.selectNow();
}
}
}
return true;
} catch( ClosedChannelException e ) {
interpreter.logWarning( e );
return false;
} catch( IOException e ) {
interpreter.logSevere( e );
return false;
}
}
public void unregister( SelectableStreamingCommChannel channel )
throws IOException
{
synchronized( this ) {
if ( isSelecting( channel ) ) {
selector.wakeup();
synchronized( selectingMutex ) {
SelectionKey key = channel.selectableChannel().keyFor( selector );
if ( key != null ) {
key.cancel();
}
selector.selectNow();
}
channel.selectableChannel().configureBlocking( true );
}
}
}
private boolean isSelecting( SelectableStreamingCommChannel channel )
{
SelectableChannel c = channel.selectableChannel();
if ( c == null ) {
return false;
}
return c.keyFor( selector ) != null;
}
}
protected boolean isSelecting( SelectableStreamingCommChannel channel )
{
synchronized( this ) {
if ( selectorThread == null ) {
return false;
}
}
final SelectorThread t = selectorThread;
synchronized( t ) {
return t.isSelecting( channel );
}
}
protected void unregisterForSelection( SelectableStreamingCommChannel channel )
throws IOException
{
selectorThread().unregister( channel );
}
protected void registerForSelection( final SelectableStreamingCommChannel channel )
throws IOException
{
selectorThread().register( channel );
/*final TimeoutHandler handler = new TimeoutHandler( interpreter.persistentConnectionTimeout() ) {
@Override
public void onTimeout()
{
try {
if ( isSelecting( channel ) ) {
selectorThread().unregister( channel );
channel.setToBeClosed( true );
channel.close();
}
} catch( IOException e ) {
interpreter.logSevere( e );
}
}
};
channel.setSelectionTimeoutHandler( handler );
if ( selectorThread().register( channel ) ) {
interpreter.addTimeoutHandler( handler );
} else {
channel.setSelectionTimeoutHandler( null );
}*/
}
/** Shutdowns the communication core, interrupting every communication-related thread. */
public synchronized void shutdown()
{
if ( active ) {
active = false;
for( Entry< String, CommListener > entry : listenersMap.entrySet() ) {
entry.getValue().shutdown();
}
if ( selectorThread != null ) {
selectorThread.selector.wakeup();
}
executorService.shutdown();
threadGroup.interrupt();
}
}
private boolean active = false;
}