/*
* Copyright 1999-2012 Alibaba Group.
*
* 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.
*/
package fm.liu.timo.net;
import java.io.IOException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.pmw.tinylog.Logger;
import fm.liu.timo.net.connection.AbstractConnection;
/**
* 网络事件反应器
*
* @author xianmao.hexm
*/
public final class NIOReactor {
private final String name;
private final Refactor reactor;
public NIOReactor(String name) throws IOException {
this.name = name;
this.reactor = new Refactor();
}
final void startup() {
new Thread(reactor, name + "-RW").start();
}
final void postRegister(AbstractConnection c) {
reactor.registerQueue.offer(c);
reactor.selector.wakeup();
}
final Queue<AbstractConnection> getRegisterQueue() {
return reactor.registerQueue;
}
final long getReactCount() {
return reactor.reactCount;
}
private final class Refactor implements Runnable {
private final Selector selector;
private final ConcurrentLinkedQueue<AbstractConnection> registerQueue;
private long reactCount;
private Refactor() throws IOException {
this.selector = Selector.open();
this.registerQueue = new ConcurrentLinkedQueue<AbstractConnection>();
}
@Override
public void run() {
final Selector selector = this.selector;
Set<SelectionKey> keys = null;
for (;;) {
++reactCount;
try {
selector.select(500L);
register(selector);
keys = selector.selectedKeys();
for (SelectionKey key : keys) {
Object att = key.attachment();
if (att != null && key.isValid()) {
if (key.isReadable()) {
read((AbstractConnection) att);
}
if (key.isWritable()) {
((AbstractConnection) att).check();
}
} else {
key.cancel();
}
}
} catch (Throwable e) {
if (!(e instanceof CancelledKeyException)) {
Logger.warn("Thread:{} Error:{}", name, e);
}
} finally {
if (keys != null) {
keys.clear();
}
}
}
}
private void register(Selector selector) {
AbstractConnection c = null;
while ((c = registerQueue.poll()) != null) {
try {
c.getActor().register(selector);
c.register();
} catch (Throwable e) {
c.close(e.getMessage());
}
}
}
private void read(AbstractConnection c) {
try {
c.read();
} catch (Throwable e) {
if (!(e instanceof IOException)) {
e.printStackTrace();
}
c.close(e.getMessage());
}
}
}
}