KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > conn > JDBCConnectionPool


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.conn;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.jdo.PoolStatus;
16 import com.versant.core.metric.BaseMetric;
17 import com.versant.core.metric.Metric;
18 import com.versant.core.metric.PercentageMetric;
19 import com.versant.core.logging.LogEventStore;
20 import com.versant.core.jdbc.JdbcConfig;
21 import com.versant.core.jdbc.logging.JdbcConnectionEvent;
22 import com.versant.core.jdbc.logging.JdbcLogEvent;
23 import com.versant.core.jdbc.logging.JdbcPoolEvent;
24 import com.versant.core.jdbc.JdbcConnectionSource;
25 import com.versant.core.jdbc.sql.SqlDriver;
26 import com.versant.core.util.BeanUtils;
27
28 import java.sql.*;
29 import java.util.*;
30
31 import com.versant.core.common.BindingSupportImpl;
32 import com.versant.core.metric.HasMetrics;
33
34 /**
35  * JDBC connection pool with a PreparedStatement cache for each connection.
36  *
37  * @see PooledConnection
38  */

39 public final class JDBCConnectionPool
40         implements Runnable JavaDoc, JdbcConnectionSource, HasMetrics {
41
42     private final Driver jdbcDriver;
43     private final SqlDriver sqlDriver;
44     private final Properties props;
45     private final String JavaDoc url;
46     private final LogEventStore pes;
47     private boolean clearBatch;
48     private final boolean waitForConOnStartup;
49     private boolean jdbcDisablePsCache;
50     private int psCacheMax;
51     private int maxActive;
52     private int maxIdle;
53     private int minIdle;
54     private int reserved;
55     private final boolean testOnAlloc;
56     private final boolean testOnReturn;
57     private final boolean testOnException;
58     private boolean testWhenIdle;
59     private String JavaDoc initSQL;
60     private boolean commitAfterInitSQL;
61     private String JavaDoc validateSQL;
62     private final int retryIntervalMs;
63     private final int retryCount;
64     private boolean closed;
65     private int conTimeout;
66     private int testInterval;
67     private boolean blockWhenFull;
68     private int maxConAge;
69     private int isolationLevel;
70
71     private PooledConnection idleHead; // next con to be given out
72
private PooledConnection idleTail; // last con returned
73
private PooledConnection idleHeadAC; // next autocommit con to be given out
74
private PooledConnection idleTailAC; // last autocommit con returned
75
private int idleCount;
76
77     private PooledConnection activeHead; // most recently allocated con
78
private PooledConnection activeTail; // least recently allocated con
79
private int activeCount;
80
81     private Thread JavaDoc cleanupThread;
82     private long timeLastTest = System.currentTimeMillis();
83
84     private Properties userProps;
85
86     private BaseMetric metricActive;
87     private BaseMetric metricIdle;
88     private BaseMetric metricMaxActive;
89     private BaseMetric metricCreated;
90     private BaseMetric metricClosed;
91     private BaseMetric metricAllocated;
92     private BaseMetric metricValidated;
93     private BaseMetric metricBad;
94     private BaseMetric metricTimedOut;
95     private BaseMetric metricExpired;
96     private BaseMetric metricWait;
97     private BaseMetric metricFull;
98
99     private int createdCount;
100     private int closedCount;
101     private int allocatedCount;
102     private int validatedCount;
103     private int badCount;
104     private int timedOutCount;
105     private int waitCount;
106     private int fullCount;
107     private int expiredCount;
108
109     private static final String JavaDoc CAT_POOL = "Con Pool";
110
111     /**
112      * Create the pool. Note that changes to jdbcConfig have no effect on the
113      * pool after construction i.e. fields in jdbcConfig are copied not
114      * referenced. The sqlDriver parameter is used to customize the pool
115      * to workaround bugs and so on in the JDBC driver or database. It can
116      * be null.
117      */

118     public JDBCConnectionPool(JdbcConfig jdbcConfig, LogEventStore pes,
119             Driver jdbcDriver, SqlDriver sqlDriver) {
120         this.pes = pes;
121         this.jdbcDriver = jdbcDriver;
122         this.sqlDriver = sqlDriver;
123
124         url = jdbcConfig.url;
125         props = new Properties();
126         if (jdbcConfig.user != null) {
127             props.put("user",
128                     jdbcConfig.user);
129         }
130         if (jdbcConfig.password != null) {
131             props.put("password",
132                     jdbcConfig.password);
133         }
134         if (jdbcConfig.properties != null) {
135             BeanUtils.parseProperties(jdbcConfig.properties, props);
136         }
137
138         //create a props for user
139
userProps = new Properties();
140         if (jdbcConfig.user != null) {
141             userProps.put("user", jdbcConfig.user);
142         }
143         userProps.put("url", url);
144         userProps.put("driver", jdbcDriver.getClass().getName());
145         if (jdbcConfig.properties != null) {
146             BeanUtils.parseProperties(jdbcConfig.properties, userProps);
147         }
148
149         jdbcDisablePsCache = jdbcConfig.jdbcDisablePsCache;
150         psCacheMax = jdbcConfig.psCacheMax;
151         waitForConOnStartup = jdbcConfig.waitForConOnStartup;
152         maxActive = jdbcConfig.maxActive;
153         maxIdle = jdbcConfig.maxIdle;
154         minIdle = jdbcConfig.minIdle;
155         reserved = jdbcConfig.reserved;
156         testOnAlloc = jdbcConfig.testOnAlloc;
157         testOnReturn = jdbcConfig.testOnRelease;
158         testOnException = jdbcConfig.testOnException;
159         testWhenIdle = jdbcConfig.testWhenIdle;
160         retryIntervalMs = jdbcConfig.retryIntervalMs > 0
161                 ? jdbcConfig.retryIntervalMs
162                 : 100;
163         retryCount = jdbcConfig.retryCount;
164         conTimeout = jdbcConfig.conTimeout;
165         testInterval = jdbcConfig.testInterval;
166         blockWhenFull = jdbcConfig.blockWhenFull;
167         maxConAge = jdbcConfig.maxConAge;
168         isolationLevel = jdbcConfig.isolationLevel;
169
170         setValidateSQL(trimToNull(jdbcConfig.validateSQL));
171         setInitSQL(trimToNull(jdbcConfig.initSQL));
172
173         if (sqlDriver != null) {
174             if (psCacheMax == 0) {
175                 psCacheMax = sqlDriver.getDefaultPsCacheMax();
176             }
177             clearBatch = sqlDriver.isClearBatchRequired();
178             if (validateSQL == null) {
179                 setValidateSQL(sqlDriver.getConnectionValidateSQL());
180             }
181             if (initSQL == null) {
182                 setInitSQL(sqlDriver.getConnectionInitSQL());
183             }
184             if (!sqlDriver.isSetTransactionIsolationLevelSupported()) {
185                 isolationLevel = 0;
186             }
187         }
188
189         // sanity check some settings
190
if (maxIdle > maxActive) maxIdle = maxActive;
191         if (minIdle > maxIdle) minIdle = maxIdle;
192     }
193
194     private String JavaDoc trimToNull(String JavaDoc s) {
195         if (s == null) {
196             return null;
197         }
198         s = s.trim();
199         if (s.length() == 0) {
200             return null;
201         }
202         return s;
203     }
204
205     public void init() {
206         if (cleanupThread != null) return;
207         cleanupThread = new Thread JavaDoc(this, "VOA Pool " + url);
208         cleanupThread.setDaemon(true);
209         cleanupThread.setPriority(Thread.MIN_PRIORITY);
210         cleanupThread.start();
211     }
212
213     /**
214      * Check that we can connect to the database and that the initSQL (if any)
215      * works.
216      */

217     public void check() throws Exception JavaDoc {
218         PooledConnection con = null;
219         try {
220             if (waitForConOnStartup) {
221                 con = createPooledConnection(0, 10000);
222             } else {
223                 con = createPooledConnection(-1, 10000);
224             }
225             if (!validateConnection(con)) {
226                 throw BindingSupportImpl.getInstance().runtime("First connection failed validation SQL:\n" +
227                         validateSQL);
228             }
229         } finally {
230             if (con != null) destroy(con);
231         }
232     }
233
234     /**
235      * Create a connection. If this fails then sleep for millis and try again.
236      * If retryCount is 0 then retry is done forever, if < 0 then there are
237      * no retries.
238      */

239     private Connection createRealConWithRetry(int retryCount, int millis) {
240         for (int n = 0; ;) {
241             try {
242                 return createRealCon();
243             } catch (RuntimeException JavaDoc e) {
244                 if( BindingSupportImpl.getInstance().isOwnDatastoreException(e) )
245                 {
246                     if (retryCount < 0 || (retryCount > 0 && ++n > retryCount)) throw e;
247                     if (pes.isWarning()) {
248                         JdbcLogEvent ev = new JdbcLogEvent(0,
249                                 JdbcLogEvent.POOL_CON_FAILED,
250                                 n + " " + e.toString());
251                         ev.updateTotalMs();
252                         pes.log(ev);
253                     }
254                     try {
255                         Thread.sleep(millis);
256                     } catch (InterruptedException JavaDoc e1) {
257                         // ignore the interruption
258
}
259                 }
260                 else
261                 {
262                     throw e;
263                 }
264             }
265         }
266     }
267
268     public int getIsolationLevel() {
269         return isolationLevel;
270     }
271
272     /**
273      * If isolationLevel != 0 then the isolation level of newly created
274      * Connections is set to this.
275      *
276      * @see Connection#TRANSACTION_READ_COMMITTED etc
277      */

278     public void setIsolationLevel(int isolationLevel) {
279         this.isolationLevel = isolationLevel;
280     }
281
282     public void setClearBatch(boolean clearBatch) {
283         this.clearBatch = clearBatch;
284     }
285
286     public boolean isClearBatch() {
287         return clearBatch;
288     }
289
290     public boolean isJdbcDisablePsCache() {
291         return jdbcDisablePsCache;
292     }
293
294     public void setJdbcDisablePsCache(boolean jdbcDisablePsCache) {
295         this.jdbcDisablePsCache = jdbcDisablePsCache;
296     }
297
298     public int getPsCacheMax() {
299         return psCacheMax;
300     }
301
302     public void setPsCacheMax(int psCacheMax) {
303         this.psCacheMax = psCacheMax;
304     }
305
306     public String JavaDoc getInitSQL() {
307         return initSQL;
308     }
309
310     public void setInitSQL(String JavaDoc initSQL) {
311         String JavaDoc s = endsWithCommit(initSQL);
312         if (s != null) {
313             commitAfterInitSQL = true;
314             this.initSQL = s;
315         } else {
316             commitAfterInitSQL = false;
317             this.initSQL = initSQL;
318         }
319     }
320
321     /**
322      * If s ends with ;[{ws}]commit[;][{ws}] then return s minus this part. Otherwise
323      * return null.
324      */

325     private String JavaDoc endsWithCommit(String JavaDoc s) {
326         if (s == null) return null;
327         s = s.trim();
328         if (!s.endsWith("commit") && !s.endsWith("COMMIT")) return null;
329         for (int i = s.length() - 7; i >= 0; i--) {
330             char c = s.charAt(i);
331             if (!Character.isWhitespace(c)) {
332                 if (c == ';') {
333                     return s.substring(0, i);
334                 } else {
335                     break;
336                 }
337             }
338         }
339         return null;
340     }
341
342     public String JavaDoc getValidateSQL() {
343         return validateSQL;
344     }
345
346     public void setValidateSQL(String JavaDoc validateSQL) {
347         this.validateSQL = validateSQL;
348     }
349
350     public int getConTimeout() {
351         return conTimeout;
352     }
353
354     public void setConTimeout(int conTimeout) {
355         this.conTimeout = conTimeout;
356     }
357
358     public int getTestInterval() {
359         return testInterval;
360     }
361
362     public void setTestInterval(int testInterval) {
363         this.testInterval = testInterval;
364     }
365
366     public boolean isTestWhenIdle() {
367         return testWhenIdle;
368     }
369
370     public void setTestWhenIdle(boolean on) {
371         testWhenIdle = on;
372     }
373
374     public boolean isBlockWhenFull() {
375         return blockWhenFull;
376     }
377
378     public void setBlockWhenFull(boolean blockWhenFull) {
379         this.blockWhenFull = blockWhenFull;
380     }
381
382     public int getMaxIdle() {
383         return maxIdle;
384     }
385
386     public void setMaxIdle(int maxIdle) {
387         this.maxIdle = maxIdle;
388         if (cleanupThread != null) cleanupThread.interrupt();
389     }
390
391     public int getMinIdle() {
392         return minIdle;
393     }
394
395     public void setMinIdle(int minIdle) {
396         this.minIdle = minIdle;
397         if (cleanupThread != null) cleanupThread.interrupt();
398     }
399
400     public int getMaxActive() {
401         return maxActive;
402     }
403
404     public void setMaxActive(int maxActive) {
405         this.maxActive = maxActive;
406         if (cleanupThread != null) cleanupThread.interrupt();
407     }
408
409     public int getReserved() {
410         return reserved;
411     }
412
413     public void setReserved(int reserved) {
414         this.reserved = reserved;
415     }
416
417     public int getIdleCount() {
418         return idleCount;
419     }
420
421     public int getActiveCount() {
422         return activeCount;
423     }
424
425     public int getMaxConAge() {
426         return maxConAge;
427     }
428
429     public void setMaxConAge(int maxConAge) {
430         this.maxConAge = maxConAge;
431     }
432
433     /**
434      * Fill s with status info for this pool.
435      */

436     public void fillStatus(PoolStatus s) {
437         s.fill(maxActive, activeCount, maxIdle, idleCount);
438     }
439
440     /**
441      * Get our JDBC URL.
442      */

443     public String JavaDoc getURL() {
444         return url;
445     }
446
447     /**
448      * Return the connection prop of the pool.
449      */

450     public Properties getConnectionProperties() {
451         return userProps;
452     }
453
454     /**
455      * Get the JDBC driver instance.
456      */

457     public Driver getJdbcDriver() {
458         return jdbcDriver;
459     }
460
461     /**
462      * Get the JDBC driver class or null if not known.
463      */

464     public String JavaDoc getDriverName() {
465         return jdbcDriver == null ? "(unknown)" : jdbcDriver.getClass().getName();
466     }
467
468     /**
469      * Allocate a PooledConnection from the pool.
470      *
471      * @param highPriority If this is true then reserved high priority
472      * @param autoCommit Must the connection have autoCommit set?
473      */

474     public Connection getConnection(boolean highPriority,
475             boolean autoCommit) throws SQLException {
476         // adjust local maxActive to maintain reserved connections if needed
477
int maxActive = this.maxActive - (highPriority ? 0 : reserved);
478         PooledConnection con = null;
479         for (; ;) {
480             synchronized (this) {
481                 for (; ;) {
482                     if (closed) {
483                         throw BindingSupportImpl.getInstance().fatal(
484                                 "Connection pool has been closed");
485                     }
486                     if (activeCount >= maxActive) {
487                         if (pes.isWarning()) {
488                             JdbcPoolEvent event = new JdbcPoolEvent(0,
489                                     JdbcLogEvent.POOL_FULL, highPriority);
490                             update(event);
491                             pes.log(event);
492                             fullCount++;
493                         }
494                         if (blockWhenFull) {
495                             try {
496                                 wait();
497                             } catch (InterruptedException JavaDoc e) {
498                                 // ignore
499
}
500                         } else {
501                             throw BindingSupportImpl.getInstance().poolFull(
502                                     "JDBC connection pool is full: " + this);
503                         }
504                     } else {
505                         con = removeFromIdleHead(autoCommit);
506                         ++activeCount;
507                         break;
508                     }
509                 }
510             }
511             if (con == null) {
512                 try {
513                     waitCount++;
514                     con = createPooledConnection(retryCount, retryIntervalMs);
515                 } finally {
516                     if (con == null) {
517                         synchronized (this) {
518                             --activeCount;
519                         }
520                     }
521                 }
522                 break;
523             } else {
524                 if (testOnAlloc && !validateConnection(con)) {
525                     destroy(con);
526                     synchronized (this) {
527                         --activeCount;
528                     }
529                 } else {
530                     break;
531                 }
532             }
533         }
534         // make sure the autoCommit status on the con is correct
535
if (con.getCachedAutoCommit() != autoCommit) {
536             if (autoCommit) { // do rollback if going from false -> true
537
con.rollback();
538             }
539             con.setAutoCommit(autoCommit);
540         }
541         con.updateLastActivityTime();
542         addToActiveHead(con);
543         log(0, JdbcLogEvent.POOL_ALLOC, con, highPriority);
544         allocatedCount++;
545         return con;
546     }
547
548     /**
549      * Test 1 idle connection. If it fails, close it and repeat.
550      */

551     public void testIdleConnections() {
552         for (; ;) {
553             PooledConnection con;
554             synchronized (this) {
555                 // don't test if the pool is almost full - this will just
556
// reduce throughput by making some thread wait for a con
557
if (activeCount >= (maxActive - reserved - 1)) break;
558                 con = removeFromIdleHead(false);
559                 if (con == null) break;
560                 ++activeCount;
561             }
562             if (validateConnection(con)) {
563                 synchronized (this) {
564                     addToIdleTail(con);
565                     --activeCount;
566                 }
567                 break;
568             } else {
569                 destroy(con);
570                 synchronized (this) {
571                     --activeCount;
572                 }
573             }
574         }
575     }
576
577     /**
578      * Check the integrity of the double linked with head and tail.
579      */

580     private void checkList(PooledConnection tail, PooledConnection head,
581             int size) {
582         if (tail == null) {
583             testTrue(head == null);
584             return;
585         }
586         if (head == null) {
587             testTrue(tail == null);
588             return;
589         }
590         checkList(tail, size);
591         checkList(head, size);
592         testTrue(tail.prev == null);
593         testTrue(head.next == null);
594     }
595
596     /**
597      * Check the integrity of the double linked list containing pc.
598      */

599     private void checkList(PooledConnection pc, int size) {
600         if (pc == null) return;
601         int c = -1;
602         // check links to tail
603
for (PooledConnection i = pc; i != null; i = i.prev) {
604             if (i.prev != null) testTrue(i.prev.next == i);
605             ++c;
606         }
607         // check links to head
608
for (PooledConnection i = pc; i != null; i = i.next) {
609             if (i.next != null) testTrue(i.next.prev == i);
610             ++c;
611         }
612         if (size >= 0) {
613             testEquals(size, c);
614         }
615     }
616
617     private static void testEquals(int a, int b) {
618         if (a != b) {
619             throw BindingSupportImpl.getInstance().internal(
620                     "assertion failed: expected " + a + " got " + b);
621         }
622     }
623
624     private static void testTrue(boolean t) {
625         if (!t) {
626             throw BindingSupportImpl.getInstance().internal(
627                     "assertion failed: expected true");
628         }
629     }
630
631     public void returnConnection(Connection con) throws SQLException {
632         con.close();
633     }
634
635     /**
636      * Return a PooledConnection to the pool. This is called by
637      * PooledConnection when it is closed. This is a NOP if the connection
638      * has been destroyed.
639      *
640      * @see PooledConnection#close
641      */

642     public void returnConnection(PooledConnection con) {
643         if (con.isDestroyed()) return;
644         if ((testOnReturn || testOnException && con.isNeedsValidation())
645                 && !validateConnection(con)) {
646             removeFromActiveList(con);
647             destroy(con);
648         } else if (maxConAge > 0 && ++con.age >= maxConAge) {
649             ++expiredCount;
650             if (pes.isFine()) {
651                 JdbcLogEvent ev = new JdbcLogEvent(0,
652                         JdbcLogEvent.POOL_CON_EXPIRED,
653                         Integer.toString(con.age));
654                 ev.updateTotalMs();
655                 pes.log(ev);
656             }
657             removeFromActiveList(con);
658             destroy(con);
659         } else {
660             synchronized (this) {
661                 removeFromActiveList(con);
662                 addToIdleTail(con);
663             }
664         }
665         log(0, JdbcLogEvent.POOL_RELEASE, con, false);
666     }
667
668     /**
669      * Close all idle connections. This method closes idleCount connections.
670      * If another thread is making new connections at the same time the
671      * idle list will not be empty on return.
672      */

673     public void closeIdleConnections() {
674         for (int i = idleCount; i > 0; i--) {
675             PooledConnection con = removeFromIdleHead(false);
676             if (con == null) break;
677             destroy(con);
678         }
679         if (cleanupThread != null) cleanupThread.interrupt();
680     }
681
682     /**
683      * Destroy a connection silently ignoring SQLException's.
684      */

685     private void destroy(PooledConnection con) {
686         closedCount++;
687         con.destroy();
688     }
689
690     /**
691      * Close all connections and shutdown the pool.
692      */

693     public void destroy() {
694         closed = true;
695         if (cleanupThread != null) cleanupThread.interrupt();
696         for (; ;) {
697             PooledConnection con = removeFromIdleHead(false);
698             if (con == null) break;
699             destroy(con);
700         }
701         // this will cause any threads waiting for connections to get a
702
// closed exception
703
synchronized (this) {
704             notifyAll();
705         }
706     }
707
708     /**
709      * Close excess idle connections or create new idle connections if less
710      * than minIdle (but do not exceed maxActive in total).
711      */

712     public void checkIdleConnections() throws Exception JavaDoc {
713         // close excess idle connections
714
for (; idleCount > maxIdle;) {
715             PooledConnection con = removeFromIdleHead(false);
716             if (con == null) break;
717             destroy(con);
718         }
719         // Start creating a new connection if there is space in the pool for 2
720
// more. Only add the connection to the pool once created if there is
721
// space for 1 more.
722
for (; ;) {
723             if (!needMoreIdleConnections(1)) break;
724             PooledConnection con = createPooledConnection(retryCount,
725                     retryIntervalMs);
726             synchronized (this) {
727                 if (needMoreIdleConnections(0)) {
728                     addToIdleTail(con);
729                     continue;
730                 }
731             }
732             destroy(con);
733             break;
734         }
735     }
736
737     private synchronized boolean needMoreIdleConnections(int space) {
738         return idleCount < minIdle && idleCount + activeCount < (maxActive - space);
739     }
740
741     /**
742      * Close active connections that have been out of the pool for too long.
743      * This is a NOP if activeTimeout <= 0.
744      */

745     public void closeTimedOutConnections() {
746         if (conTimeout <= 0) return;
747         for (; ;) {
748             PooledConnection con;
749             synchronized (this) {
750                 if (activeTail == null) return;
751                 long t = activeTail.getLastActivityTime();
752                 if (t == 0) return;
753                 int s = (int)((System.currentTimeMillis() - t) / 1000);
754                 if (s < conTimeout) return;
755                 con = activeTail;
756                 removeFromActiveList(con);
757             }
758             timedOutCount++;
759             if (pes.isWarning()) {
760                 JdbcPoolEvent event = new JdbcPoolEvent(0,
761                         JdbcLogEvent.POOL_CON_TIMEOUT, false);
762                 event.setConnectionID(System.identityHashCode(con));
763                 update(event);
764                 pes.log(event);
765             }
766             destroy(con);
767         }
768     }
769
770     /**
771      * Return the con at the head of the idle list removing it from the list.
772      * If there is no idle con then null is returned. The con has its idle
773      * flag cleared.
774      *
775      * @param autoCommit An attempt is made to return a connction with
776      * autoCommit in this state. If this is not possible a connection
777      * with a different setting may be returned.
778      */

779     private synchronized PooledConnection removeFromIdleHead(boolean autoCommit) {
780         PooledConnection ans = removeFromIdleHeadImp(autoCommit);
781         return ans == null ? removeFromIdleHeadImp(!autoCommit) : ans;
782     }
783
784     private PooledConnection removeFromIdleHeadImp(boolean autoCommit) {
785         PooledConnection con;
786         if (autoCommit) {
787             con = idleHeadAC;
788             if (con == null) return null;
789             idleHeadAC = con.prev;
790             con.prev = null;
791             if (idleHeadAC == null) {
792                 idleTailAC = null;
793             } else {
794                 idleHeadAC.next = null;
795             }
796         } else {
797             con = idleHead;
798             if (con == null) return null;
799             idleHead = con.prev;
800             con.prev = null;
801             if (idleHead == null) {
802                 idleTail = null;
803             } else {
804                 idleHead.next = null;
805             }
806         }
807         --idleCount;
808         con.idle = false;
809         if (Debug.DEBUG) {
810             checkList(idleTail, idleHead, -1);
811             checkList(idleTailAC, idleHeadAC, -1);
812         }
813         return con;
814     }
815
816     /**
817      * Add con to the tail of the idle list. The con has its idle flag set.
818      * This will notify any blocked threads so they can get the newly idle
819      * connection.
820      */

821     private synchronized void addToIdleTail(PooledConnection con) {
822         if (Debug.DEBUG) {
823             if (con.prev != null || con.next != null) {
824                 throw BindingSupportImpl.getInstance().internal("con belongs to a list");
825             }
826         }
827         con.idle = true;
828         if (con.getCachedAutoCommit()) {
829             if (idleTailAC == null) {
830                 idleHeadAC = idleTailAC = con;
831             } else {
832                 con.next = idleTailAC;
833                 idleTailAC.prev = con;
834                 idleTailAC = con;
835             }
836         } else {
837             if (idleTail == null) {
838                 idleHead = idleTail = con;
839             } else {
840                 con.next = idleTail;
841                 idleTail.prev = con;
842                 idleTail = con;
843             }
844         }
845         ++idleCount;
846         if (Debug.DEBUG) {
847             checkList(idleTail, idleHead, -1);
848             checkList(idleTailAC, idleHeadAC, -1);
849         }
850         notify();
851     }
852
853     /**
854      * Add con to the head of the active list. Note that this does not
855      * bump up activeCount.
856      */

857     private synchronized void addToActiveHead(PooledConnection con) {
858         if (Debug.DEBUG) {
859             if (con.prev != null || con.next != null) {
860                 throw BindingSupportImpl.getInstance().internal("con belongs to a list");
861             }
862         }
863         if (activeHead == null) {
864             activeHead = activeTail = con;
865         } else {
866             con.prev = activeHead;
867             activeHead.next = con;
868             activeHead = con;
869         }
870         if (Debug.DEBUG) checkList(activeTail, activeHead, -1);
871     }
872
873     /**
874      * Remove con from the active list.
875      */

876     private synchronized void removeFromActiveList(PooledConnection con) {
877         if (con.prev != null) {
878             con.prev.next = con.next;
879         } else {
880             activeTail = con.next;
881         }
882         if (con.next != null) {
883             con.next.prev = con.prev;
884         } else {
885             activeHead = con.prev;
886         }
887         con.prev = con.next = null;
888         --activeCount;
889         if (Debug.DEBUG) checkList(activeTail, activeHead, -1);
890     }
891
892     private JdbcPoolEvent log(long txId, int type, PooledConnection con,
893             boolean highPriority) {
894         if (pes.isFine()) {
895             JdbcPoolEvent event = new JdbcPoolEvent(txId, type, highPriority);
896             event.setAutoCommit(con.getCachedAutoCommit());
897             event.setConnectionID(System.identityHashCode(con));
898             update(event);
899             pes.log(event);
900             return event;
901         }
902         return null;
903     }
904
905     private void update(JdbcPoolEvent ev) {
906         ev.update(maxActive, activeCount, maxIdle, idleCount);
907     }
908
909     private Connection createRealCon() {
910         JdbcConnectionEvent ev = null;
911         if (pes.isFine()) {
912             ev = new JdbcConnectionEvent(0, null, url,
913                     JdbcConnectionEvent.CON_OPEN);
914             pes.log(ev);
915         }
916         Connection realCon = null;
917         try {
918             realCon = jdbcDriver.connect(url, props);
919             if (realCon == null) {
920                 throw BindingSupportImpl.getInstance().fatalDatastore(
921                         formatConnectionErr("Unable to connect to " + url));
922             }
923         } catch (SQLException x) {
924             RuntimeException JavaDoc e = sqlDriver.mapException(x,
925                     formatConnectionErr("Unable to connect to " + url + ":\n" + x),
926                     false);
927             if (ev != null) {
928                 ev.setErrorMsg(e);
929             }
930             throw e;
931         } catch (RuntimeException JavaDoc x) {
932             if (ev != null) {
933                 ev.setErrorMsg(x);
934             }
935             throw x;
936         } finally {
937             if (ev != null) {
938                 ev.updateTotalMs();
939             }
940         }
941         createdCount++;
942         return realCon;
943     }
944
945     private String JavaDoc formatConnectionErr(String JavaDoc msg) {
946         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
947         s.append(msg);
948         s.append("\nJDBC Driver: " + jdbcDriver.getClass().getName());
949         ArrayList a = new ArrayList(props.keySet());
950         Collections.sort(a);
951         for (Iterator i = a.iterator(); i.hasNext(); ) {
952             String JavaDoc p = (String JavaDoc)i.next();
953             Object JavaDoc v = "password".equals(p) ? "(hidden)" : props.get(p);
954             s.append('\n');
955             s.append(p);
956             s.append('=');
957             s.append(v);
958         }
959         return s.toString();
960     }
961
962     /**
963      * Create an initialize a PooledConnection. This will run the initSQL
964      * and do setAutoCommit(false). It will not add it to the idle or active
965      * lists.
966      */

967     private PooledConnection createPooledConnection(int retryCount,
968             int retryIntervalMs) throws SQLException {
969         Connection realCon = createRealConWithRetry(retryCount,
970                 retryIntervalMs);
971         PooledConnection pooledCon = new PooledConnection(
972                 JDBCConnectionPool.this, realCon, pes,
973                 !jdbcDisablePsCache, psCacheMax);
974         boolean ok = false;
975         try {
976             initConnection(pooledCon);
977             pooledCon.setAutoCommit(false);
978             if (isolationLevel != 0) {
979                 pooledCon.setTransactionIsolation(isolationLevel);
980             }
981             ok = true;
982         } finally {
983             if (!ok && realCon != null) {
984                 try {
985                     pooledCon.closeRealConnection();
986                 } catch (SQLException x) {
987                     // ignore
988
}
989             }
990         }
991         return pooledCon;
992     }
993
994     /**
995      * Initialize the connection with the initSQL (if any).
996      */

997     private void initConnection(Connection con) {
998         if (initSQL == null) return;
999         Statement stat = null;
1000        try {
1001            stat = con.createStatement();
1002            stat.execute(initSQL);
1003        } catch (SQLException e) {
1004            throw sqlDriver.mapException(e,
1005                    "Error executing initSQL on new Connection: " + e +
1006                    "\n" + initSQL, true);
1007        } finally {
1008            if (stat != null) {
1009                try {
1010                    stat.close();
1011                } catch (SQLException e) {
1012                    // ignore
1013
}
1014            }
1015        }
1016        if (commitAfterInitSQL) {
1017            try {
1018                con.commit();
1019            } catch (SQLException e) {
1020                throw sqlDriver.mapException(e,
1021                        "Error doing commit after executing initSQL on new Connection: " + e + "\n" +
1022                        initSQL, true);
1023            }
1024        }
1025    }
1026
1027    /**
1028     * Check that the connection is open and if so validate the connection with
1029     * validateSQL (if any). Returns true if the connection is ok.
1030     */

1031    private boolean validateConnection(PooledConnection con) {
1032        validatedCount++;
1033        boolean ans = validateConnectionImp(con);
1034        if (!ans) badCount++;
1035        return ans;
1036    }
1037
1038    private boolean validateConnectionImp(PooledConnection con) {
1039        try {
1040            if (con.isClosed()) return false;
1041            if (validateSQL != null) {
1042                PreparedStatement ps = null;
1043                ResultSet rs = null;
1044                try {
1045                    ps = con.prepareStatement(validateSQL);
1046                    rs = ps.executeQuery();
1047                    if (!rs.next()) {
1048                        if (pes.isWarning()) {
1049                            JdbcLogEvent ev = new JdbcLogEvent(0,
1050                                    JdbcLogEvent.POOL_BAD_CON,
1051                                    "No row returned");
1052                            ev.updateTotalMs();
1053                            pes.log(ev);
1054                        }
1055                        return false;
1056                    }
1057                } finally {
1058                    if (rs != null) rs.close();
1059                    if (ps != null) ps.close();
1060                }
1061                if (!con.getCachedAutoCommit()) {
1062                    con.rollback();
1063                }
1064            }
1065            return true;
1066        } catch (SQLException e) {
1067            if (pes.isWarning()) {
1068                JdbcLogEvent ev = new JdbcLogEvent(0,
1069                        JdbcLogEvent.POOL_BAD_CON, e.toString());
1070                ev.updateTotalMs();
1071                pes.log(ev);
1072            }
1073            return false;
1074        }
1075    }
1076
1077    
1078    
1079    /**
1080     * Perform maintenance operations on the pool at periodic intervals.
1081     */

1082    public void run() {
1083        
1084        for (; ;) {
1085              
1086            try {
1087                Thread.sleep(5000);
1088            } catch (InterruptedException JavaDoc e) {
1089                // ignore
1090
}
1091             
1092            if (closed) break;
1093
1094            if (duration(timeLastTest) >= testInterval) {
1095                timeLastTest = System.currentTimeMillis();
1096                if (testWhenIdle) testIdleConnections();
1097                if (conTimeout > 0) closeTimedOutConnections();
1098            }
1099
1100            try {
1101                checkIdleConnections();
1102            } catch (Exception JavaDoc e) {
1103                // ignore
1104
}
1105        }
1106    }
1107
1108    private long duration(long start) {
1109        return (System.currentTimeMillis() - start) / 1000;
1110    }
1111
1112    /**
1113     * Return our name and status.
1114     */

1115    public String JavaDoc toString() {
1116        PoolStatus ps = new PoolStatus(url);
1117        fillStatus(ps);
1118        return ps.toString();
1119    }
1120
1121    /**
1122     * Add all BaseMetric's for this store to the List.
1123     */

1124    public void addMetrics(List list) {
1125        list.add(metricActive = new BaseMetric("JDBCPoolActive",
1126                "Pool Active", CAT_POOL, "Number of active JDBC connections in pool",
1127                0, Metric.CALC_AVERAGE));
1128        list.add(metricMaxActive = new BaseMetric("JDBCPoolMaxActive",
1129                "Pool Max Active", CAT_POOL, "Max number of JDBC connections allowed in pool",
1130                0, Metric.CALC_AVERAGE));
1131        list.add(metricIdle = new BaseMetric("JDBCPoolIdle",
1132                "Pool Idle", CAT_POOL, "Number of idle JDBC connections in pool",
1133                0, Metric.CALC_AVERAGE));
1134        list.add(metricWait = new BaseMetric("JDBCPoolWait",
1135                "Pool Wait", CAT_POOL, "Number of times that a caller had to wait for a connection",
1136                0, Metric.CALC_DELTA));
1137        list.add(metricFull = new BaseMetric("JDBCPoolFull",
1138                "Pool Full", CAT_POOL, "Number of times that the pool was full and a connection was needed",
1139                0, Metric.CALC_DELTA));
1140        list.add(metricTimedOut = new BaseMetric("JDBCConTimedOut",
1141                "Con Timed Out", CAT_POOL, "Number of active JDBC connections timed out and closed",
1142                0, Metric.CALC_DELTA));
1143        list.add(metricExpired = new BaseMetric("JDBCConExpired",
1144                "Con Expired", CAT_POOL, "Number of JDBC connections closed due to their age reaching the maximum lifespan",
1145                0, Metric.CALC_DELTA));
1146        list.add(metricBad = new BaseMetric("JDBCConBad",
1147                "Con Bad", CAT_POOL, "Number of JDBC connections that failed validation test",
1148                0, Metric.CALC_DELTA));
1149        list.add(metricCreated = new BaseMetric("JDBCConCreated",
1150                "Con Created", CAT_POOL, "Number of JDBC connections created",
1151                0, Metric.CALC_DELTA));
1152        list.add(metricClosed = new BaseMetric("JDBCConClosed",
1153                "Con Closed", CAT_POOL, "Number of JDBC connections closed",
1154                0, Metric.CALC_DELTA));
1155        list.add(metricAllocated = new BaseMetric("JDBCConAllocated",
1156                "Con Allocated", CAT_POOL, "Number of JDBC connections given out by the pool",
1157                3, Metric.CALC_DELTA_PER_SECOND));
1158        list.add(metricValidated = new BaseMetric("JDBCConValidated",
1159                "Con Validated", CAT_POOL, "Number of JDBC connections tested by the pool",
1160                0, Metric.CALC_DELTA));
1161        list.add(new PercentageMetric("JdbcPoolPercentFull",
1162                "Pool % Full ", CAT_POOL,
1163                "Active connections as a percentage of the maximum",
1164                metricActive, metricMaxActive));
1165    }
1166
1167    /**
1168     * Get values for our metrics.
1169     */

1170    public void sampleMetrics(int[][] buf, int pos) {
1171        buf[metricActive.getIndex()][pos] = activeCount;
1172        buf[metricMaxActive.getIndex()][pos] = maxActive;
1173        buf[metricIdle.getIndex()][pos] = idleCount;
1174        buf[metricWait.getIndex()][pos] = waitCount;
1175        buf[metricFull.getIndex()][pos] = fullCount;
1176        buf[metricTimedOut.getIndex()][pos] = timedOutCount;
1177        buf[metricExpired.getIndex()][pos] = expiredCount;
1178        buf[metricBad.getIndex()][pos] = badCount;
1179        buf[metricAllocated.getIndex()][pos] = allocatedCount;
1180        buf[metricValidated.getIndex()][pos] = validatedCount;
1181        buf[metricCreated.getIndex()][pos] = createdCount;
1182        buf[metricClosed.getIndex()][pos] = closedCount;
1183    }
1184
1185}
1186
Popular Tags