KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cofax > connectionpool > ConnectionPool


1 /*
2  * ConnectionPool is part of the Cofax content management system library.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Please see http://www.cofax.org for contact information and other related informaion.
19  *
20  * $Header: /cvsroot/cofax/cofax/src/org/cofax/connectionpool/ConnectionPool.java,v 1.3.2.1 2006/12/11 16:33:22 fxrobin Exp $
21  */

22
23 package org.cofax.connectionpool;
24
25 import java.sql.*;
26 import java.util.*;
27 import java.io.*;
28 import org.cofax.CofaxUtil;
29
30 public class ConnectionPool {
31
32     final static boolean DEBUG = false;
33
34     private String JavaDoc name;
35
36     private String JavaDoc URL;
37
38     private String JavaDoc user;
39
40     private String JavaDoc password;
41
42     private int maxConns;
43
44     private int timeOut;
45
46     private int initConns;
47
48     private int connUsageLimit;
49
50     private int peakInUse;
51
52     private int avgInUse;
53
54     private LogWriter logWriter;
55
56     private String JavaDoc testQuery;
57
58     private long connUsageTimeToDie = CofaxUtil.MILLISECONDS_PER_SECOND * 30; // milisecond
59

60     // time
61
// to
62
// kill
63
// a
64
// connection
65

66     private long numConnCleanUps;
67
68     private long numConnsCleaned;
69
70     private long peakRunTime;
71
72     private long numConnsReq;
73
74     private long numConnsDel;
75
76     private long numConnsRet;
77
78     private long totalRunTime;
79
80     private long totalInUse;
81
82     private long numWait;
83
84     private double avgRunTime;
85
86     private Vector freeConnections = new Vector();
87
88     private Vector usedConnections = new Vector();
89
90     public ConnectionPool(String JavaDoc name, String JavaDoc URL, String JavaDoc user, String JavaDoc password, int maxConns, int inInitConns, int inTimeOut, PrintWriter pw, int logLevel,
91             String JavaDoc testQuery, int connUsageLimit) {
92
93         this.name = name;
94         this.URL = URL;
95         this.user = user;
96         this.password = password;
97         this.maxConns = maxConns;
98         this.initConns = inInitConns;
99         this.testQuery = testQuery;
100         this.connUsageLimit = connUsageLimit;
101         this.timeOut = inTimeOut > 0 ? inTimeOut : 5;
102         this.numConnCleanUps = 0;
103         this.numConnsCleaned = 0;
104         this.peakInUse = 1;
105         this.peakRunTime = 0;
106         this.avgRunTime = 0.0;
107         this.avgInUse = 0;
108         this.numConnsReq = 0;
109         this.numConnsDel = 0;
110         this.numConnsRet = 0;
111         this.totalRunTime = 0;
112         this.totalInUse = 0;
113         this.numWait = 0;
114
115         logWriter = new LogWriter(name, logLevel, pw);
116         creatInitConnections();
117
118         logWriter.log("SYS: New pool created", LogWriter.INFO);
119         String JavaDoc lf = System.getProperty("line.separator");
120         logWriter.log(lf + " url=" + URL + lf + " user=" + user + lf + " password=" + password + lf + " initconns=" + initConns + lf + " maxconns=" + maxConns
121                 + lf + " testQuery=" + testQuery + lf + " connUsageLimit=" + connUsageLimit + lf + " logintimeout=" + this.timeOut, LogWriter.DEBUG);
122         logWriter.log(getStats(), LogWriter.DEBUG);
123     }
124
125     public String JavaDoc getURL() {
126         return URL;
127     }
128
129     public String JavaDoc getUser() {
130         return user;
131     }
132
133     public String JavaDoc getPassword() {
134         return password;
135     }
136
137     public int getMaxConns() {
138         return maxConns;
139     }
140
141     public int getInitConns() {
142         return initConns;
143     }
144
145     public int getTimeOut() {
146         return timeOut;
147     }
148
149     public int getConUsageLimit() {
150         return connUsageLimit;
151     }
152
153     public long getKillTime() {
154         return connUsageTimeToDie;
155     }
156
157     public void setURL(String JavaDoc inURL) {
158         URL = inURL;
159     }
160
161     public void setUser(String JavaDoc inUser) {
162         user = inUser;
163     }
164
165     public void setPassword(String JavaDoc inPassowrd) {
166         password = inPassowrd;
167     }
168
169     public void setMaxConns(int inMaxConns) {
170         maxConns = inMaxConns;
171     }
172
173     public void setInitConns(int inInitConns) {
174         initConns = inInitConns;
175     }
176
177     public void setTimeOut(int inTimeOut) {
178         timeOut = inTimeOut;
179     }
180
181     public void setConUsageLimit(int inConnUsageLimit) {
182         connUsageLimit = inConnUsageLimit;
183     }
184
185     public void setKillTime(long inKillTime) {
186         connUsageTimeToDie = inKillTime;
187     }
188
189     private void creatInitConnections() {
190
191         int curCons = freeConnections.size() + usedConnections.size();
192
193         for (int i = curCons; i < this.initConns; i++) {
194             try {
195                 ConnectionWrapper newCon = newConnection();
196                 freeConnections.addElement(newCon);
197             } catch (SQLException e) {
198                 logWriter.log("Error Creating Init Connection:" + e.toString(), LogWriter.ERROR);
199             }
200         }
201     }
202
203     public ConnectionWrapper getConnection() throws SQLException {
204         // logWriter.log("Request for connection received", LogWriter.DEBUG) ;
205
try {
206             ConnectionWrapper conn = getConnection(timeOut * 1000);
207             return conn;
208         } catch (SQLException e) {
209             logWriter.log(e, "Exception getting connection", LogWriter.ERROR);
210             // 20010823 Sam-- Commented this out for now to stop stack trace in
211
// the log file.
212
throw e;
213         }
214     }
215
216     public synchronized void freeConnection(ConnectionWrapper conn) {
217
218         // Put the connection at the end of the Vector
219
java.util.Date JavaDoc currDate = new java.util.Date JavaDoc();
220         long curTime = currDate.getTime();
221         long timeInUse;
222
223         this.numConnsRet++;
224         timeInUse = curTime - conn.getLastTimeUsed();
225         this.totalRunTime += timeInUse;
226         this.avgRunTime = ((double) this.totalRunTime / this.numConnsRet);
227         conn.setLastTimeUsed();
228         freeConnections.addElement(conn);
229         usedConnections.remove(conn);
230         // notifyAll() ; ###!!!### 20010904 -SAC just let any waiting
231
// connections wait their few miliseconds.
232
logWriter.log("CON: Released ConnectionID: " + conn.getConnectionId() + " |was in use for: " + timeInUse + " mSecs |was running: "
233                 + conn.getCurrentActivity(), LogWriter.INFO);
234         logWriter.log(getStats(), LogWriter.DEBUG);
235
236     }
237
238     private synchronized ConnectionWrapper getConnection(long timeout) throws SQLException {
239
240         // Get a pooled Connection from the cache or a new one.
241
// Wait if all are checked out and the max limit has
242
// been reached.
243

244         long startTime = System.currentTimeMillis();
245         long remaining = timeout;
246         ConnectionWrapper conn = null;
247         long sleepTime = (long) (remaining / 4); // sleep for 1/4 of the
248
// timeout and try again
249

250         while ((conn = getPooledConnection()) == null) {
251             try {
252                 logWriter.log("CON: All Connections in use, waiting for: " + remaining + " mSecs", LogWriter.DEBUG);
253                 wait(sleepTime);
254                 numWait++;
255             } catch (InterruptedException JavaDoc e) {
256             }
257             remaining = timeout - (System.currentTimeMillis() - startTime);
258             if (remaining <= 0) {
259                 // Timeout has expired
260
logWriter.log("ERR: Timed out waiting for con, throwing error...", LogWriter.DEBUG);
261                 logWriter.log(getStats(), LogWriter.DEBUG);
262                 throw new SQLException("getConnection() timed-out");
263             }
264         }
265
266         // Check if the Connection is still OK
267
if (!isConnectionOK(conn)) {
268             // It was bad. Try again with the remaining timeout
269
return getConnection(remaining);
270         }
271
272         // check the number of connection and rebuild if nessicary
273
// only do this down here because it means we got a new connection and
274
// the db is ok
275
if (freeConnections.size() + usedConnections.size() < this.initConns) {
276             logWriter.log("SYS: Connection Pool below initial size, rebuilding now...", LogWriter.INFO);
277             this.creatInitConnections();
278         }
279
280         java.util.Date JavaDoc currDate = new java.util.Date JavaDoc();
281         long curTime = currDate.getTime();
282         long timeSinceCreated = curTime - conn.getTimeCreated();
283         logWriter.log("CON: Pool Use ConnectionID: " + conn.getConnectionId() + " |Use Count: " + conn.getTimesUsed() + " |Time Alive: " + timeSinceCreated
284                 + " mSecs", LogWriter.INFO);
285         logWriter.log(getStats(), LogWriter.DEBUG);
286         return conn;
287
288     }
289
290     private boolean isConnectionOK(ConnectionWrapper conn) {
291
292         if (conn == null)
293             return false;
294
295         long connId = conn.getConnectionId();
296         if (DEBUG)
297             logWriter.log("CON: Checking ConnectionID: " + connId, LogWriter.DEBUG);
298
299         if (conn.getTimesUsed() > connUsageLimit) {
300             try {
301                 usedConnections.remove(conn);
302                 conn.closeConnection();
303                 conn = null;
304                 if ((this.freeConnections.size() + this.usedConnections.size()) < this.initConns) {
305                     ConnectionWrapper newCon = newConnection();
306                     freeConnections.addElement(newCon);
307                 }
308             } catch (SQLException se) {
309                 logWriter.log("ERR: Error closing connection: " + se, LogWriter.DEBUG);
310             } finally {
311                 conn = null;
312             }
313             logWriter.log("CON: Use limit reached ConnectionID: " + connId, LogWriter.DEBUG);
314             return false;
315         }
316         Statement testStmt = null;
317         try {
318             if (!conn.isClosed()) {
319                 // Try to createStatement to see if it's really alive
320
testStmt = conn.createStatement();
321                 testStmt.execute(testQuery);
322                 testStmt.close();
323
324             } else {
325                 logWriter.log("ERR: Connection was already closed!", LogWriter.ERROR);
326                 throw new SQLException("Connection was already closed");
327             }
328         } catch (SQLException e) {
329             logWriter.log("ERR: Testing of the connection failed: " + e.getMessage(), LogWriter.ERROR);
330             // Closing statement for bad connection.
331
if (testStmt != null) {
332                 try {
333                     testStmt.close();
334                 } catch (SQLException se) {
335                     logWriter.log("ERR: Error closing connection: " + se, LogWriter.ERROR);
336                 }
337             }
338             // Closing bad connection.
339
if (conn != null) {
340                 try {
341                     logWriter.log("DEBUG: Removing connection from pool", LogWriter.DEBUG);
342                     usedConnections.remove(conn);
343                     conn.closeConnection();
344                     conn = null;
345                 } catch (SQLException se) {
346                     logWriter.log("ERR : Error removing bad connection: " + se.getMessage(), LogWriter.ERROR);
347                 }
348             }
349             return false;
350         }
351         return true;
352
353     }
354
355     /**
356      * Function: get Purpose: To search in use connections for any that have
357      * "run away" and should be killed Args: Returns: true - if any connection
358      * have been killed false - if all connection were fine Author: Sam Cohen
359      * Created: 7/23/2001 Notes: the connection pool calls this function when
360      * the max number of connection is reached and it needs a new connection. So
361      * all might be normal when this is called in which case the calling
362      * function uses the return value of false to continue on and make new
363      * connections wait. If it gets a true back then it can issue a brand new
364      * connection because we killed on in here. Revision History: 8/23/2001 -
365      * added new first if clause to rebuild conns to init level should they fall
366      * below, due to a db restart or other loss of connections.
367      */

368     private ConnectionWrapper getPooledConnection() {
369
370         ConnectionWrapper conn = null;
371
372         this.numConnsReq++;
373         this.runBadConnCleanUp();
374         if (freeConnections.size() > 0) {
375             conn = (ConnectionWrapper) freeConnections.firstElement();
376             conn.incrementTimesUsed();
377             freeConnections.remove(conn);
378         } else if (maxConns == 0 || (freeConnections.size() + usedConnections.size() < maxConns)) {
379             try {
380                 conn = newConnection();
381                 conn.incrementTimesUsed();
382             } catch (Exception JavaDoc e) {
383             }
384         }
385         if (conn != null) {
386             usedConnections.addElement(conn);
387             this.numConnsDel++;
388         }
389
390         this.totalInUse += usedConnections.size();
391         this.avgInUse = (int) (this.totalInUse / this.numConnsReq);
392         if (usedConnections.size() > this.peakInUse)
393             this.peakInUse = usedConnections.size();
394
395         return conn;
396
397     }
398
399     /**
400      * Function: runBadConnCleanUp Purpose: To search in use connections for any
401      * that have "run away" and should be killed Args: Returns: true - if any
402      * connection have been killed false - if all connection were fine Author:
403      * Sam Cohen Created: 7/23/2001 Notes: the connection pool calls this
404      * function when the max number of connection is reached and it needs a new
405      * connection. So all might be normal when this is called in which case the
406      * calling function uses the return value of false to continue on and make
407      * new connections wait. If it gets a true back then it can issue a brand
408      * new connection because we killed on in here. Revision History:
409      */

410     private synchronized boolean runBadConnCleanUp() {
411
412         boolean cleaned = false;
413
414         if (usedConnections.size() > 0) {
415             this.numConnCleanUps++;
416
417             Enumeration uCons = usedConnections.elements();
418             ConnectionWrapper con;
419             java.util.Date JavaDoc currDate = new java.util.Date JavaDoc();
420             long curTime = currDate.getTime();
421             long timeInUse;
422
423             logWriter.log("SYS: Checking for errant connections", LogWriter.INFO);
424             while (uCons.hasMoreElements()) {
425                 con = (ConnectionWrapper) uCons.nextElement();
426                 timeInUse = curTime - con.getLastTimeUsed();
427                 if (timeInUse > this.peakRunTime)
428                     this.peakRunTime = timeInUse;
429
430                 if (timeInUse >= this.connUsageTimeToDie) {
431                     try {
432                         logWriter.log("CON: Killing errant ConnectionID: " + con.getConnectionId() + " |Use Time: " + timeInUse + " mSecs |Query: "
433                                 + con.getCurrentActivity(), LogWriter.INFO);
434                         usedConnections.remove(con);
435                         con.closeConnection();
436                         con = null;
437                         this.numConnsCleaned++;
438                         cleaned = true;
439                     } catch (Exception JavaDoc e) {
440                         logWriter.log("ERR: Could not kill errant ConnectionID : " + con.getConnectionId() + " |Reason: " + e.toString(), LogWriter.ERROR);
441                     }
442                 }
443             }
444         }
445         return cleaned;
446
447     }
448
449     private ConnectionWrapper newConnection() throws SQLException {
450
451         Connection conn = null;
452         if (user == null)
453             conn = DriverManager.getConnection(URL);
454         else
455             conn = DriverManager.getConnection(URL, user, password);
456         ConnectionWrapper newCon = new ConnectionWrapper(conn, this);
457         logWriter.log("CON: Created a new ConnectionID: " + newCon.getConnectionId(), LogWriter.INFO);
458         return newCon;
459
460     }
461
462     public synchronized void release() {
463
464         Enumeration allConnections = freeConnections.elements();
465         while (allConnections.hasMoreElements()) {
466             ConnectionWrapper con = (ConnectionWrapper) allConnections.nextElement();
467             long connId = con.getConnectionId();
468             try {
469                 logWriter.log("CON: Closed ConnectionID: " + connId, LogWriter.INFO);
470                 freeConnections.remove(con);
471                 con.closeConnection();
472                 con = null;
473             } catch (SQLException e) {
474                 logWriter.log(e, "ERR: Couldn't close connection", LogWriter.ERROR);
475             }
476         }
477         freeConnections.removeAllElements();
478
479         allConnections = usedConnections.elements();
480         while (allConnections.hasMoreElements()) {
481             ConnectionWrapper con = (ConnectionWrapper) allConnections.nextElement();
482             long connId = con.getConnectionId();
483             try {
484                 logWriter.log("CON: Closed ConnectionID: " + connId, LogWriter.INFO);
485                 usedConnections.remove(con);
486                 con.closeConnection();
487                 con = null;
488             } catch (SQLException e) {
489                 logWriter.log(e, "ERR: Couldn't close connection", LogWriter.ERROR);
490             }
491         }
492         usedConnections.removeAllElements();
493
494     }
495
496     private String JavaDoc getStats() {
497
498         String JavaDoc retVal = "STAT: Total connections: " + (freeConnections.size() + usedConnections.size()) + " |Available: " + freeConnections.size()
499                 + " |In Use: " + usedConnections.size();
500         return (retVal);
501
502     }
503
504     /**
505      * Function: getConnectionStats Purpose: To display all in-use and free
506      * database connection and what they are doing or did last Args: Returns: An
507      * HTML formatted string which can display to a browser the connection pool
508      * info Author: Sam Cohen Created: 7/23/2001 Notes: Should be called from
509      * the admin servlet which will decide where/how to display this Revision
510      * History:
511      */

512     public String JavaDoc getConnectionStats() {
513
514         String JavaDoc outPut;
515         Enumeration uCons = usedConnections.elements();
516         Enumeration fCons = freeConnections.elements();
517         ConnectionWrapper con;
518         java.util.Date JavaDoc currDate = new java.util.Date JavaDoc();
519         long curTime = currDate.getTime();
520         long timeSinceCreated;
521         long timeSinceUsed;
522
523         long uConns = usedConnections.size();
524         long fConns = freeConnections.size();
525         long tConns = uConns + fConns;
526         long tDie = this.connUsageTimeToDie / CofaxUtil.MILLISECONDS_PER_SECOND;
527         long tPRt = this.peakRunTime / CofaxUtil.MILLISECONDS_PER_SECOND;
528         double tAvgRt = this.avgRunTime / CofaxUtil.MILLISECONDS_PER_SECOND;
529
530         // generate the general stats table
531
outPut = "<br><br><table border=1>\n";
532         outPut += "<tr><td colspan=2 bgColor=FFEEFF align=center><font size=4>Connection Pool Settings</font></td></tr>\n";
533         outPut += "<tr><td bgColor=FFEEFF>Connection Pool Name:</td><td>" + this.name + "</td></tr>\n";
534         outPut += "<tr><td bgColor=FFEEFF>Initial Number of Connections:</td><td>" + this.initConns + "</td></tr>\n";
535         outPut += "<tr><td bgColor=FFEEFF>Max Number of Connections:</td><td>" + this.maxConns + "</td></tr>\n";
536         outPut += "<tr><td bgColor=FFEEFF>Usage Limit for Conns</td><td>" + this.connUsageLimit + "</td></tr>\n";
537         outPut += "<tr><td bgColor=FFEEFF>Wait for Conn Time:</td><td>" + this.timeOut + "</td></tr>\n";
538         outPut += "<tr><td bgColor=FFEEFF>Time before In-use con is killed:</td><td>" + tDie + "</td></tr>\n";
539
540         outPut += "<tr><td colspan=2 bgColor=FFEEFF align=center><font size=4>Connection Pool Stats</font></td></tr>\n";
541         outPut += "<tr><td bgColor=FFEEFF>Current Connections Total/In Use/Avail:</td>\n";
542         outPut += "<td>" + tConns + "/" + uConns + "/" + fConns + "</td></tr>\n";
543         outPut += "<tr><td bgColor=FFEEFF>Total Number of Cons Requeted</td><td>" + this.numConnsReq + "</td></tr>\n";
544         outPut += "<tr><td bgColor=FFEEFF>Total Number of Cons Delivered</td><td>" + this.numConnsDel + "</td></tr>\n";
545         outPut += "<tr><td bgColor=FFEEFF>Total Number of Cons Returned</td><td>" + this.numConnsRet + "</td></tr>\n";
546         outPut += "<tr><td bgColor=FFEEFF>Numer of Requests which had to wait</td><td>" + this.numWait + "</td></tr>\n";
547         outPut += "<tr><td bgColor=FFEEFF>Average Number of Cons in Use(at any time)</td><td>" + this.avgInUse + "</td></tr>\n";
548         outPut += "<tr><td bgColor=FFEEFF>Peak Number of Cons is Use:</td><td>" + this.peakInUse + "</td></tr>\n";
549         outPut += "<tr><td bgColor=FFEEFF>Average Con Use Time</td><td>" + tAvgRt + "</td></tr>\n";
550         outPut += "<tr><td bgColor=FFEEFF>Peak Con Use Time:</td><td>" + tPRt + "</td></tr>\n";
551         outPut += "<tr><td bgColor=FFEEFF>Number of times Con cleanup has run:</td><td>" + this.numConnCleanUps + "</td></tr>\n";
552         outPut += "<tr><td bgColor=FFEEFF>Number of cleaned Cons :</td><td>" + this.numConnsCleaned + "</td></tr>\n";
553
554         outPut += "</table>\n";
555
556         // generate the used connection stats table
557
outPut += "<br><br><table border=1>\n";
558         outPut += "<tr><td colspan=5 bgColor=FFEEFF align=center><font size=4>In Use Connections(" + uConns + ")</font></td></tr>\n";
559         outPut += "<tr><td bgColor=FFEEFF>ConnectionID</td>\n";
560         outPut += "<td bgColor=FFEEFF>Number of times used</td>\n";
561         outPut += "<td bgColor=FFEEFF>Time Since Created(Sec)</td>\n";
562         outPut += "<td bgColor=FFEEFF>Time since this use started(Sec)</td>\n";
563         outPut += "<td bgColor=FFEEFF>Current Activity</td></tr>\n";
564         while (uCons.hasMoreElements()) {
565             con = (ConnectionWrapper) uCons.nextElement();
566             timeSinceCreated = ((curTime - con.getTimeCreated()) / CofaxUtil.MILLISECONDS_PER_SECOND);
567             timeSinceUsed = ((curTime - con.getLastTimeUsed()) / CofaxUtil.MILLISECONDS_PER_SECOND);
568             outPut += "<tr><td>" + con.getConnectionId() + "</td>\n";
569             outPut += "<td>" + con.getTimesUsed() + "</td>\n";
570             outPut += "<td>" + timeSinceCreated + "</td>\n";
571             outPut += "<td>" + timeSinceUsed + "</td><td>\n";
572             if (con.getCurrentActivity() != null)
573                 outPut += con.getCurrentActivity();
574             else
575                 outPut += "Has not been used Yet";
576             outPut += "</td></tr>\n";
577         }
578         outPut += "</table>\n";
579
580         // generate the free connection stats table
581
outPut += "<br><br><table border=1>\n";
582         outPut += "<tr><td colspan=5 bgColor=FFEEFF align=center><font size=4>Available Connections(" + fConns + ")</font></td></tr>\n";
583         outPut += "<tr><td bgColor=FFEEFF>ConnectionID</td>\n";
584         outPut += "<td bgColor=FFEEFF>Number of times used</td>\n";
585         outPut += "<td bgColor=FFEEFF>Time Since Created(Sec)</td>\n";
586         outPut += "<td bgColor=FFEEFF>Time since last used(Sec)</td>\n";
587         outPut += "<td bgColor=FFEEFF>Last Activity</td></tr>\n";
588         while (fCons.hasMoreElements()) {
589             con = (ConnectionWrapper) fCons.nextElement();
590             timeSinceCreated = ((curTime - con.getTimeCreated()) / CofaxUtil.MILLISECONDS_PER_SECOND);
591             timeSinceUsed = ((curTime - con.getLastTimeUsed()) / CofaxUtil.MILLISECONDS_PER_SECOND);
592             outPut += "<tr><td>" + con.getConnectionId() + "</td>\n";
593             outPut += "<td>" + con.getTimesUsed() + "</td>\n";
594             outPut += "<td>" + timeSinceCreated + "</td>\n";
595             outPut += "<td>" + timeSinceUsed + "</td><td>\n";
596             if (con.getCurrentActivity() != null)
597                 outPut += con.getCurrentActivity();
598             else
599                 outPut += "Has not been used Yet";
600             outPut += "</td></tr>\n";
601         }
602         outPut += "</table>\n";
603         return (outPut);
604
605     }
606
607 }
608
Popular Tags