/***************************************************************************
* *
* SocketEndpoint.java *
* ------------------- *
* date : 12.08.2004 *
* copyright : (C) 2004-2008 Distributed and *
* Mobile Systems Group *
* Lehrstuhl fuer Praktische Informatik *
* Universitaet Bamberg *
* http://www.uni-bamberg.de/pi/ *
* email : sven.kaffille@uni-bamberg.de *
* karsten.loesing@uni-bamberg.de *
* *
* *
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* A copy of the license can be found in the license.txt file supplied *
* with this software or at: http://www.gnu.org/copyleft/gpl.html *
* *
***************************************************************************/
package de.uniba.wiai.lspi.chord.com.socket;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import de.uniba.wiai.lspi.chord.com.Endpoint;
import de.uniba.wiai.lspi.chord.com.Node;
import de.uniba.wiai.lspi.chord.data.URL;
import de.uniba.wiai.lspi.util.logging.Logger;
import static de.uniba.wiai.lspi.util.logging.Logger.LogLevel.*;
/**
* This class represents an {@link Endpoint} for communication over socket
* protocol. It provides a <code>ServerSocket</code> to that clients can
* connect and starts for each incoming connection a
* {@link de.uniba.wiai.lspi.chord.com.socket.RequestHandler} that handles
* {@link de.uniba.wiai.lspi.chord.com.socket.Request}s for method invocations
* from remote nodes. These {@link de.uniba.wiai.lspi.chord.com.socket.Request}s
* are sent by one {@link SocketProxy} representing the node, that this is the
* endpoint for, at another node.
*
* @author sven
* @version 1.0.5
*/
public final class SocketEndpoint extends Endpoint implements Runnable {
/**
* Logger for this endpoint.
*/
private final static Logger logger = Logger.getLogger(SocketEndpoint.class);
private final static boolean debug = logger.isEnabledFor(DEBUG);
/**
* {@link Set} containing all {@link RequestHandler}s created by this
* endpoint.
*/
private Set<RequestHandler> handlers = new HashSet<RequestHandler>();
/**
* The Socket this endpoint listens to for connections.
*/
private ServerSocket mySocket = null;
/**
* The {@link java.util.concurrent.Executor} responsible for carrying out
* executions of methods with help of an instance of
* {@link InvocationThread}.
*/
private final ThreadPoolExecutor invocationExecutor = InvocationThread
.createInvocationThreadPool();
/**
* Creates a new <code>SocketEndpoint</code> for the given {@link Node}
* with {@link URL url}. <code>url</code> must have the protocol indexed
* by <code>{@link URL#SOCKET_PROTOCOL}</code> in the
* <code>{@link URL#KNOWN_PROTOCOLS}</code> array.
*
* @param node1
* The {@link Node} node this endpoint provides connections to.
* @param url1
* The {@link URL} of this endpoint.
*/
public SocketEndpoint(Node node1, URL url1) {
super(node1, url1);
SocketEndpoint.logger.info("Initialisation finished.");
}
/*
* (non-Javadoc)
*
* @see de.uniba.wiai.lspi.chord.com.Endpoint#openConnections()
*/
protected void openConnections() {
/* Open server socket on port specified by url */
try {
if (debug) {
SocketEndpoint.logger
.debug("Trying to open server socket on port "
+ this.url.getPort());
}
this.mySocket = new ServerSocket(this.url.getPort());
this.setState(LISTENING);
if (debug) {
SocketEndpoint.logger.debug("Server socket opened on port "
+ this.url.getPort() + ". Starting listener thread.");
}
/* and start thread to listen for incoming connections. */
Thread listenerThread = new Thread(this, "SocketEndpoint_"
+ this.url + "_Thread");
listenerThread.start();
if (debug) {
SocketEndpoint.logger.debug("Listener Thread " + listenerThread
+ "started. ");
}
} catch (IOException e) {
/* TODO: change type of exception */
throw new RuntimeException(
"SocketEndpoint could not listen on port "
+ this.url.getPort() + " " + e.getMessage());
}
}
/*
* (non-Javadoc)
*
* @see de.uniba.wiai.lspi.chord.com.Endpoint#entriesAcceptable()
*/
protected void entriesAcceptable() {
if (debug) {
SocketEndpoint.logger.debug("entriesAcceptable() called");
}
this.setState(ACCEPT_ENTRIES);
}
/*
* (non-Javadoc)
*
* @see de.uniba.wiai.lspi.chord.com.Endpoint#closeConnections()
*/
protected void closeConnections() {
this.setState(STARTED);
/* try to close socket */
try {
this.mySocket.close();
} catch (IOException e) {
/* should not occur */
if (debug) {
SocketEndpoint.logger.debug("Could not close socket "
+ this.mySocket, e);
}
}
this.invocationExecutor.shutdownNow();
/*
* Close outgoing connections.
*/
SocketProxy.shutDownAll();
}
/**
* Run method from {@link Runnable} to accept connections from clients. This
* method runs until {@link #closeConnections()} is called. It creates
* threads responsible for the handling of requests from other nodes.
*/
public void run() {
// Cleaner cleaner = new Cleaner();
while (this.getState() > STARTED) {
if (debug) {
SocketEndpoint.logger.debug("Waiting for incoming connection.");
}
Socket incomingConnection = null;
try {
incomingConnection = this.mySocket.accept();
if (debug) {
SocketEndpoint.logger.debug("Incoming connection "
+ incomingConnection);
}
/*
* Create a handler for requests that come in over the newly
* created socket.
*/
if (debug) {
SocketEndpoint.logger
.debug("Creating request handler for incoming connection.");
}
RequestHandler handler = new RequestHandler(this.node,
incomingConnection, this);
/*
* Remember handler to be able to close its connection (shut it
* down.
*/
this.handlers.add(handler);
/* Start handler thread */
if (debug) {
SocketEndpoint.logger
.debug("Request handler created. Starting thread.");
}
handler.start();
if (debug) {
SocketEndpoint.logger
.debug("Request handler thread started.");
}
} catch (IOException e) {
/* Can this happen? */
if ((this.getState() > STARTED)) {
if (debug) {
SocketEndpoint.logger.debug(
"Could not accept connection from other node!",
e);
}
if (incomingConnection != null) {
try {
incomingConnection.close();
} catch (IOException e1) {
// can be ignored, as incoming Connection is no longer needed.
}
incomingConnection = null;
}
} else {
/* Socket has been closed */
}
/* TODO: go on or notify some one? */
}
}
SocketEndpoint.logger.info("Listener thread stopped.");
/* Disconnect all */
for (RequestHandler handler : this.handlers) {
handler.disconnect();
}
this.handlers.clear();
}
/**
* Schedule an invocation of a local method to be executed.
*
* @param invocationThread
*/
void scheduleInvocation(InvocationThread invocationThread) {
if (debug) {
logger.debug("Scheduling invocation: " + invocationThread);
}
this.invocationExecutor.execute(invocationThread);
if (debug) {
logger.debug("Current jobs: "
+ this.invocationExecutor.getQueue().size());
logger.debug("Active jobs: "
+ this.invocationExecutor.getActiveCount());
logger.debug("Completed jobs: "
+ this.invocationExecutor.getCompletedTaskCount());
}
}
}