Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.craft.atom.nio.NioAcceptor Maven / Gradle / Ivy
package io.craft.atom.nio;
import io.craft.atom.io.IoAcceptor;
import io.craft.atom.io.IoAcceptorX;
import io.craft.atom.io.IoHandler;
import io.craft.atom.io.IoReactorX;
import io.craft.atom.nio.api.NioAcceptorConfig;
import io.craft.atom.nio.spi.NioBufferSizePredictorFactory;
import io.craft.atom.nio.spi.NioChannelEventDispatcher;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Accepts incoming connection based TCP or datagram based UDP, communicates with clients, and fires events.
*
* @author mindwind
* @version 1.0, Feb 21, 2013
* @see NioTcpAcceptor
* @see NioUdpAcceptor
*/
@ToString(callSuper = true, of = { "config", "bindAddresses", "unbindAddresses", "boundmap" })
abstract public class NioAcceptor extends NioReactor implements IoAcceptor {
private static final Logger LOG = LoggerFactory.getLogger(NioAcceptor.class);
protected final Set bindAddresses = new HashSet() ;
protected final Set unbindAddresses = new HashSet() ;
protected final Map boundmap = new ConcurrentHashMap();
protected final Object lock = new Object() ;
protected volatile boolean selectable = false ;
protected volatile boolean endFlag = false ;
protected NioAcceptorConfig config ;
protected IOException exception ;
protected Selector selector ;
// ~ ----------------------------------------------------------------------------------------------------------
/**
* Constructs a new nio acceptor with default configuration, binds to the specified local address port.
*
* @param handler
* @param port
*/
public NioAcceptor(IoHandler handler, int port) {
this(handler, new NioAcceptorConfig(), port);
}
/**
* Constructs a new nio acceptor with specified configuration, binds to the specified local address port.
*
* @param handler
* @param config
* @param port
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, int port) {
this(handler, config, new InetSocketAddress(port));
}
/**
* Constructs a new nio acceptor with specified configuration and dispatcher, binds to the specified local address port.
*
* @param handler
* @param config
* @param dispatcher
* @param port
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, int port) {
this(handler, config, dispatcher, new InetSocketAddress(port));
}
/**
* Constructs a new nio acceptor with specified configuration, dispatcher and predictor factory, binds to the specified local address port.
*
* @param handler
* @param config
* @param dispatcher
* @param predictorFactory
* @param port
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory, int port) {
this(handler, config, dispatcher, predictorFactory, new InetSocketAddress(port));
}
/**
* Constructs a new nio acceptor with default configuration, and binds the specified socket addresses.
*
* @param handler
* @param firstLocalAddress
* @param otherLocalAddresses
*/
public NioAcceptor(IoHandler handler, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
this(handler, new NioAcceptorConfig(), firstLocalAddress, otherLocalAddresses);
}
/**
* Constructs a new acceptor the specified configuration, and binds the specified socket addresses.
*
* @param handler
* @param config
* @param firstLocalAddress
* @param otherLocalAddresses
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
this(handler, config, new NioOrderedDirectChannelEventDispatcher(config.getTotalEventSize()), new NioAdaptiveBufferSizePredictorFactory(), firstLocalAddress, otherLocalAddresses);
}
/**
* Constructs a new acceptor the specified configuration and dispatcher, binds the specified socket addresses.
*
* @param handler
* @param config
* @param dispatcher
* @param firstLocalAddress
* @param otherLocalAddresses
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
this(handler, config, dispatcher, new NioAdaptiveBufferSizePredictorFactory(), firstLocalAddress, otherLocalAddresses);
}
/**
* Constructs a new acceptor the specified configuration, dispatcher and predictor factory, binds the specified socket addresses.
*
* @param handler
* @param config
* @param dispatcher
* @param predictorFactory
* @param firstLocalAddress
* @param otherLocalAddresses
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
this(handler, config, dispatcher, predictorFactory);
try {
bind(firstLocalAddress, otherLocalAddresses);
} catch (IOException e) {
throw new RuntimeException("Failed to construct", e);
} finally {
if (!selectable && selector != null) {
try {
selector.close();
} catch (IOException e) {
LOG.warn("[CRAFT-ATOM-NIO] Selector close exception", e);
}
}
}
}
/**
* Constructs a new nio acceptor with default configuration, but not binds to any address.
*
* @param handler
*/
public NioAcceptor(IoHandler handler) {
this(handler, new NioAcceptorConfig(), new NioOrderedDirectChannelEventDispatcher(), new NioAdaptiveBufferSizePredictorFactory());
}
/**
* Constructs a new nio acceptor with specified configuration, but not binds to any address.
*
* @param handler
* @param config
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config) {
this(handler, config, new NioOrderedDirectChannelEventDispatcher(config.getTotalEventSize()), new NioAdaptiveBufferSizePredictorFactory());
}
/**
* Constructs a new nio acceptor with specified configuration and dispatcher, but not binds to any address.
*
* @param handler
* @param config
* @param dispatcher
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher) {
this(handler, config, dispatcher, new NioAdaptiveBufferSizePredictorFactory());
}
/**
* Constructs a new acceptor the specified configuration, dispatcher and predictor, but not binds to any address.
*
* @param handler
* @param config
* @param dispatcher
* @param predictorFactory
*/
public NioAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory) {
if (handler == null) {
throw new IllegalArgumentException("Handler should not be null!");
}
this.handler = handler;
this.config = (config == null ? new NioAcceptorConfig() : config);
this.dispatcher = dispatcher;
this.predictorFactory = predictorFactory;
this.pool = new NioProcessorPool(config, handler, dispatcher);
}
// ~ ------------------------------------------------------------------------------------------------------------
/**
* Init nio acceptor to ready state for bind socket address.
*
* @throws IOException
*/
private void init() throws IOException {
selector = Selector.open();
selectable = true;
new AcceptThread().start();
}
@Override
synchronized public void bind(int port) throws IOException {
bind(new InetSocketAddress(port));
}
@Override
synchronized public void bind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) throws IOException {
if (!this.selectable) {
init();
}
if (firstLocalAddress == null) {
throw new IllegalArgumentException("Need a local address to bind");
}
List localAddresses = new ArrayList(2);
localAddresses.add(firstLocalAddress);
if (otherLocalAddresses != null) {
for (SocketAddress address : otherLocalAddresses) {
localAddresses.add(address);
}
}
bindAddresses.addAll(localAddresses);
if (!bindAddresses.isEmpty()) {
synchronized (lock) {
// wake up for unblocking the select() to process binding addresses
selector.wakeup();
// wait for bind result
wait0();
}
}
}
private void wait0() throws IOException {
while (!this.endFlag) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new IOException(e);
}
}
// reset end flag
this.endFlag = false;
if (this.exception != null) {
IOException e = exception;
this.exception = null;
throw e;
}
}
private void bind0() {
if (!bindAddresses.isEmpty()) {
for (SocketAddress address : bindAddresses) {
boolean success = false;
try {
bindByProtocol(address);
success = true;
LOG.debug("[CRAFT-ATOM-NIO] Bind |address={}|", address);
} catch (IOException e) {
exception = e;
} finally {
if (!success) {
rollback();
break;
}
}
}
bindAddresses.clear();
// notify bind end
synchronized (lock) {
endFlag = true;
lock.notifyAll();
}
}
}
/**
* Rollback already bound address
*/
protected void rollback() {
Iterator> it = boundmap.entrySet().iterator();
while(it.hasNext()) {
Entry entry = it.next();
try {
close(entry.getValue());
} catch (IOException e) {
LOG.warn("[CRAFT-ATOM-NIO] Rollback bind operation exception", e);
} finally {
it.remove();
}
}
}
private void close(SelectableChannel sc) throws IOException {
if (sc != null) {
SelectionKey key = sc.keyFor(selector);
if (key != null) {
key.cancel();
}
sc.close();
}
}
@Override
public void shutdown() {
this.selectable = false;
this.selector.wakeup();
}
private void shutdown0() throws IOException {
// clear bind/unbind addresses cache
this.bindAddresses.clear();
this.unbindAddresses.clear();
// close all opened server socket channel
for (SelectableChannel sc : boundmap.values()) {
close(sc);
}
// close acceptor selector
this.selector.close();
super.shutdown();
LOG.debug("[CRAFT-ATOM-NIO] Shutdown acceptor successful");
}
@Override
public Set getBoundAddresses() {
return new HashSet(boundmap.keySet());
}
@Override
synchronized public final void unbind(int port) throws IOException {
unbind(new InetSocketAddress(port));
}
@Override
synchronized public final void unbind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) throws IOException {
if (firstLocalAddress == null) {
return;
}
List localAddresses = new ArrayList(2);
if (boundmap.containsKey(firstLocalAddress)) {
localAddresses.add(firstLocalAddress);
}
if (otherLocalAddresses != null) {
for (SocketAddress address : otherLocalAddresses) {
if (boundmap.containsKey(address)) {
localAddresses.add(address);
}
}
}
unbindAddresses.addAll(localAddresses);
if (!unbindAddresses.isEmpty()) {
synchronized (lock) {
// wake up for unblocking the select() to process unbinded addresses
selector.wakeup();
// wait for unbind result
wait0();
}
}
}
/**
* Unbind at once according to specified type.
*/
private void unbind0() {
if (!unbindAddresses.isEmpty()) {
for (SocketAddress address : unbindAddresses) {
try {
if (boundmap.containsKey(address)) {
SelectableChannel sc = boundmap.get(address);
close(sc);
boundmap.remove(address);
}
LOG.debug("[CRAFT-ATOM-NIO] Unbind |address={}|" + address);
} catch (IOException e) {
exception = e;
}
}
unbindAddresses.clear();
// notify bind end
synchronized (lock) {
endFlag = true;
lock.notifyAll();
}
}
}
private void accept() throws IOException {
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
acceptByProtocol(key);
}
}
/**
* Bind at once according to protocol type.
*
* @param address
*/
protected abstract void bindByProtocol(SocketAddress address) throws IOException;
/**
* Accept at once according to protocol type.
*
* @param key
* @return nio byte channel
*/
protected abstract NioByteChannel acceptByProtocol(SelectionKey key) throws IOException;
// ~ ------------------------------------------------------------------------------------------------------------
private class AcceptThread extends Thread {
public void run() {
while (selectable) {
try {
int selected = selector.select();
if (selected > 0) {
accept();
}
// bind addresses to listen
bind0();
// unbind canceled addresses
unbind0();
} catch (ClosedSelectorException e) {
LOG.error("[CRAFT-ATOM-NIO] Closed selector exception", e);
break;
} catch (Exception e) {
LOG.error("[CRAFT-ATOM-NIO] Unexpected exception", e);
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
}
// if selectable == false, shutdown the acceptor
try {
shutdown0();
} catch (Exception e) {
LOG.error("[CRAFT-ATOM-NIO] Shutdown exception", e);
}
}
}
@Override
public IoAcceptorX x() {
NioAcceptorX x = new NioAcceptorX();
x.setWaitBindAddresses(new HashSet(bindAddresses));
x.setWaitUnbindAddresses(new HashSet(unbindAddresses));
x.setBoundAddresses(new HashSet(boundmap.keySet()));
IoReactorX rx = super.x();
x.setNewChannelCount(rx.newChannelCount());
x.setFlushingChannelCount(rx.flushingChannelCount());
x.setClosingChannelCount(rx.closingChannelCount());
x.setAliveChannelCount(rx.aliveChannelCount());
return x;
}
}