KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > net > SocketSendingTracker


1 package org.jivesoftware.messenger.net;
2
3 import org.jivesoftware.util.JiveGlobals;
4 import org.jivesoftware.util.Log;
5
6 import java.io.IOException JavaDoc;
7 import java.net.Socket JavaDoc;
8 import java.util.Date JavaDoc;
9 import java.util.Map JavaDoc;
10 import java.util.concurrent.ConcurrentHashMap JavaDoc;
11
12 /**
13  * A SocketSendingTracker keeps track of all the sockets that are currently sending data and
14  * checks the health of the sockets to detect hanged connections. If a sending operation takes
15  * too much time (i.e. exceeds a time limit) then it is assumed that the connection has been
16  * lost and for some reason the JVM has not been notified of the dead connection. Once a dead
17  * connection has been detected it will be closed so that the thread that was writing to the
18  * socket can resume. Resuming locked threads is important since otherwise a complete system halt
19  * may occur.<p>
20  *
21  * The time limit to wait before considering a connection dead can be configured changing the
22  * property <b>xmpp.session.sending-limit</b>. If the property was not defined then a default
23  * time limit of 60 seconds will be assumed. This means that by default if a sending operation
24  * takes longer than 60 seconds then the connection will be closed and the client disconnected.
25  * Therefore, it is important to not set a very low time limit since active clients may be
26  * incorrectly considered as dead clients.
27  *
28  * @author Gaston Dombiak
29  */

30 public class SocketSendingTracker {
31
32
33     private static SocketSendingTracker instance = new SocketSendingTracker();
34     /**
35      * Map that holds the sockets that are currently sending information together with the date
36      * when the sending operation started.
37      */

38     private Map JavaDoc<Socket JavaDoc, Date JavaDoc> sockets = new ConcurrentHashMap JavaDoc<Socket JavaDoc, Date JavaDoc>();
39
40     /**
41      * Flag that indicates if the tracket should shutdown the tracking process.
42      */

43     private boolean shutdown = false;
44
45     /**
46      * Thread used for checking periodically the health of the sockets involved in sending
47      * operations.
48      */

49     private Thread JavaDoc checkingThread;
50
51     /**
52      * Returns the unique instance of this class.
53      *
54      * @return the unique instance of this class.
55      */

56     public static SocketSendingTracker getInstance() {
57         return instance;
58     }
59
60     /**
61      * Hide the constructor so that only one instance of this class can exist.
62      */

63     private SocketSendingTracker() {
64     }
65
66     /**
67      * Register that the specified socket has started sending information. The registration will
68      * include the timestamp when the sending operation started so that if after several minutes
69      * it hasn't finished then the socket will be closed.
70      *
71      * @param socket the socket that started sending data.
72      */

73     public void socketStartedSending(Socket JavaDoc socket) {
74         sockets.put(socket, new Date JavaDoc());
75     }
76
77     /**
78      * Register that the specified socket has finished sending information. The socket will
79      * be removed from the tracking list.
80      *
81      * @param socket the socket that finished sending data.
82      */

83     public void socketFinishedSending(Socket JavaDoc socket) {
84         sockets.remove(socket);
85     }
86
87     /**
88      * Start up the daemon thread that will check for the health of the sockets that are
89      * currently sending data.
90      */

91     public void start() {
92         shutdown = false;
93         checkingThread = new Thread JavaDoc("SocketSendingTracker") {
94             public void run() {
95                 while (!shutdown) {
96                     checkHealth();
97                     synchronized (this) {
98                         try {
99                             wait(10000);
100                         }
101                         catch (InterruptedException JavaDoc e) {
102                         }
103                     }
104                 }
105             }
106         };
107         checkingThread.setDaemon(true);
108         checkingThread.start();
109     }
110
111     /**
112      * Indicates that the checking thread should be stoped. The thread will be waked up
113      * so that it can be stoped.
114      */

115     public void shutdown() {
116         shutdown = true;
117         // Use a wait/notify algorithm to ensure that the thread stops immediately if it
118
// was waiting
119
synchronized (checkingThread) {
120             checkingThread.notify();
121         }
122     }
123
124     /**
125      * Checks if a socket has been trying to send data for a given amount of time. If it has
126      * exceded a limit of time then the socket will be closed.<p>
127      *
128      * It is expected that sending operations will not take too much time so the checking will
129      * be very fast since very few sockets will be present in the Map and most or all of them
130      * will not exceed the time limit. Therefore, it is expected the overhead of this class to be
131      * quite small.
132      */

133     private void checkHealth() {
134         for (Socket JavaDoc socket : sockets.keySet()) {
135             Date JavaDoc startDate = sockets.get(socket);
136             if (startDate != null &&
137                     System.currentTimeMillis() - startDate.getTime() >
138                     JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
139                 // Check that the sending operation is still active
140
if (sockets.get(socket) != null) {
141                     // Close the socket
142
try {
143                         Log.debug("Closing socket: " + socket + " that started sending data at: " +
144                                 startDate);
145                         socket.close();
146                     }
147                     catch (IOException JavaDoc e) {
148                         Log.error("Error closing socket", e);
149                     }
150                     finally {
151                         // Remove tracking on this socket
152
sockets.remove(socket);
153                     }
154                 }
155             }
156
157         }
158     }
159 }
160
Popular Tags