// Copyright (c) 2014 Tom Zhou<iwebpp@gmail.com>
package com.iwebpp.node.net;
import java.util.ArrayList;
import java.util.List;
import com.iwebpp.libuvpp.Address;
import com.iwebpp.libuvpp.handles.LoopHandle;
import com.iwebpp.libuvpp.handles.StreamHandle;
import com.iwebpp.node.EventEmitter2;
import com.iwebpp.node.NodeContext;
import com.iwebpp.node.Util;
public abstract class AbstractServer
extends EventEmitter2 {
private final static String TAG = "AbstractServer";
private int _connections;
protected StreamHandle _handle;
private List<List<AbstractSocket>> _slaves;
private boolean allowHalfOpen;
private int maxConnections = 1024;
private Address _sockname;
protected NodeContext context;
protected void _emitCloseIfDrained() throws Exception {
debug(TAG, "SERVER _emitCloseIfDrained");
final AbstractServer self = this;
if (self._handle!=null || self.get_connections()>0) {
debug(TAG, "SERVER handle? " + self._handle +
" connections? " + self.get_connections());
return;
}
// TBD...
///process.nextTick(function() {
context.nextTick(new NodeContext.nextTickListener() {
@Override
public void onNextTick() throws Exception {
debug(TAG, "SERVER: emit close");
self.emit("close");
}
});
}
public AbstractServer(
final NodeContext context,
Options options,
final ConnectionListener listener) throws Exception {
AbstractServer self = this;
// node context
this.context = context;
// set initial onConnection callback
if (listener != null) {
self.on("connection", new Listener(){
@Override
public void onEvent(Object data) throws Exception {
AbstractSocket socket = (AbstractSocket)data;
listener.onConnection(socket);
}
});
}
this.set_connections(0);
/*
*
Object.defineProperty(this, 'connections', {
get: util.deprecate(function() {
if (self._usingSlaves) {
return null;
}
return self._connections;
}, 'connections property is deprecated. Use getConnections() method'),
set: util.deprecate(function(val) {
return (self._connections = val);
}, 'connections property is deprecated. Use getConnections() method'),
configurable: true, enumerable: true
});*/
this._handle = null;
this._slaves = new ArrayList< List<AbstractSocket> >();
this.setAllowHalfOpen(options.allowHalfOpen);
}
@SuppressWarnings("unused")
private AbstractServer(){}
public static class Options {
public boolean allowHalfOpen;
public Options(boolean allowHalfOpen) {
this.allowHalfOpen = allowHalfOpen;
}
@SuppressWarnings("unused")
private Options(){}
}
protected static int _listen(StreamHandle handle, int backlog) {
// Use a backlog of 512 entries. We pass 511 to the listen() call because
// the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
// which will thus give us a backlog of 512 entries.
return handle.listen(backlog>0? backlog : 511);
}
public void listen(String address, int port, int addressType,
int backlog, int fd, final ListeningCallback cb) throws Exception {
AbstractServer self = this;
///if (util.isFunction(lastArg)) {
if (cb != null) {
self.once("listening", new Listener(){
@Override
public void onEvent(Object data) throws Exception {
cb.onListening();
}
});
}
_listen2(address, port, addressType, backlog, fd);
}
public void listen(String address, int port, int backlog,
final ListeningCallback cb) throws Exception {
listen(address, port, Util.ipFamily(address), backlog, -1, cb);
}
public void listen(String address, int port,
final ListeningCallback cb) throws Exception {
listen(address, port, Util.ipFamily(address), 256, -1, cb);
}
public void listen(int port, int backlog,
final ListeningCallback cb) throws Exception {
listen("0.0.0.0", port, 4, backlog, -1, cb);
}
public void listen(int port, final ListeningCallback cb) throws Exception {
listen("0.0.0.0", port, 4, 256, -1, cb);
}
public Address address() {
return this._getsockname();
}
public String localAddress() {
return this._getsockname().getIp();
}
public int localPort() {
return this._getsockname().getPort();
}
public String family() {
return this._getsockname().getFamily();
}
private Address _getsockname() {
if (null == this._handle /*|| !this._handle.getsockname*/) {
return null;
}
if (null == this._sockname) {
Address out = this._getSocketName();
if (null == out) return null; // FIXME(bnoordhuis) Throw?
this._sockname = out;
}
return this._sockname;
}
public int getConnections() {
/*
*
function end(err, connections) {
process.nextTick(function() {
cb(err, connections);
});
}
if (!this._usingSlaves) {
return end(null, this._connections);
}
// Poll slaves
var left = this._slaves.length,
total = this._connections;
function oncount(err, count) {
if (err) {
left = -1;
return end(err);
}
total += count;
if (--left === 0) return end(null, total);
}
this._slaves.forEach(function(slave) {
slave.getConnections(oncount);
});
*/
return this.get_connections();
}
public void close(final CloseListener cb) throws Exception {
if (cb!=null) {
if (null==this._handle) {
this.once("close", new Listener(){
@Override
public void onEvent(Object data) throws Exception {
cb.onClose("Not running");
}
});
} else {
///this.once("close", cb);
this.once("close", new Listener(){
@Override
public void onEvent(Object data) throws Exception {
cb.onClose(data!=null? data.toString() : "");
}
});
}
}
if (this._handle != null) {
this._handle.close();
this._handle = null;
}
this._emitCloseIfDrained();
}
protected void _setupSlave(List<AbstractSocket> socketList) {
this._slaves.add(socketList);
}
public void ref() {
if (this._handle!=null)
this._handle.ref();
};
public void unref() {
if (this._handle!=null)
this._handle.unref();
}
// Event listeners
public void onConnection(final ConnectionListener cb) throws Exception {
this.on("connection", new Listener(){
@Override
public void onEvent(Object raw) throws Exception {
AbstractSocket data = (AbstractSocket)raw;
cb.onConnection(data);
}
});
}
public interface ConnectionListener {
public void onConnection(AbstractSocket socket);
}
public void onceClose(final CloseListener cb) throws Exception {
this.once("close", new Listener(){
@Override
public void onEvent(Object raw) throws Exception {
cb.onClose(raw!=null? raw.toString() : "");
}
});
}
public interface CloseListener {
public void onClose(String error);
}
public void onceListening(final ListeningCallback cb) throws Exception {
this.once("listening", new Listener(){
@Override
public void onEvent(Object raw) throws Exception {
cb.onListening();
}
});
}
public interface ListeningCallback {
public void onListening() throws Exception;
}
public void onError(final ErrorListener cb) throws Exception {
this.on("error", new Listener(){
@Override
public void onEvent(Object raw) throws Exception {
cb.onError(raw!=null? raw.toString() : "");
}
});
}
public interface ErrorListener {
public void onError(String error) throws Exception;
}
/**
* @return the _connections
*/
protected int get_connections() {
return _connections;
}
/**
* @param _connections the _connections to set
*/
protected void set_connections(int _connections) {
this._connections = _connections;
}
/**
* @return the maxConnections
*/
public int getMaxConnections() {
return maxConnections;
}
/**
* @param maxConnections the maxConnections to set
*/
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* @return the allowHalfOpen
*/
public boolean isAllowHalfOpen() {
return allowHalfOpen;
}
/**
* @param allowHalfOpen the allowHalfOpen to set
*/
protected void setAllowHalfOpen(boolean allowHalfOpen) {
this.allowHalfOpen = allowHalfOpen;
}
// Abstract server methods
protected abstract int _bind(final String ip, final int port);
protected abstract int _bind6(final String ip, final int port);
protected abstract Address _getSocketName();
protected abstract int _listen(final int backlog);
protected abstract int _accept(final StreamHandle client);
protected abstract StreamHandle _createHandle(final LoopHandle loop);
protected abstract StreamHandle _createServerHandle(String address, int port,
int addressType, int fd);
protected abstract void _listen2(String address, int port, int addressType,
int backlog, int fd) throws Exception;
}