ca.odell.glazedlists.impl.nio.NIODaemon Maven / Gradle / Ivy
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.nio;
// NIO is used for CTP
import java.io.IOException;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An event queue of I/O events and a thread to run them on.
*/
public final class NIODaemon implements Runnable {
/** logging */
private static Logger logger = Logger.getLogger(NIODaemon.class.toString());
/** asynch queue of tasks to execute */
private List pendingRunnables = new ArrayList();
/** the only thread that shall access the network resources of this manager */
private Thread ioThread = null;
/** the selector to awaken when necessary */
private Selector selector;
/** whether the connection manager shall shut down */
private boolean keepRunning = false;
/** whom to handle incoming connections */
private NIOServer server = null;
/**
* Starts the NIODaemon.
*/
public synchronized void start() throws IOException {
// verify we haven't already started
if(ioThread != null) throw new IllegalStateException();
// prepare for non-blocking, selectable IO
selector = Selector.open();
// start handling connections
keepRunning = true;
ioThread = new Thread(this, "GlazedLists nio");
ioThread.start();
}
/**
* Continuously selects a connection which needs servicing and services it.
*/
@Override
public void run() {
// the list of runnables to run this iteration
List toExecute = new ArrayList();
// always run the selector handler
SelectAndHandle selectAndHandle = new SelectAndHandle(this);
// continuously select a socket and action on it
while(keepRunning) {
// get the list of runnables to run
synchronized(this) {
toExecute.addAll(pendingRunnables);
toExecute.add(selectAndHandle);
pendingRunnables.clear();
}
// run the runnables
for(Iterator i = toExecute.iterator(); keepRunning && i.hasNext(); ) {
Runnable runnable = (Runnable)i.next();
i.remove();
try {
runnable.run();
} catch(RuntimeException e) {
logger.log(Level.SEVERE, "Failure processing I/O, continuing", e);
}
}
}
// do final clean up of state
synchronized(this) {
pendingRunnables.clear();
selector = null;
ioThread = null;
keepRunning = false;
}
}
/**
* Tests whether this connection manager has started.
*/
public synchronized boolean isRunning() {
return (ioThread != null);
}
/**
* Tests whether the current thread is the network thread.
*/
public synchronized boolean isNetworkThread() {
return Thread.currentThread() == ioThread;
}
/**
* Wake up the CTP thread so that it may process pending events.
*/
private void wakeUp() {
selector.wakeup();
}
/**
* Runs the specified task on the NIODaemon thread.
*/
public void invokeAndWait(Runnable runnable) {
// if the server has not yet been started
if(!isRunning()) throw new IllegalStateException();
// invoke immediately if possible
if(isNetworkThread()) {
runnable.run();
// run on the network thread while waiting on the current thread
} else {
BlockingRunnable blockingRunnable = new BlockingRunnable(runnable);
synchronized(blockingRunnable) {
// start the event
synchronized(this) {
pendingRunnables.add(blockingRunnable);
}
wakeUp();
// wait for it to be completed
try {
blockingRunnable.wait();
} catch(InterruptedException e) {
throw new RuntimeException("Wait interrupted " + e.getMessage());
}
// propagate any RuntimeExceptions
RuntimeException problem = blockingRunnable.getInvocationTargetException();
if(problem != null) throw problem;
}
}
}
/**
* Runs the specified task the next time the NIODaemon thread has a chance.
*/
public void invokeLater(Runnable runnable) {
synchronized(this) {
// if the server has not yet been started
if(!isRunning()) throw new IllegalStateException();
pendingRunnables.add(runnable);
wakeUp();
}
}
/**
* Stops the NIODaemon.
*/
public void stop() {
// shutdown the server
invokeAndWait(new Shutdown(this));
// stop the server
invokeAndWait(new Stop());
}
/**
* Stops the server after it has been shut down.
*/
private class Stop implements Runnable {
@Override
public void run() {
// warn if unsatisfied keys remain
if(selector.keys().size() != 0) {
logger.warning("Server stopping with " + selector.keys().size() + " active connections");
} else {
logger.info("Server stopping with " + selector.keys().size() + " active connections");
}
// break out of the server dispatch loop
keepRunning = false;
}
}
/**
* Gets the selector that this NIODaemon manages.
*/
public Selector getSelector() {
return selector;
}
/**
* Configure this NIODaemon to use the specified server handler for acceptable
* selection keys.
*/
public void setServer(NIOServer server) {
this.server = server;
}
public NIOServer getServer() {
return server;
}
}