/**
* (c) 2011 tkuri
*/
package tkuri.jxy.server;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import tkuri.jxy.Util;
import tkuri.jxy.arch.ASelectionAttachment;
import tkuri.jxy.arch.ChannelReader;
import tkuri.jxy.arch.TheSelector;
/**
* origin server
*/
public class OriginPeer extends ASelectionAttachment {
static int ID = 0;
private int id_ = ID++;
private InetSocketAddress addr_;
private LinkedList<ITransfer> ascentQ_ = new LinkedList<ITransfer>();
private ITransfer ascent_ = null;
private LinkedList<ITransfer> descentQ_ = new LinkedList<ITransfer>();
private ITransfer descent_ = null;
SocketChannel socket = null;
ChannelReader reader = null;
//OriginResponse response = null;
private SelectionKey selKey_ = null;
/**
* ctor
* @param aAddr
*/
OriginPeer(InetSocketAddress aAddr) {
addr_ = aAddr;
}
void enq(ITransfer aTransfer) {
Util.say("origin", id_, ": enqueue ", aTransfer);
ascentQ_.add(aTransfer);
_queryNextAscent();
}
private boolean _queryNextAscent() {
if (descent_ != null) {
wantToWrite(false);
return false; // descent 作業中はリクエスト処理しない
}
if (ascent_ != null) {
return true;
}
ascent_ = ascentQ_.pollFirst();
//Util.say("origin", id_, ": next ascent " + ascent_);
if (ascent_ != null) {
_verifySocket();
return true;
} else if (reader.available() < 0) {
Util.say("origin", id_, ": no ascent task, no data anymore.");
_termIfIdle();
} else {
//Util.say("origin: no write");
wantToWrite(false);
}
return false;
}
/*
* verify
*/
private void _verifySocket() {
//Util.say("verify");
if (socket != null) {
if (reader.fill() < 0) {
//Util.say("origin", id_, ": verify: origin sock closed");
invalidate(true);
} else {
//Util.say("origin", id_, ": verify: origin sock living");
}
}
if (socket != null) {
ascent_.onOriginConnect(this, true);
//Util.say("origin", id_, ": want to write");
wantToWrite(true);
return;
}
try {
socket = SocketChannel.open();
reader = new ChannelReader(socket);
socket.configureBlocking(false);
selKey_ = TheSelector.registerChannel(
socket
, SelectionKey.OP_CONNECT
+ SelectionKey.OP_READ
+ SelectionKey.OP_WRITE
, this);
Util.say("origin", id_, ": connecting: ", addr_);
if (socket.connect(addr_)) {
onConnect();
} else {
// onConnect を待つ
}
} catch (Exception x) {
// このあたりで例外が出るとすれば,NIO自体に問題があるからでは?
throw new RuntimeException(x);
}
//Util.say("verified");
}
public boolean isValid() {
return (selKey_ != null && selKey_.isValid());
}
@Override
public int interestOps() {
if (isValid()) {
return selKey_.interestOps();
}
return 0;
}
@Override
public void interestOps(int aOps) {
if (isValid()) {
selKey_.interestOps(aOps);
}
}
/**
*
* @return
*/
public boolean isConnected() {
return (socket != null) ? socket.isConnected() : false;
}
private void _termIfIdle() {
if (ascent_ == null && ascentQ_.size() == 0
&& descent_ == null && descentQ_.size() == 0) {
invalidate(true);
}
}
public void invalidate(boolean aClose) {
if (aClose && socket != null) {
Util.say("origin", id_, ": invalidate(true)");
Util.close(socket);
selKey_.cancel();
socket = null;
reader = null;
selKey_ = null;
}
}
@Override
public void onSelected(Selector aSelector) {
}
@Override
public void onAccept() {
}
@Override
public void onConnect() {
interestOps(interestOps() & (~SelectionKey.OP_CONNECT));
ascent_.onOriginConnect(this, false);
}
public boolean readFirst() {
return false;
}
@Override
public void onWritable() {
if (! _queryNextAscent()) {
return;
}
if (! isValid() || !isConnected()) {
return;
}
ascent_.onOriginWritable();
}
public void notifyRequestEnd() {
if (ascent_ == null) {
Util.say("origin", id_, ": why ascent = null?");
return;
}
descent_ = ascent_;
wantToWrite(false);
//wantToRead(true);
// descentQ_.add(ascent_);キューにする必要ない...
// ascent_ = null; だめ.レスポンス完了するまで次のリクエストをしたくない.Conn:closeされるかもしれないし...
}
@Override
public void onReadable() {
if (! isValid() || !isConnected()) {
return;
}
reader.fill();
//Util.say("origin", id_, ": length: ", reader.length());
if (descent_ == null) {
descent_ = descentQ_.pollFirst();
if (descent_ == null) {
Util.say("origin", id_, ": no next descent.");
_termIfIdle();
return;
}
//Util.say("origin", id_, ": next descent " + descent_);
}
descent_.onOriginReadable();
}
public void notifyResponseEnd() {
descent_ = null;
ascent_ = null;
//wantToRead(false);
_queryNextAscent();
}
@Override
public String toString() {
return "OriginPeer" + id_ + ": asc = " + ascent_ + " / dsc = " + descent_
+ " / descQsize = " + descentQ_.size();
}
}