/*********************************************************************
* ConnectionAcceptor.java
* created on 01.03.2005 by netseeker
* $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/core/ConnectionAcceptor.java,v $
* $Date: 2007/03/29 15:46:50 $
* $Revision: 1.4 $
*
* ====================================================================
*
* Copyright 2005-2006 netseeker aka Michael Manske
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This file is part of the ejoe framework.
* For more information on the author, please see
* <http://www.manskes.de/>.
*
*********************************************************************/
package de.netseeker.ejoe.core;
import java.io.IOException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.netseeker.ejoe.ConnectionHeader;
import de.netseeker.ejoe.EJConstants;
import de.netseeker.ejoe.io.IOUtil;
/**
* The ConnectionAcceptor class is a server thread handling accepting of incoming client connections and simply hands
* over these connections to another thread for further processing. The adavantage of the separation of acception
* management and processing management *can* result in a significantly higher throughput if dealing with a lot of
* clients.
*
* @author netseeker
* @since 0.3.0
*/
public final class ConnectionAcceptor extends Thread
{
private static final Logger logger = Logger.getLogger( ConnectionAcceptor.class.getName() );
private ServerSocketChannel _channel;
private ChannelRegistrar[] _registrars;
private Selector _selector;
public ConnectionAcceptor(final ServerSocketChannel channel, final ChannelRegistrar registrars[])
throws IOException
{
super( "EJOE ConnectionAcceptor" );
this._channel = channel;
this._registrars = registrars;
this._selector = Selector.open();
// clean out previously cancelled keys - system might reuse selectors
this._selector.selectNow();
}
/*
* (non-Javadoc)
*
* @see java.lang.Thread#run()
*/
public void run()
{
try
{
// register our server socket on the selector
this._channel.register( _selector, SelectionKey.OP_ACCEPT );
while ( !isInterrupted() )
{
// just do endless select operations until one or more intersted
// socket channels will be found
if ( _selector.select() == 0 )
{
continue;
}
Iterator it = _selector.selectedKeys().iterator();
SelectionKey selKey = null;
SocketChannel cChannel = null;
ConnectionHeader header = null;
while ( it.hasNext() && !isInterrupted() )
{
selKey = (SelectionKey) it.next();
it.remove();
try
{
// validate the key
if ( !selKey.isValid() )
{
continue;
}
// socket is ready for establishing a connection?
if ( selKey.isAcceptable() )
{
// establish the connection
cChannel = this._channel.accept();
// a little bit paranoia
if ( cChannel != null )
{
// little bit tuning
cChannel.socket().setTrafficClass( EJConstants.IPTOS_THROUGHPUT );
cChannel.socket().setReuseAddress( true );
cChannel.socket().setSoLinger( false, 0 );
// ensure that the socket channel is prepared
// for non-blocking operations
cChannel.configureBlocking( false );
// create a new ConnectionHeader for all
// upcoming operations on the
// accepted socket connection
header = new ConnectionHeader( cChannel, cChannel.socket().getRemoteSocketAddress()
.toString(), true );
// register the channel on another thread which
// will do further connection handling
getProcessor().register( header, SelectionKey.OP_READ );
if ( logger.isLoggable( Level.FINE ) )
{
logger.log( Level.FINE, "Connection accepted from "
+ cChannel.socket().getRemoteSocketAddress() );
}
}
}
}
catch ( CancelledKeyException cke )
{
logger.log( Level.WARNING, "Key canceled!", cke );
}
}
}
}
catch ( IOException e )
{
logger.log( Level.SEVERE, "!!! IOException occured !!! ", e );
}
finally
{
try
{
IOUtil.closeQuiet( _selector );
}
catch ( Exception e )
{
logger.log( Level.SEVERE, "!!! Error while stopping server !!!", e );
}
}
}
/**
* @return
*/
private ChannelRegistrar getProcessor()
{
ChannelRegistrar rProcessor = null;
if ( this._registrars.length == 1 )
{
rProcessor = this._registrars[0];
}
else
{
int load = -1, pLoad = 0;
ChannelRegistrar tProcessor;
for ( int i = 0; i < this._registrars.length; i++ )
{
tProcessor = this._registrars[i];
pLoad = tProcessor.getLoad();
if ( pLoad < load || load == -1 )
{
rProcessor = tProcessor;
}
}
}
return rProcessor;
}
}