/**
* <pre>
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU AFFERO GENERAL PUBLIC LICENSE for more details.
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* </pre>
*/
package com.meidusa.amoeba.net;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.meidusa.amoeba.runtime.Shutdowner;
import com.meidusa.amoeba.service.Service;
/**
* 指定一个端口,创建一个serverSocket. 将该ServerSocket所创建的Connection加入管理
* 该manager只负责socket accept netEvent,socket的 IO netEvent 由 {@link ServerableConnectionManager#connMgr} 负责
*
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
*/
public class ServerableConnectionManager extends AuthingableConnectionManager implements Shutdowner, Service{
protected static Logger log = Logger.getLogger(ServerableConnectionManager.class);
/* class AuthingConnectionObserver implements ConnectionObserver{
public void connectionClosed(Connection conn) {
}
public void connectionEstablished(Connection connection) {
if(connection instanceof AuthingableConnection){
((AuthingableConnection)connection).setAuthenticator(ServerableConnectionManager.this.getAuthenticator());
((AuthingableConnection)connection).beforeAuthing();
}
}
public void connectionFailed(Connection conn, Exception fault) {
}
}*/
protected int port;
protected ServerSocketChannel ssocket;
protected String ipAddress;
protected ConnectionFactory connFactory;
private ConnectionManager manager;
//private ConnectionObserver connObserver = new AuthingConnectionObserver();
private int backlog = 128;
public ConnectionManager getManager() {
return manager;
}
public void setManager(ConnectionManager manager) {
this.manager = manager;
}
public ServerableConnectionManager() throws IOException{
this.setDaemon(false);
}
public void setConnectionFactory(ConnectionFactory connFactory) {
this.connFactory = connFactory;
}
protected void initServerSocket(){
try {
//this.addConnectionObserver(connObserver);
// create a listening socket and add it to the select set
ssocket = ServerSocketChannel.open();
ssocket.configureBlocking(false);
InetSocketAddress isa = null;
if (ipAddress != null) {
isa = new InetSocketAddress(ipAddress, port);
} else {
isa = new InetSocketAddress(port);
}
ssocket.socket().bind(isa,this.backlog);
registerServerChannel(ssocket);
Level level = log.getLevel();
log.setLevel(Level.INFO);
if (log.isInfoEnabled()) {
log.info(this.getName()+" listening on " + isa + ".");
}
log.setLevel(level);
} catch (IOException ioe) {
log.error("Failure listening to socket on port '" + port + "'.", ioe);
System.err.println("Failure listening to socket on port '" + port + "'.");
ioe.printStackTrace();
System.exit(-1);
}
}
// documentation inherited
protected void willStart() {
super.willStart();
initServerSocket();
}
protected void registerServerChannel(final ServerSocketChannel listener) throws IOException {
// register this listening socket and map its select key to a net event
// handler that will
// accept new connections
NetEventHandler serverNetEvent = new NetEventHandler() {
private SelectionKey key;
public int handleEvent(long when) {
Connection conn = null;
do{
conn = acceptConnection(listener);
}while(conn != null);
return 0;
}
public boolean checkIdle(long now) {
return false; // we're never idle
}
public SelectionKey getSelectionKey() {
return key;
}
public void setSelectionKey(SelectionKey key) {
this.key = key;
}
public boolean doWrite() {
return true;
}
};
SelectionKey sk = listener.register(_selector, SelectionKey.OP_ACCEPT, serverNetEvent);
serverNetEvent.setSelectionKey(sk);
postRegisterNetEventHandler(serverNetEvent, SelectionKey.OP_ACCEPT);
}
protected Connection acceptConnection(ServerSocketChannel listener) {
SocketChannel channel = null;
try {
channel = listener.accept();
if (channel == null) {
if(log.isDebugEnabled()){
log.debug("Psych! Got ACCEPT_READY, but no connection.");
}
return null;
}
if (!(channel instanceof SelectableChannel)) {
try {
log.warn("Provided with un-selectable socket as result of accept(), can't " + "cope [channel=" + channel + "].");
} catch (Error err) {
log.warn("Un-selectable channel also couldn't be printed.");
}
// stick a fork in the socket
channel.socket().close();
return null;
}
Connection connection = connFactory.createConnection(channel, System.currentTimeMillis());
if(connection instanceof AuthingableConnection){
((AuthingableConnection)connection).setAuthenticator(this.getAuthenticator());
((AuthingableConnection)connection).beforeAuthing();
}
if(manager != null){
manager.postRegisterNetEventHandler(connection,SelectionKey.OP_READ);
}else{
this.postRegisterNetEventHandler(connection,SelectionKey.OP_READ);
}
return connection;
} catch (Exception e) {
if (channel != null) {
try {
channel.socket().close();
} catch (IOException ioe) {
log.warn("Failed closing aborted connection: " + ioe);
}
}
return null;
}
}
public void closeAll() {
super.closeAll();
try {
ssocket.close();
} catch (IOException e) {
}
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getBacklog() {
return backlog;
}
public void setBacklog(int backlog) {
this.backlog = backlog;
}
public void shutdown(){
Level level = log.getLevel();
log.setLevel(Level.INFO);
super.shutdown();
try {
ssocket.close();
} catch (IOException e) {
}
log.warn(this.getName()+" shutdown completed!");
log.setLevel(level);
}
@Override
public int getShutdownPriority() {
return 10;
}
}