package org.dcache.ftp.data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
/**
* Multiplexer implements an event loop around a normal Java NIO
* Selector and delegates each even to MultiplexerListener
* implementations.
*
* Besides the infrastructure for registering listeners and the event
* loop, this class provides little functionality.
*
* Notice that the multiplexer is not thread-safe.
*/
public class Multiplexer
{
private static final Logger LOGGER = LoggerFactory.getLogger(Multiplexer.class);
protected boolean _shutdown;
protected Selector _selector;
/**
* Constructs a new multiplexer. The multiplexer must be destroyed
* by a call to close().
*/
public Multiplexer() throws IOException {
_shutdown = false;
_selector = Selector.open();
}
/**
* The event loop. The event loop continues running until
* shutdown() is called or the current thread has been
* interrupted.
*
* @throws InterruptedException
*/
public void loop() throws IOException, FTPException, InterruptedException {
while (!_shutdown) {
_selector.select();
if (Thread.interrupted()) {
throw new InterruptedException();
}
for (SelectionKey key : _selector.selectedKeys()) {
MultiplexerListener listener =
(MultiplexerListener)key.attachment();
if (key.isValid() && key.isConnectable() ) {
listener.connect(this, key);
}
if (key.isValid() && key.isAcceptable()) {
listener.accept(this, key);
}
if (key.isValid() && key.isReadable()) {
listener.read(this, key);
}
if (key.isValid() && key.isWritable()) {
listener.write(this, key);
}
}
_selector.selectedKeys().clear();
}
}
/**
* Register a listener on the given channel. Only one listener can
* be registered on any given channel. If a listener was already
* registered, the old listener is silently unregistered and the
* new listener is registered. The listener is registered for the
* type of events specified by the op bitmask (@see
* SelectionKey).
*/
public SelectionKey register(MultiplexerListener listener,
int op, SelectableChannel channel)
throws IOException
{
return channel.register(_selector, op, listener);
}
/**
* Add a listener to the multiplexer. This is equivalent to
* calling listener.register(multiplexer).
*/
public void add(MultiplexerListener listener) throws IOException {
listener.register(this);
}
/**
* Closes the multiplexer. This closes the encapsulated selector
* and all channels currently registered in the selector.
*/
public void close() throws IOException {
for (SelectionKey key : _selector.keys()) {
key.channel().close();
}
_selector.selectNow();
_selector.close();
}
/**
* Shuts down the multiplexer, causing it to leave the event loop.
*/
public void shutdown() {
LOGGER.trace("Multiplexer shutting down");
_shutdown = true;
}
}