Commit 78b773df authored by (no author)'s avatar (no author)

This commit was manufactured by cvs2svn to create branch

'jive_messenger_2_1_branch'.

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/branches@1403 b35dd754-fafc-0310-a699-88a17e54d16e
parent c15a0520
package org.jivesoftware.messenger.net;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A SocketSendingTracker keeps track of all the sockets that are currently sending data and
* checks the health of the sockets to detect hanged connections. If a sending operation takes
* too much time (i.e. exceeds a time limit) then it is assumed that the connection has been
* lost and for some reason the JVM has not been notified of the dead connection. Once a dead
* connection has been detected it will be closed so that the thread that was writing to the
* socket can resume. Resuming locked threads is important since otherwise a complete system halt
* may occur.<p>
*
* The time limit to wait before considering a connection dead can be configured changing the
* property <b>xmpp.session.sending-limit</b>. If the property was not defined then a default
* time limit of 60 seconds will be assumed. This means that by default if a sending operation
* takes longer than 60 seconds then the connection will be closed and the client disconnected.
* Therefore, it is important to not set a very low time limit since active clients may be
* incorrectly considered as dead clients.
*
* @author Gaston Dombiak
*/
public class SocketSendingTracker {
private static SocketSendingTracker instance = new SocketSendingTracker();
/**
* Map that holds the sockets that are currently sending information together with the date
* when the sending operation started.
*/
private Map<Socket, Date> sockets = new ConcurrentHashMap<Socket, Date>();
/**
* Flag that indicates if the tracket should shutdown the tracking process.
*/
private boolean shutdown = false;
/**
* Thread used for checking periodically the health of the sockets involved in sending
* operations.
*/
private Thread checkingThread;
/**
* Returns the unique instance of this class.
*
* @return the unique instance of this class.
*/
public static SocketSendingTracker getInstance() {
return instance;
}
/**
* Hide the constructor so that only one instance of this class can exist.
*/
private SocketSendingTracker() {
}
/**
* Register that the specified socket has started sending information. The registration will
* include the timestamp when the sending operation started so that if after several minutes
* it hasn't finished then the socket will be closed.
*
* @param socket the socket that started sending data.
*/
public void socketStartedSending(Socket socket) {
sockets.put(socket, new Date());
}
/**
* Register that the specified socket has finished sending information. The socket will
* be removed from the tracking list.
*
* @param socket the socket that finished sending data.
*/
public void socketFinishedSending(Socket socket) {
sockets.remove(socket);
}
/**
* Start up the daemon thread that will check for the health of the sockets that are
* currently sending data.
*/
public void start() {
shutdown = false;
checkingThread = new Thread("SocketSendingTracker") {
public void run() {
while (!shutdown) {
checkHealth();
synchronized (this) {
try {
wait(10000);
}
catch (InterruptedException e) {
}
}
}
}
};
checkingThread.setDaemon(true);
checkingThread.start();
}
/**
* Indicates that the checking thread should be stoped. The thread will be waked up
* so that it can be stoped.
*/
public void shutdown() {
shutdown = true;
// Use a wait/notify algorithm to ensure that the thread stops immediately if it
// was waiting
synchronized (checkingThread) {
checkingThread.notify();
}
}
/**
* Checks if a socket has been trying to send data for a given amount of time. If it has
* exceded a limit of time then the socket will be closed.<p>
*
* It is expected that sending operations will not take too much time so the checking will
* be very fast since very few sockets will be present in the Map and most or all of them
* will not exceed the time limit. Therefore, it is expected the overhead of this class to be
* quite small.
*/
private void checkHealth() {
for (Socket socket : sockets.keySet()) {
Date startDate = sockets.get(socket);
if (startDate != null &&
System.currentTimeMillis() - startDate.getTime() >
JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
// Check that the sending operation is still active
if (sockets.get(socket) != null) {
// Close the socket
try {
Log.debug("Closing socket: " + socket + " that started sending data at: " +
startDate);
socket.close();
}
catch (IOException e) {
Log.error("Error closing socket", e);
}
finally {
// Remove tracking on this socket
sockets.remove(socket);
}
}
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment