package org.webpieces.nio.impl.cm.basic.nioimpl;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
import org.webpieces.nio.api.exceptions.NioClosedChannelException;
import org.webpieces.nio.api.exceptions.NioException;
import org.webpieces.nio.api.exceptions.RuntimeInterruptedException;
import org.webpieces.nio.api.testutil.nioapi.Select;
import org.webpieces.nio.api.testutil.nioapi.SelectorListener;
/**
*/
public class SelectorImpl implements Select
{
private static final Logger log = LoggerFactory.getLogger(SelectorImpl.class);
private PollingThread thread;
private AbstractSelector selector;
private boolean running = false;
private boolean wantToShutDown = false;
private SelectorListener listener;
private SelectorProvider provider;
/**
* Creates an instance of SelectorImpl.
* @param selector
*/
public SelectorImpl(AbstractSelector selector) {
}
/**
* Creates an instance of SelectorImpl.
* @param provider
*/
public SelectorImpl(SelectorProvider provider) {
this.provider = provider;
}
/**
* @see org.webpieces.nio.api.testutil.nioapi.Select#wakeup()
*/
public void wakeup() {
if(selector != null)
selector.wakeup();
}
/**
*/
public Selector getSelector() {
return selector;
}
public void startPollingThread(SelectorListener l, String threadName) {
if(running)
throw new IllegalStateException("Already running, can't start again");
this.listener = l;
try {
selector = provider.openSelector();
thread = new PollingThread();
thread.setDaemon(true);
thread.setName(threadName);
thread.start();
} catch(IOException e) {
throw new NioException(e);
}
}
public void stopPollingThread() {
if(!running)
return;
wantToShutDown = true;
selector.wakeup();
synchronized(this) {
if(running) {
try {
this.wait(20000);
} catch(InterruptedException e) {
throw new RuntimeInterruptedException(e);
}
}
if(running)
log.error("Tried to shutdown channelmanager, but it took longer " +
"than 20 seconds. It may be hung now");
}
}
//protect the Thread from being started or controlled by putting
//the run in a private class. The rest of the methods are protected
//so they are ok.
private class PollingThread extends Thread {
@Override
public void run() {
try {
running = true;
runLoop();
log.trace(()->"shutting down the PollingThread");
selector.close();
selector = null;
thread = null;
synchronized(SelectorImpl.this) {
running = false;
SelectorImpl.this.notifyAll();
}
} catch (Exception e) {
log.error("Exception on ConnectionManager thread", e);
}
}
}
protected void runLoop() {
while (!wantToShutDown) {
listener.selectorFired();
}
}
/**
* @see org.webpieces.nio.api.testutil.nioapi.Select#getThread()
*/
public Object getThread() {
return thread;
}
/**
* @see org.webpieces.nio.api.testutil.nioapi.Select#selectedKeys()
*/
public Set<SelectionKey> selectedKeys() {
return selector.selectedKeys();
}
public int select() {
try {
return selector.select();
} catch (IOException e) {
throw new NioException(e);
}
}
/**
* @see org.webpieces.nio.api.testutil.nioapi.Select#isRunning()
*/
public boolean isRunning() {
return running;
}
/**
* @see org.webpieces.nio.api.testutil.nioapi.Select#isWantShutdown()
*/
public boolean isWantShutdown() {
return wantToShutDown;
}
public void setRunning(boolean b) {
running = b;
}
public SelectionKey getKeyFromChannel(SelectableChannel realChannel) {
return realChannel.keyFor(selector);
}
public SelectionKey register(SelectableChannel s, int allOps, Object struct) {
if(struct == null)
throw new IllegalArgumentException("struct cannot be null");
try {
return s.register(selector, allOps, struct);
} catch (ClosedChannelException e) {
throw new NioClosedChannelException(e);
}
}
}