KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > proxool > ProxyConnection


1 /*
2  * This software is released under a licence similar to the Apache Software Licence.
3  * See org.logicalcobwebs.proxool.package.html for details.
4  * The latest version is available at http://proxool.sourceforge.net
5  */

6 package org.logicalcobwebs.proxool;
7
8 import org.logicalcobwebs.concurrent.WriterPreferenceReadWriteLock;
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.logicalcobwebs.proxool.util.FastArrayList;
12
13 import java.sql.Connection JavaDoc;
14 import java.sql.SQLException JavaDoc;
15 import java.sql.Statement JavaDoc;
16 import java.util.Date JavaDoc;
17 import java.util.Set JavaDoc;
18 import java.util.HashSet JavaDoc;
19 import java.util.List JavaDoc;
20 import java.text.DecimalFormat JavaDoc;
21
22 /**
23  * Manages a connection. This is wrapped up inside a...
24  *
25  * @version $Revision: 1.37 $, $Date: 2006/01/18 14:40:02 $
26  * @author bill
27  * @author $Author: billhorsman $ (current maintainer)
28  * @since Proxool 0.10
29  */

30 public class ProxyConnection implements ProxyConnectionIF {
31
32     static final int STATUS_FORCE = -1;
33
34     private WriterPreferenceReadWriteLock statusReadWriteLock = new WriterPreferenceReadWriteLock();
35
36     private static final Log LOG = LogFactory.getLog(ProxyConnection.class);
37
38     private Connection JavaDoc connection;
39
40     private String JavaDoc delegateUrl;
41
42     private int mark;
43
44     private String JavaDoc reasonForMark;
45
46     private int status;
47
48     private long id;
49
50     private Date JavaDoc birthDate;
51
52     private long timeLastStartActive;
53
54     private long timeLastStopActive;
55
56     private ConnectionPool connectionPool;
57
58     private ConnectionPoolDefinitionIF definition;
59
60     private String JavaDoc requester;
61
62     private Set JavaDoc openStatements = new HashSet JavaDoc();
63
64     private DecimalFormat JavaDoc idFormat = new DecimalFormat JavaDoc("0000");
65
66     private List sqlCalls = new FastArrayList();
67
68     /**
69      * Whether we have invoked a method that requires us to reset
70      */

71     private boolean needToReset = false;
72
73     /**
74      *
75      * @param connection the real connection that is used
76      * @param id unique ID
77      * @param delegateUrl
78      * @param connectionPool the pool it is a member of
79      * @param definition the definition that was used to build it (could possibly be different from
80      * the one held in the connectionPool)
81      * @param status {@link #STATUS_ACTIVE}, {@link #STATUS_AVAILABLE}, {@link #STATUS_FORCE}, {@link #STATUS_NULL}, or {@link #STATUS_OFFLINE}
82      * @throws SQLException
83      */

84     protected ProxyConnection(Connection JavaDoc connection, long id, String JavaDoc delegateUrl, ConnectionPool connectionPool, ConnectionPoolDefinitionIF definition, int status) throws SQLException JavaDoc {
85         this.connection = connection;
86         this.delegateUrl = delegateUrl;
87         setId(id);
88         this.connectionPool = connectionPool;
89         this.definition = definition;
90         setBirthTime(System.currentTimeMillis());
91
92         this.status = status;
93         if (status == STATUS_ACTIVE) {
94             setTimeLastStartActive(System.currentTimeMillis());
95         }
96
97         // We only need to call this for the first connection we make. But it returns really
98
// quickly and we don't call it that often so we shouldn't worry.
99
connectionPool.initialiseConnectionResetter(connection);
100
101         if (connection == null) {
102             throw new SQLException JavaDoc("Unable to create new connection");
103         }
104     }
105
106     /**
107      * Whether the underlying connections are equal
108      * @param obj the object (probably another connection) that we
109      * are being compared to
110      * @return whether they are the same
111      */

112     public boolean equals(Object JavaDoc obj) {
113         if (obj != null) {
114             if (obj instanceof ProxyConnection) {
115                 return connection.hashCode() == ((ProxyConnection) obj).getConnection().hashCode();
116             } else if (obj instanceof Connection JavaDoc) {
117                 return connection.hashCode() == obj.hashCode();
118             } else {
119                 return super.equals(obj);
120             }
121         } else {
122             return false;
123         }
124     }
125
126     /**
127      * Whether this connection is available. (When you close the connection
128      * it doesn't really close, it just becomes available for others to use).
129      * @return true if the connection is not active
130      */

131     public boolean isClosed() {
132         return (getStatus() != STATUS_ACTIVE);
133     }
134
135     /**
136      * The subclass should call this to indicate that a change has been made to
137      * the connection that might mean it needs to be reset (like setting autoCommit
138      * to false or something). We don't reset unless this has been called to avoid
139      * the overhead of unnecessary resetting.
140      *
141      * @param needToReset true if the connection might need resetting.
142      */

143     protected void setNeedToReset(boolean needToReset) {
144         this.needToReset = needToReset;
145     }
146
147     /**
148      * The ConnectionPool that this connection belongs to
149      * @return connectionPool
150      */

151     protected ConnectionPool getConnectionPool() {
152         return connectionPool;
153     }
154
155     /**
156      * Get the definition that was used to create this connection
157      * @return definition
158      */

159     public ConnectionPoolDefinitionIF getDefinition() {
160         return definition;
161     }
162
163     /**
164      * By calling this we can keep track of any statements that are
165      * left open when this connection is returned to the pool.
166      * @param statement the statement that we have just opened/created.
167      * @see #registerClosedStatement
168      */

169     protected void addOpenStatement(Statement JavaDoc statement) {
170         openStatements.add(statement);
171     }
172
173     /**
174      * @see ProxyConnectionIF#registerClosedStatement
175      */

176     public void registerClosedStatement(Statement JavaDoc statement) {
177         if (openStatements.contains(statement)) {
178             openStatements.remove(statement);
179         } else {
180             connectionPool.getLog().warn(connectionPool.displayStatistics() + " - #" + getId() + " registered a statement as closed which wasn't known to be open.");
181         }
182     }
183
184     /**
185      * Close the connection for real
186      * @throws java.sql.SQLException if anything goes wrong
187      */

188     public void reallyClose() throws SQLException JavaDoc {
189         try {
190             connectionPool.registerRemovedConnection(getStatus());
191             // Clean up the actual connection
192
connection.close();
193         } catch (Throwable JavaDoc t) {
194             connectionPool.getLog().error("#" + idFormat.format(getId()) + " encountered errors during destruction: ", t);
195         }
196
197     }
198
199     /**
200      * @see ProxyConnectionIF#isReallyClosed
201      */

202     public boolean isReallyClosed() throws SQLException JavaDoc {
203         if (connection == null) {
204             return true;
205         } else {
206             return connection.isClosed();
207         }
208     }
209
210     /**
211      * @see ProxyConnectionIF#close
212      */

213     public void close() throws SQLException JavaDoc {
214         try {
215             boolean removed = false;
216             if (isMarkedForExpiry()) {
217                 if (connectionPool.getLog().isDebugEnabled()) {
218                     connectionPool.getLog().debug("Closing connection quickly (without reset) because it's marked for expiry anyway");
219                 }
220             } else {
221                 // Close any open statements, as specified in JDBC
222
Statement JavaDoc[] statements = (Statement JavaDoc[]) openStatements.toArray(new Statement JavaDoc[openStatements.size()]);
223                 for (int j = 0; j < statements.length; j++) {
224                     Statement JavaDoc statement = statements[j];
225                     statement.close();
226                     if (connectionPool.getLog().isDebugEnabled()) {
227                         connectionPool.getLog().debug("Closing statement " + Integer.toHexString(statement.hashCode()) + " automatically");
228                     }
229                 }
230                 openStatements.clear();
231
232                 if (needToReset) {
233                     // This call should be as quick as possible. Should we consider only
234
// calling it if values have changed? The trouble with that is that it
235
// means keeping track when they change and that might be even
236
// slower
237
if (!connectionPool.resetConnection(connection, "#" + getId())) {
238                         connectionPool.removeProxyConnection(this, "it couldn't be reset", true, true);
239                         removed = true;
240                     }
241                     needToReset = false;
242                 }
243             }
244             // If we removed it above then putting it back will only cause a confusing log event later when
245
// it is unable to be changed from ACTIVE to AVAILABLE.
246
if (!removed) {
247                 connectionPool.putConnection(this);
248             }
249         } catch (Throwable JavaDoc t) {
250             connectionPool.getLog().error("#" + idFormat.format(getId()) + " encountered errors during closure: ", t);
251         }
252
253     }
254
255     /**
256      * This gets called /just/ before a connection is served. You can use it to reset some of the attributes.
257      * The lifecycle is: {@link #open()} then {@link #close()}
258      */

259     protected void open() {
260         sqlCalls.clear();
261     }
262
263     public int getMark() {
264         return mark;
265     }
266
267     public int getStatus() {
268         return status;
269     }
270
271     /**
272      * @see ProxyConnectionIF#setStatus(int)
273      */

274     public boolean setStatus(int newStatus) {
275         return setStatus(STATUS_FORCE, newStatus);
276     }
277
278     /**
279      * @see ProxyConnectionIF#setStatus(int, int)
280      */

281     public boolean setStatus(int oldStatus, int newStatus) {
282         boolean success = false;
283         try {
284             statusReadWriteLock.writeLock().acquire();
285             connectionPool.acquireConnectionStatusWriteLock();
286             if (this.status == oldStatus || oldStatus == STATUS_FORCE) {
287                 connectionPool.changeStatus(this.status, newStatus);
288                 this.status = newStatus;
289                 success = true;
290
291                 if (newStatus == oldStatus) {
292                     LOG.warn("Unexpected attempt to change status from " + oldStatus + " to " + newStatus
293                             + ". Why would you want to do that?");
294                 } else if (newStatus == STATUS_ACTIVE) {
295                     setTimeLastStartActive(System.currentTimeMillis());
296                 } else if (oldStatus == STATUS_ACTIVE) {
297                     setTimeLastStopActive(System.currentTimeMillis());
298                 }
299             }
300         } catch (InterruptedException JavaDoc e) {
301             LOG.error("Unable to acquire write lock for status");
302         } finally {
303             connectionPool.releaseConnectionStatusWriteLock();
304             statusReadWriteLock.writeLock().release();
305         }
306         return success;
307     }
308
309     public long getId() {
310         return id;
311     }
312
313     public void setId(long id) {
314         this.id = id;
315     }
316
317     /**
318      * @see ConnectionInfoIF#getBirthTime
319      */

320     public long getBirthTime() {
321         return birthDate.getTime();
322     }
323
324     /**
325      * @see ConnectionInfoIF#getBirthDate
326      */

327     public Date JavaDoc getBirthDate() {
328         return birthDate;
329     }
330
331     /**
332      * @see ConnectionInfoIF#getAge
333      */

334     public long getAge() {
335         return System.currentTimeMillis() - getBirthTime();
336     }
337
338     /**
339      * @see ConnectionInfoIF#getBirthTime
340      */

341     public void setBirthTime(long birthTime) {
342         birthDate = new Date JavaDoc(birthTime);
343     }
344
345     /**
346      * @see ConnectionInfoIF#getTimeLastStartActive
347      */

348     public long getTimeLastStartActive() {
349         return timeLastStartActive;
350     }
351
352     /**
353      * @see ConnectionInfoIF#getTimeLastStartActive
354      */

355     public void setTimeLastStartActive(long timeLastStartActive) {
356         this.timeLastStartActive = timeLastStartActive;
357         setTimeLastStopActive(0);
358     }
359
360     /**
361      * @see ConnectionInfoIF#getTimeLastStopActive
362      */

363     public long getTimeLastStopActive() {
364         return timeLastStopActive;
365     }
366
367     /**
368      * @see ConnectionInfoIF#getTimeLastStopActive
369      */

370     public void setTimeLastStopActive(long timeLastStopActive) {
371         this.timeLastStopActive = timeLastStopActive;
372     }
373
374     /**
375      * @see ConnectionInfoIF#getRequester
376      */

377     public String JavaDoc getRequester() {
378         return requester;
379     }
380
381     /**
382      * @see ConnectionInfoIF#getRequester
383      */

384     public void setRequester(String JavaDoc requester) {
385         this.requester = requester;
386     }
387
388     /**
389      * @see ProxyConnectionIF#isNull
390      */

391     public boolean isNull() {
392         return getStatus() == STATUS_NULL;
393     }
394
395     /**
396      * @see ProxyConnectionIF#isAvailable
397      */

398     public boolean isAvailable() {
399         return getStatus() == STATUS_AVAILABLE;
400     }
401
402     /**
403      * @see ProxyConnectionIF#isActive
404      */

405     public boolean isActive() {
406         return getStatus() == STATUS_ACTIVE;
407     }
408
409     /**
410      * @see ProxyConnectionIF#isOffline
411      */

412     public boolean isOffline() {
413         return getStatus() == STATUS_OFFLINE;
414     }
415
416     /**
417      * @see ProxyConnectionIF#markForExpiry
418      */

419     public void markForExpiry(String JavaDoc reason) {
420         mark = MARK_FOR_EXPIRY;
421         reasonForMark = reason;
422     }
423
424     /**
425      * @see ProxyConnectionIF#isMarkedForExpiry
426      */

427     public boolean isMarkedForExpiry() {
428         return getMark() == MARK_FOR_EXPIRY;
429     }
430
431     /**
432      * @see ProxyConnectionIF#getReasonForMark
433      */

434     public String JavaDoc getReasonForMark() {
435         return reasonForMark;
436     }
437
438     /**
439      * @see ProxyConnectionIF#getConnection
440      */

441     public Connection JavaDoc getConnection() {
442         return connection;
443     }
444
445     /**
446      * @see Object#toString
447      */

448     public String JavaDoc toString() {
449         return getId() + " is " + ConnectionPool.getStatusDescription(getStatus());
450     }
451
452     /**
453      * @see ConnectionInfoIF#getDelegateUrl
454      */

455     public String JavaDoc getDelegateUrl() {
456         return delegateUrl;
457     }
458
459     /**
460      * @see ConnectionInfoIF#getProxyHashcode
461      */

462     public String JavaDoc getProxyHashcode() {
463         return Integer.toHexString(hashCode());
464     }
465
466     /**
467      * @see ConnectionInfoIF#getDelegateHashcode
468      */

469     public String JavaDoc getDelegateHashcode() {
470         if (connection != null) {
471             return Integer.toHexString(connection.hashCode());
472         } else {
473             return null;
474         }
475     }
476
477     /**
478      * Compares using {@link #getId()}
479      * @param o must be another {@link ConnectionInfoIF} implementation
480      * @return the comparison
481      * @see Comparable#compareTo(Object)
482      */

483     public int compareTo(Object JavaDoc o) {
484         return new Long JavaDoc(((ConnectionInfoIF) o).getId()).compareTo(new Long JavaDoc(getId()));
485     }
486
487     public String JavaDoc[] getSqlCalls() {
488         return (String JavaDoc[]) sqlCalls.toArray(new String JavaDoc[0]);
489     }
490
491     public String JavaDoc getLastSqlCall() {
492         if (sqlCalls != null && sqlCalls.size() > 0) {
493             return (String JavaDoc) sqlCalls.get(sqlCalls.size() - 1);
494         } else {
495             return null;
496         }
497     }
498
499     public void addSqlCall(String JavaDoc sqlCall) {
500         this.sqlCalls.add(sqlCall);
501     }
502 }
503
Popular Tags