001: /*
002: * This file is part of the QuickServer library
003: * Copyright (C) QuickServer.org
004: *
005: * Use, modification, copying and distribution of this software is subject to
006: * the terms and conditions of the GNU Lesser General Public License.
007: * You should have received a copy of the GNU LGP License along with this
008: * library; if not, you can download a copy from <http://www.quickserver.org/>.
009: *
010: * For questions, suggestions, bug-reports, enhancement-requests etc.
011: * visit http://www.quickserver.org
012: *
013: */
014:
015: package org.quickserver.net.server;
016:
017: import java.util.*;
018: import java.io.*;
019: import java.net.*;
020:
021: import org.quickserver.net.*;
022: import org.quickserver.util.pool.thread.*;
023: import org.quickserver.util.*;
024: import org.quickserver.net.server.impl.*;
025:
026: import java.util.logging.*;
027:
028: /**
029: * Class (Server Hook) that closes any dead (ghost) sockets that are no
030: * longer connected or communicating.
031: * <p>
032: * It runs as a daemon thread. This thread will simply return if the socket
033: * timeout is set to <= 0 in QuickServer. It will close any socket that
034: * has not sent in any communication for more than the socket timeout set,
035: * i.e., if socket timeout is set to 1000 miliseconds then
036: * if a client socket has not communicated for more than 1 seconds then this
037: * thread will close the socket and returns the ClientHandler to the pool.
038: * </p>
039: * @author Akshathkumar Shetty
040: * @since 1.3.3
041: */
042: public class GhostSocketReaper extends Thread implements ServerHook {
043: private static Logger logger = Logger
044: .getLogger(GhostSocketReaper.class.getName());
045:
046: private long time = 1;
047: private QuickServer quickserver;
048: private volatile boolean stopFlag;
049:
050: private long timeOut = 0;
051: private long timeOutDelay = 0;
052: private ClientIdentifier clientIdentifier = null;
053: private List notifiedGhostList = Collections
054: .synchronizedList(new ArrayList());
055:
056: public void initHook(QuickServer quickserver) {
057: this .quickserver = quickserver;
058: clientIdentifier = quickserver.getClientIdentifier();
059: }
060:
061: public boolean handleEvent(int event) {
062: if (event == ServerHook.POST_STARTUP) {
063: //logger.finest("Startup Event");
064: setDaemon(true);
065: stopFlag = false;
066: setName("GhostSocketReaper-For-(" + quickserver + ")");
067: start();
068: return true;
069: } else if (event == ServerHook.POST_SHUTDOWN) {
070: //logger.finest("Shutdown Event");
071: stopFlag = true;
072: return true;
073: }
074: return false;
075: }
076:
077: public String info() {
078: StringBuffer sb = new StringBuffer();
079: sb.append("GhostSocketReaper - ServerHook");
080: return sb.toString();
081: }
082:
083: public void run() {
084: logger.fine("Starting GhostSocketReaper thread - "
085: + quickserver.getName());
086: if (quickserver.getTimeout() <= 0) {
087: stopFlag = true;
088: logger.info("Timeout is less than 0, so will exit - "
089: + quickserver.getName());
090: return;
091: }
092: timeOut = quickserver.getTimeout();
093:
094: if (quickserver.getBasicConfig().getServerMode().getBlocking() == true)
095: timeOutDelay = 1000; //so that it can use SocketTimeoutException
096:
097: List ghostList = new ArrayList();
098: long searchSleepTime = timeOut / 2;
099: int failCount = 0;
100: boolean flag = false;
101: while (stopFlag == false) {
102: try {
103: try {
104: sleep(searchSleepTime);
105: } catch (InterruptedException ie) {
106: logger.warning("InterruptedException : "
107: + ie.getMessage());
108: }
109:
110: if (failCount < 4) {
111: flag = optimisticGhostSocketsFinder(ghostList);
112: if (flag == false)
113: failCount++;
114: else
115: failCount = 0;
116: } else {
117: syncGhostSocketsFinder(ghostList);
118: failCount = 0;
119: }
120: cleanGhostSockets(ghostList, true);
121:
122: } catch (Exception e) {
123: logger.fine("Exception : " + e);
124: if (Assertion.isEnabled()) {
125: logger.finest("StackTrace:\n"
126: + MyString.getStackTrace(e));
127: }
128: }
129: }//end of while
130:
131: //wait till all client have disconnected.. then clean the pool
132: while (stopFlag == true) {
133: try {
134: try {
135: sleep(searchSleepTime);
136: } catch (InterruptedException ie) {
137: logger.warning("InterruptedException : "
138: + ie.getMessage());
139: }
140:
141: if (quickserver.isClosed() == false) {
142: break;
143: }
144:
145: if (quickserver.getClientCount() <= 0) {
146: try {
147: Thread.yield();
148: sleep(1000);//lets wait for objects to come back to pool
149: } catch (InterruptedException ie) {
150: logger.warning("InterruptedException : "
151: + ie.getMessage());
152: }
153: quickserver.closeAllPools();
154: break;
155: }
156: } catch (Exception e) {
157: logger.fine("Exception : " + e);
158: if (Assertion.isEnabled()) {
159: logger.finest("StackTrace:\n"
160: + MyString.getStackTrace(e));
161: }
162: break;
163: }
164: }
165: logger.info("Returning from GhostSocketReaper thread - "
166: + quickserver.getName());
167: }
168:
169: private long getCurrentTime() {
170: return new Date().getTime();
171: }
172:
173: private boolean optimisticGhostSocketsFinder(List list) {
174: Iterator iterator = null;
175: ClientHandler clientHandler = null;
176: int count = 0;
177: long currentTime = getCurrentTime();
178: try {
179: iterator = clientIdentifier.findAllClient();
180: /*
181: if(iterator.hasNext()) {
182: logger.finest("ENTER");
183: }
184: */
185:
186: while (iterator.hasNext()) {
187: count++;
188: if (count == 60) {
189: currentTime = getCurrentTime(); //update time
190: count = 1;
191: }
192: clientHandler = (ClientHandler) iterator.next();
193:
194: checkClientHandlerForGhostSocket(clientHandler,
195: currentTime, list);
196:
197: if (list.size() > 100) {
198: logger
199: .finest("Found about 100 ghost sockets, lets clean..");
200: break;
201: }
202: }//end of while
203: } catch (ConcurrentModificationException e) {
204: //ignore
205: return false;
206: }
207: return true;
208: }
209:
210: private void syncGhostSocketsFinder(List list) {
211: Iterator iterator = null;
212: ClientHandler clientHandler = null;
213: int count = 0;
214: long currentTime = getCurrentTime();
215: synchronized (clientIdentifier.getObjectToSynchronize()) {
216: iterator = clientIdentifier.findAllClient();
217: if (iterator.hasNext())
218: logger.finest("ENTER");
219:
220: while (iterator.hasNext()) {
221: count++;
222: if (count == 60) {
223: currentTime = getCurrentTime(); //update time
224: count = 1;
225: }
226: clientHandler = (ClientHandler) iterator.next();
227:
228: checkClientHandlerForGhostSocket(clientHandler,
229: currentTime, list);
230:
231: if (list.size() > 100) {
232: logger
233: .finest("Found about 100 ghost sockets, lets clean..");
234: break;
235: }
236: }//end of while
237: }
238: }
239:
240: private void cleanGhostSockets(List list, boolean checkTimeout) {
241: long currentTime = getCurrentTime();
242: long commTime = 0;
243: long diff = -1;
244: ClientHandler clientHandler = null;
245:
246: int size = list.size();
247: for (int i = 0; i < size; i++) {
248: try {
249: clientHandler = (ClientHandler) list.get(i);
250:
251: if (((BasicClientHandler) clientHandler).getWillClean() == true) {
252: logger.finest("Not closing client " + clientHandler
253: + ", WillClean is true");
254: continue;
255: }
256:
257: if (checkTimeout) {
258: timeOut = clientHandler.getTimeout() + timeOutDelay;
259: if (clientHandler.getLastCommunicationTime() != null) {
260: commTime = clientHandler
261: .getLastCommunicationTime().getTime();
262: diff = currentTime - commTime;
263:
264: if (diff < timeOut
265: && clientHandler.isClosed() == false) {
266: logger
267: .finest("Not closing client "
268: + clientHandler
269: + ", must have been reassigned. CommTime(sec): "
270: + commTime / 1000
271: + ", Diff(sec): " + diff
272: / 1000);
273: continue;
274: }
275: }
276: }//end of if checkTimeout
277:
278: if (clientHandler.isClosed() == false) {
279: logger.finest("Closing client "
280: + clientHandler.getName());
281: try {
282: if (clientHandler
283: .hasEvent(ClientEvent.RUN_BLOCKING) == true) {
284: clientHandler.closeConnection();
285: } else {
286: if (((NonBlockingClientHandler) clientHandler)
287: .getThreadAccessCount() != 0) {
288: clientHandler.closeConnection();
289:
290: Object obj = clientHandler
291: .getInputStream();
292: if (obj != null) {
293: synchronized (obj) {
294: clientHandler.getInputStream()
295: .notifyAll();
296: }
297: }
298: } else {
299: clientHandler
300: .addEvent(ClientEvent.CLOSE_CON);
301: quickserver.getClientPool().addClient(
302: clientHandler);
303: }
304: }
305: } catch (Exception er) {
306: logger.fine("Error closing client "
307: + clientHandler);
308: clientHandler.forceClose();
309: }
310: } else {
311: if (clientHandler
312: .hasEvent(ClientEvent.RUN_BLOCKING) == false) {
313: logger.finest("Notifying IO of client "
314: + clientHandler);
315: Object obj = clientHandler.getInputStream();
316: if (obj == null)
317: continue;
318: synchronized (obj) {
319: clientHandler.getInputStream().notifyAll();
320: }
321:
322: logger.finest("Returning objs to pool "
323: + clientHandler);
324: if (quickserver.getClientDataPool() != null
325: && clientHandler.getClientData() != null)
326: quickserver.getClientDataPool()
327: .returnObject(
328: clientHandler
329: .getClientData());
330:
331: logger.finest(Thread.currentThread().getName()
332: + " returning " + getName());
333: quickserver.getClientHandlerPool()
334: .returnObject(clientHandler);
335: } else {
336: logger
337: .finest("Skipping closed "
338: + clientHandler
339: + " since in blocking mode.. this should clean up it self.");
340: }
341: }
342: } catch (Exception ee) {
343: logger.fine("Exception forcing the close : " + ee);
344: if (Assertion.isEnabled()) {
345: logger.finest("StackTrace:\n"
346: + MyString.getStackTrace(ee));
347: }
348: }
349: }//end of for
350: list.clear();
351: }
352:
353: private void checkClientHandlerForGhostSocket(
354: ClientHandler clientHandler, long currentTime, List list) {
355: if (clientHandler == null)
356: return;
357:
358: if (clientHandler.isOpen() == false) {
359: logger
360: .finest("Not connected .. so returning ClientData and ClientHandler objects for : "
361: + clientHandler);
362: list.add(clientHandler);
363: return;
364: }
365:
366: if (clientHandler.getTimeout() <= 0) {
367: //logger.finest("ClientHandler's timeout is <=0, so skipping.");
368: return;
369: }
370:
371: if (clientHandler.getLastCommunicationTime() == null)
372: return;
373:
374: long commTime = clientHandler.getLastCommunicationTime()
375: .getTime();
376: long diff = currentTime - commTime;
377:
378: long timeOut = clientHandler.getTimeout() + timeOutDelay;
379: if (diff >= timeOut) {
380: logger.finest("Closable client " + clientHandler
381: + ", Time diff(sec) : " + diff / 1000);
382: list.add(clientHandler);
383: }
384: }
385: }
|