001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.sql;
031:
032: import com.caucho.config.ConfigException;
033: import com.caucho.config.types.InitParam;
034: import com.caucho.config.types.Period;
035: import com.caucho.jca.ConnectionPool;
036: import com.caucho.jca.ResourceManagerImpl;
037: import com.caucho.loader.EnvironmentLocal;
038: import com.caucho.log.Log;
039: import com.caucho.management.j2ee.J2EEManagedObject;
040: import com.caucho.management.j2ee.JDBCDataSource;
041: import com.caucho.management.j2ee.JDBCResource;
042: import com.caucho.naming.Jndi;
043: import com.caucho.transaction.TransactionManagerImpl;
044: import com.caucho.util.L10N;
045: import com.caucho.webbeans.component.HandleAware;
046: import com.caucho.webbeans.manager.WebBeansContainer;
047:
048: import javax.annotation.PostConstruct;
049: import javax.resource.spi.ManagedConnectionFactory;
050: import javax.sql.ConnectionPoolDataSource;
051: import javax.sql.DataSource;
052: import javax.sql.XADataSource;
053: import java.io.IOException;
054: import java.io.PrintWriter;
055: import java.sql.Connection;
056: import java.sql.Driver;
057: import java.sql.SQLException;
058: import java.util.logging.Logger;
059:
060: /**
061: * Manages a pool of database connections. In addition, DBPool configures
062: * the database connection from a configuration file.
063: *
064: * <p>Like JDBC 2.0 pooling, DBPool returns a wrapped Connection.
065: * Applications can use that connection just like an unpooled connection.
066: * It is more important than ever to <code>close()</code> the connection,
067: * because the close returns the connection to the connection pool.
068: *
069: * <h4>Example using DataSource JNDI style (recommended)</h4>
070: *
071: * <pre><code>
072: * Context env = (Context) new InitialContext().lookup("java:comp/env");
073: * DataSource pool = (DataSource) env.lookup("jdbc/test");
074: * Connection conn = pool.getConnection();
075: * try {
076: * ... // normal connection stuff
077: * } finally {
078: * conn.close();
079: * }
080: * </code></pre>
081: *
082: * <h4>Configuration</h4>
083: *
084: * <pre><code>
085: * <database name='jdbc/test'>
086: * <init>
087: * <driver>postgresql.Driver</driver>
088: * <url>jdbc:postgresql://localhost/test</url>
089: * <user>ferg</user>
090: * <password>foobar</password>
091: * </init>
092: * </database>
093: * </code></pre>
094: *
095: * <h4>Pool limits and timeouts</h4>
096: *
097: * The pool will only allow getMaxConnections() connections alive at a time.
098: * If <code>getMaxConnection</code> connections are already active,
099: * <code>getPooledConnection</code> will block waiting for an available
100: * connection. The wait is timed. If connection-wait-time passes
101: * and there is still no connection, <code>getPooledConnection</code>
102: * create a new connection anyway.
103: *
104: * <p>Connections will only stay in the pool for about 5 seconds. After
105: * that they will be removed and closed. This reduces the load on the DB
106: * and also protects against the database dropping old connections.
107: */
108: public class DBPool implements DataSource, java.io.Serializable,
109: HandleAware {
110: protected static final Logger log = Logger.getLogger(DBPool.class
111: .getName());
112: private static final L10N L = new L10N(DBPool.class);
113:
114: private static int _g_id;
115:
116: private EnvironmentLocal<DBPoolImpl> _localPoolImpl;
117: private EnvironmentLocal<DataSource> _localDataSourceImpl;
118:
119: private String _var;
120: private String _name;
121: private String _jndiName;
122: private String _tmpName;
123:
124: private ResourceManagerImpl _resourceManager;
125: private ConnectionPool _connectionPool;
126:
127: private DBPoolImpl _poolImpl;
128: private DataSource _dataSource;
129: private DataSourceImpl _resinDataSource;
130:
131: // serialization handle
132: private Object _serializationHandle;
133:
134: /**
135: * Null constructor for the Driver interface; called by the JNDI
136: * configuration. Applications should not call this directly.
137: */
138: public DBPool() {
139: _poolImpl = new DBPoolImpl();
140:
141: _resourceManager = ResourceManagerImpl.createLocalManager();
142: _connectionPool = _resourceManager.createConnectionPool();
143:
144: DatabaseConfig.configDefault(this );
145: }
146:
147: /**
148: * Returns the Pool's name
149: */
150: public String getJndiName() {
151: return _jndiName;
152: }
153:
154: /**
155: * Sets the Pool's JNDI name. Also puts the pool in the classloader's
156: * list of pools.
157: */
158: public void setJndiName(String name) {
159: _jndiName = name;
160:
161: if (_var == null && _name == null)
162: getPool().setName(name);
163: }
164:
165: /**
166: * Sets the Pool's WebBeans name. Also puts the pool in the classloader's
167: * list of pools.
168: */
169: public void setName(String name) {
170: _name = name;
171:
172: getPool().setName(name);
173: }
174:
175: /**
176: * Sets the Pool's var.
177: */
178: public void setVar(String var) {
179: _var = var;
180:
181: if (_name == null)
182: getPool().setName(var);
183: }
184:
185: /**
186: * Returns the Pool's name
187: */
188: public String getName() {
189: return getPool().getName();
190: }
191:
192: /**
193: * Sets a custom driver (or data source)
194: */
195: public DriverConfig createDriver() throws ConfigException {
196: return getPool().createDriver();
197: }
198:
199: /**
200: * Sets a custom driver (or data source)
201: */
202: public DriverConfig createBackupDriver() throws ConfigException {
203: return getPool().createBackupDriver();
204: }
205:
206: /**
207: * Sets a driver parameter (compat).
208: */
209: public void setInitParam(InitParam init) {
210: getPool().setInitParam(init);
211: }
212:
213: /**
214: * Sets the jdbc-driver config.
215: */
216: public void setJDBCDriver(Driver jdbcDriver) throws SQLException {
217: getPool().setJDBCDriver(jdbcDriver);
218: }
219:
220: /**
221: * Configure the initial connection
222: */
223: public ConnectionConfig createConnection() throws ConfigException {
224: return getPool().createConnection();
225: }
226:
227: /**
228: * Sets the jdbc-driver config.
229: */
230: public void setPoolDataSource(
231: ConnectionPoolDataSource poolDataSource)
232: throws SQLException {
233: getPool().setPoolDataSource(poolDataSource);
234: }
235:
236: /**
237: * Sets the jdbc-driver config.
238: */
239: public void setXADataSource(XADataSource xaDataSource)
240: throws SQLException {
241: getPool().setXADataSource(xaDataSource);
242: }
243:
244: /**
245: * Sets the jdbc-driver URL
246: */
247: public void setURL(String url) throws ConfigException {
248: getPool().setURL(url);
249: }
250:
251: /**
252: * Return the url
253: */
254: public String getURL() {
255: return getPool().getURL();
256: }
257:
258: /**
259: * Returns the connection's user (compat).
260: */
261: public String getUser() {
262: return getPool().getUser();
263: }
264:
265: /**
266: * Sets the connection's user.
267: */
268: public void setUser(String user) {
269: getPool().setUser(user);
270: }
271:
272: /**
273: * Returns the connection's password
274: */
275: public String getPassword() {
276: return getPool().getPassword();
277: }
278:
279: /**
280: * Sets the connection's password
281: */
282: public void setPassword(String password) {
283: getPool().setPassword(password);
284: }
285:
286: /**
287: * Get the maximum number of pooled connections.
288: */
289: public int getMaxConnections() {
290: return _connectionPool.getMaxConnections();
291: }
292:
293: /**
294: * Sets the maximum number of pooled connections.
295: */
296: public void setMaxConnections(int maxConnections)
297: throws ConfigException {
298: _connectionPool.setMaxConnections(maxConnections);
299: }
300:
301: /**
302: * Get the total number of connections
303: */
304: public int getTotalConnections() {
305: return _connectionPool.getConnectionCount();
306: }
307:
308: /**
309: * Sets the time to wait for a connection when all are used.
310: */
311: public void setConnectionWaitTime(Period waitTime) {
312: _connectionPool.setConnectionWaitTime(waitTime);
313: }
314:
315: /**
316: * Gets the time to wait for a connection when all are used.
317: */
318: public long getConnectionWaitTime() {
319: return _connectionPool.getConnectionWaitTime();
320: }
321:
322: /**
323: * The number of connections to overflow if the connection pool fills
324: * and there's a timeout.
325: */
326: public void setMaxOverflowConnections(int maxOverflowConnections) {
327: _connectionPool
328: .setMaxOverflowConnections(maxOverflowConnections);
329: }
330:
331: /**
332: * The number of connections to create at any one time.
333: */
334: public void setMaxCreateConnections(int maxCreateConnections)
335: throws ConfigException {
336: _connectionPool.setMaxCreateConnections(maxCreateConnections);
337: }
338:
339: /**
340: * Set true if the stack trace should be saved on allocation.
341: */
342: public void setSaveAllocationStackTrace(boolean save) {
343: _connectionPool.setSaveAllocationStackTrace(save);
344: }
345:
346: /**
347: * Set true if dangling connections should be closed.
348: */
349: public void setCloseDanglingConnections(boolean isClose) {
350: _connectionPool.setCloseDanglingConnections(isClose);
351: }
352:
353: /**
354: * The number of connections to overflow if the connection pool fills
355: * and there's a timeout.
356: */
357: public int getMaxOverflowConnections() {
358: return _connectionPool.getMaxOverflowConnections();
359: }
360:
361: /**
362: * Get the total number of connections in use by the program.
363: */
364: public int getActiveConnections() {
365: return _connectionPool.getConnectionActiveCount();
366: }
367:
368: /**
369: * Get the maximum number of connections in the idle pool.
370: */
371: public int getMaxIdleCount() {
372: return _connectionPool.getMaxIdleCount();
373: }
374:
375: /**
376: * Set the maximum number of connections in the idle pool.
377: * being closed.
378: */
379: public void setMaxIdleCount(int count) {
380: _connectionPool.setMaxIdleCount(count);
381: }
382:
383: /**
384: * Get the time in milliseconds a connection will remain in the pool before
385: * being closed.
386: */
387: public long getMaxIdleTime() {
388: return _connectionPool.getMaxIdleTime();
389: }
390:
391: /**
392: * Set the time in milliseconds a connection will remain in the pool before
393: * being closed.
394: */
395: public void setMaxIdleTime(Period idleTime) {
396: _connectionPool.setMaxIdleTime(idleTime.getPeriod());
397: }
398:
399: /**
400: * Get the time in milliseconds a connection will remain in the pool before
401: * being closed.
402: */
403: public long getMaxPoolTime() {
404: return _connectionPool.getMaxPoolTime();
405: }
406:
407: /**
408: * Set the time in milliseconds a connection will remain in the pool before
409: * being closed.
410: */
411: public void setMaxPoolTime(Period maxPoolTime) {
412: _connectionPool.setMaxPoolTime(maxPoolTime.getPeriod());
413: }
414:
415: /**
416: * Get the time in milliseconds a connection can remain active.
417: */
418: public long getMaxActiveTime() {
419: return _connectionPool.getMaxActiveTime();
420: }
421:
422: /**
423: * Set the time in milliseconds a connection can remain active.
424: */
425: public void setMaxActiveTime(Period maxActiveTime) {
426: _connectionPool.setMaxActiveTime(maxActiveTime.getPeriod());
427: }
428:
429: /**
430: * Get the table to 'ping' to see if the connection is still live.
431: */
432: public String getPingTable() {
433: return getPool().getPingTable();
434: }
435:
436: /**
437: * Set the table to 'ping' to see if the connection is still live.
438: *
439: * @param pingTable name of the SQL table to ping.
440: */
441: public void setPingTable(String pingTable) {
442: getPool().setPingTable(pingTable);
443: }
444:
445: /**
446: * Set the query to 'ping' to see if the connection is still live.
447: *
448: * @param pingQuery SQL to use for ping.
449: */
450: public void setPingQuery(String pingQuery) {
451: getPool().setPingQuery(pingQuery);
452: }
453:
454: /**
455: * If true, the pool will ping when attempting to reuse a connection.
456: */
457: public boolean getPingOnReuse() {
458: return getPool().getPingOnReuse();
459: }
460:
461: /**
462: * Set the table to 'ping' to see if the connection is still live.
463: */
464: public void setPingOnReuse(boolean pingOnReuse) {
465: getPool().setPingOnReuse(pingOnReuse);
466: }
467:
468: /**
469: * If true, the pool will ping in the idle pool.
470: */
471: public boolean getPingOnIdle() {
472: return getPool().getPingOnIdle();
473: }
474:
475: /**
476: * Set the table to 'ping' to see if the connection is still live.
477: */
478: public void setPingOnIdle(boolean pingOnIdle) {
479: getPool().setPingOnIdle(pingOnIdle);
480: }
481:
482: /**
483: * Set the table to 'ping' to see if the connection is still live.
484: */
485: public void setPing(boolean ping) {
486: getPool().setPing(ping);
487: }
488:
489: /**
490: * Sets the time to ping for ping-on-idle
491: */
492: public void setPingInterval(Period interval) {
493: getPool().setPingInterval(interval);
494: }
495:
496: /**
497: * Gets how often the ping for ping-on-idle
498: */
499: public long getPingInterval() {
500: return getPool().getPingInterval();
501: }
502:
503: /**
504: * Returns the prepared statement cache size.
505: */
506: public int getPreparedStatementCacheSize() {
507: return getPool().getPreparedStatementCacheSize();
508: }
509:
510: /**
511: * Sets the prepared statement cache size.
512: */
513: public void setPreparedStatementCacheSize(int size) {
514: getPool().setPreparedStatementCacheSize(size);
515: }
516:
517: /**
518: * Set the transaction manager for this pool.
519: */
520: public void setTransactionManager(TransactionManagerImpl tm) {
521: getPool().setTransactionManager(tm);
522: }
523:
524: /**
525: * Set true if statement should be wrapped.
526: */
527: public void setWrapStatements(boolean isWrap) {
528: getPool().setWrapStatements(isWrap);
529: }
530:
531: /**
532: * Returns the transaction manager.
533: */
534: /*
535: public TransactionManager getTransactionManager()
536: {
537: return getPool().getTransactionManager();
538: }
539: */
540:
541: /**
542: * Sets the transaction timeout.
543: */
544: public void setTransactionTimeout(Period period) {
545: getPool().setTransactionTimeout(period);
546: }
547:
548: /**
549: * Returns true if this is transactional.
550: */
551: public boolean isXA() {
552: return getPool().isXA();
553: }
554:
555: /**
556: * Returns true if this is transactional.
557: */
558: public void setXA(boolean isTransactional) {
559: getPool().setXA(isTransactional);
560: }
561:
562: /**
563: * Returns true if this is transactional.
564: */
565: public void setXAForbidSameRM(boolean isXAForbidSameRM) {
566: getPool().setXAForbidSameRM(isXAForbidSameRM);
567: }
568:
569: /**
570: * Set the output for spying.
571: */
572: public void setSpy(boolean isSpy) throws IOException {
573: getPool().setSpy(isSpy);
574: }
575:
576: /**
577: * HandleAware callback to set the webbeans handle for serialization
578: */
579: public void setSerializationHandle(Object handle) {
580: _serializationHandle = handle;
581: }
582:
583: /**
584: * Initialize the pool.
585: */
586: @PostConstruct
587: public void init() throws Exception {
588: _localPoolImpl = new EnvironmentLocal<DBPoolImpl>(
589: "caucho.db-pool." + getName());
590: _localPoolImpl.set(_poolImpl);
591:
592: _poolImpl.init();
593:
594: _connectionPool.setName(getName());
595:
596: _connectionPool.setShareable(true);
597: _connectionPool.setXATransaction(_poolImpl.isXATransaction());
598: _connectionPool.setLocalTransaction(_poolImpl
599: .isLocalTransaction());
600:
601: ManagedConnectionFactory mcf = _poolImpl
602: .getManagedConnectionFactory();
603:
604: _dataSource = (DataSource) _connectionPool.init(mcf);
605: _connectionPool.start();
606:
607: _localDataSourceImpl = new EnvironmentLocal<DataSource>(
608: "caucho.data-source." + getName());
609: _localDataSourceImpl.set(_dataSource);
610:
611: if (_jndiName != null) {
612: String name = _jndiName;
613:
614: Jndi.bindDeepShort(name, this );
615: }
616:
617: String name = _name;
618:
619: if (name == null)
620: name = _jndiName;
621:
622: if (name == null)
623: name = _var;
624:
625: if (name != null)
626: WebBeansContainer.create().addSingleton(this , name);
627: else
628: WebBeansContainer.create().addSingleton(this );
629:
630: J2EEManagedObject.register(new JDBCResource(this ));
631: J2EEManagedObject.register(new JDBCDataSource(this ));
632: }
633:
634: /**
635: * Returns a new or pooled connection.
636: */
637: public Connection getConnection() throws SQLException {
638: return getDataSource().getConnection();
639: }
640:
641: /**
642: * Return a connection. The connection will only be pooled if
643: * user and password match the configuration. In general, applications
644: * should use the null-argument getConnection().
645: *
646: * @param user database user
647: * @param password database password
648: * @return a database connection
649: */
650: public Connection getConnection(String user, String password)
651: throws SQLException {
652: return getDataSource().getConnection(user, password);
653: }
654:
655: /**
656: * Clears the pool.
657: */
658: public void closeIdleConnections() {
659: ConnectionPool connectionPool = _connectionPool;
660:
661: if (connectionPool != null)
662: connectionPool.clear();
663: }
664:
665: /*
666: * Closes the specified connection and removes from the pool.
667: */
668: public void markForPoolRemoval(ManagedConnectionImpl mConn) {
669: _connectionPool.markForPoolRemoval(mConn);
670: }
671:
672: /**
673: * Returns the login timeout
674: */
675: public int getLoginTimeout() throws SQLException {
676: return getDataSource().getLoginTimeout();
677: }
678:
679: /**
680: * Sets the login timeout
681: */
682: public void setLoginTimeout(int timeout) throws SQLException {
683: getDataSource().setLoginTimeout(timeout);
684: }
685:
686: /**
687: * Gets the log writer
688: */
689: public PrintWriter getLogWriter() throws SQLException {
690: return getDataSource().getLogWriter();
691: }
692:
693: /**
694: * Sets the log writer
695: */
696: public void setLogWriter(PrintWriter log) throws SQLException {
697: getDataSource().setLogWriter(log);
698: }
699:
700: /**
701: * Returns the underlying pool.
702: */
703: private DBPoolImpl getPool() {
704: if (_poolImpl == null || _poolImpl.isClosed()) {
705: _poolImpl = _localPoolImpl.get();
706:
707: if (_poolImpl == null)
708: throw new IllegalStateException(L.l(
709: "DBPool `{0}' no longer exists.", getName()));
710: }
711:
712: return _poolImpl;
713: }
714:
715: /**
716: * Returns the underlying data source.
717: */
718: private DataSource getDataSource() {
719: if (_dataSource == null || _resinDataSource != null
720: && _resinDataSource.isClosed()) {
721: _dataSource = _localDataSourceImpl.get();
722:
723: if (_dataSource instanceof DataSourceImpl)
724: _resinDataSource = (DataSourceImpl) _dataSource;
725: else
726: _resinDataSource = null;
727:
728: if (_dataSource == null)
729: throw new IllegalStateException(L.l(
730: "DBPool `{0}' no longer exists.", getName()));
731: }
732:
733: return _dataSource;
734: }
735:
736: public <T> T unwrap(Class<T> iface) throws SQLException {
737: throw new UnsupportedOperationException("Not supported yet.");
738: }
739:
740: public boolean isWrapperFor(Class<?> iface) throws SQLException {
741: throw new UnsupportedOperationException("Not supported yet.");
742: }
743:
744: /**
745: * For serialization, return the handle
746: */
747: private Object writeReplace() {
748: return _serializationHandle;
749: }
750:
751: /**
752: * Returns a string description of the pool.
753: */
754: public String toString() {
755: return "DBPool[" + getName() + "]";
756: }
757: }
|