com.caucho.sql.DBPool Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.sql;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.inject.Qualifier;
import javax.resource.spi.ManagedConnectionFactory;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import com.caucho.config.ConfigException;
import com.caucho.config.Names;
import com.caucho.config.inject.BeanBuilder;
import com.caucho.config.inject.CurrentLiteral;
import com.caucho.config.inject.HandleAware;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.types.InitParam;
import com.caucho.config.types.Period;
import com.caucho.env.dbpool.ConnectionPool;
import com.caucho.jca.ra.ResourceManagerImpl;
import com.caucho.loader.EnvironmentLocal;
import com.caucho.management.server.JdbcDriverMXBean;
import com.caucho.naming.Jndi;
import com.caucho.transaction.TransactionManagerImpl;
import com.caucho.util.L10N;
/**
* Manages a pool of database connections. In addition, DBPool configures
* the database connection from a configuration file.
*
* Like JDBC 2.0 pooling, DBPool returns a wrapped Connection.
* Applications can use that connection just like an unpooled connection.
* It is more important than ever to close()
the connection,
* because the close returns the connection to the connection pool.
*
*
Example using DataSource JNDI style (recommended)
*
*
* Context env = (Context) new InitialContext().lookup("java:comp/env");
* DataSource pool = (DataSource) env.lookup("jdbc/test");
* Connection conn = pool.getConnection();
* try {
* ... // normal connection stuff
* } finally {
* conn.close();
* }
*
*
* Configuration
*
*
* <database name='jdbc/test'>
* <init>
* <driver>postgresql.Driver</driver>
* <url>jdbc:postgresql://localhost/test</url>
* <user>ferg</user>
* <password>foobar</password>
* </init>
* </database>
*
*
* Pool limits and timeouts
*
* The pool will only allow getMaxConnections() connections alive at a time.
* If getMaxConnection
connections are already active,
* getPooledConnection
will block waiting for an available
* connection. The wait is timed. If connection-wait-time passes
* and there is still no connection, getPooledConnection
* create a new connection anyway.
*
* Connections will only stay in the pool for about 5 seconds. After
* that they will be removed and closed. This reduces the load on the DB
* and also protects against the database dropping old connections.
*/
@SuppressWarnings("serial")
public class DBPool
implements DataSource, java.io.Serializable, HandleAware
{
protected static final Logger log
= Logger.getLogger(DBPool.class.getName());
private static final L10N L = new L10N(DBPool.class);
private EnvironmentLocal _localPoolImpl;
private EnvironmentLocal _localDataSourceImpl;
private String _var;
private String _name;
private String _jndiName;
private ArrayList _bindingList
= new ArrayList();
private ResourceManagerImpl _resourceManager;
private ConnectionPool _connectionPool;
private DBPoolImpl _poolImpl;
private DataSource _dataSource;
private UserDataSource _resinDataSource;
// serialization handle
private Object _serializationHandle;
private QueryAdmin _queryAdmin = new QueryAdmin(this);
private DatabaseAdmin _databaseAdmin;
/**
* Null constructor for the Driver interface; called by the JNDI
* configuration. Applications should not call this directly.
*/
public DBPool()
{
_poolImpl = new DBPoolImpl();
_resourceManager = ResourceManagerImpl.createLocalManager();
_connectionPool = _resourceManager.createConnectionPool();
_databaseAdmin = new DatabaseAdmin(this, _connectionPool);
DatabaseConfig.configDefault(this);
}
/**
* Returns the Pool's name
*/
public String getJndiName()
{
return _jndiName;
}
/**
* Sets the Pool's JNDI name. Also puts the pool in the classloader's
* list of pools.
*/
public void setJndiName(String name)
{
_jndiName = name;
if (_var == null && _name == null)
getPool().setName(name);
}
/**
* Sets the Pool's WebBeans name. Also puts the pool in the classloader's
* list of pools.
*/
public void setName(String name)
{
_name = name;
getPool().setName(name);
}
/**
* Sets the Pool's var.
*/
public void setVar(String var)
{
_var = var;
if (_name == null)
getPool().setName(var);
}
/**
* Returns the Pool's name
*/
public String getName()
{
return getPool().getName();
}
/**
* Adds an annotation
*/
public void addAnnotation(Annotation ann)
{
if (ann.annotationType().isAnnotationPresent(Qualifier.class)) {
_bindingList.add(ann);
}
else
throw new ConfigException(L.l("'{0}' is an unsupported annotation for ."));
}
/**
* Sets a custom driver (or data source)
*/
public DriverConfig createDriver()
throws ConfigException
{
return getPool().createDriver();
}
/**
* Sets a custom driver (or data source)
*/
public DriverConfig createBackupDriver()
throws ConfigException
{
return getPool().createBackupDriver();
}
/**
* Adds a preconfigured driver using Java Injection syntax.
*/
public void add(DataSource dataSource)
{
DriverConfig config = createDriver();
config.setDriverObject(dataSource);
}
/**
* Adds a preconfigured driver using Java Injection syntax.
*/
public void add(ConnectionPoolDataSource dataSource)
{
DriverConfig config = createDriver();
config.setDriverObject(dataSource);
}
/**
* Adds a preconfigured driver using Java Injection syntax.
*/
public void add(XADataSource dataSource)
{
DriverConfig config = createDriver();
config.setDriverObject(dataSource);
}
/**
* Sets a driver parameter (compat).
*/
public void setInitParam(InitParam init)
{
getPool().setInitParam(init);
}
/**
* Sets the jdbc-driver config.
*/
public void setJDBCDriver(Driver jdbcDriver)
throws SQLException
{
getPool().setJDBCDriver(jdbcDriver);
}
/**
* Returns driver admin
*/
public JdbcDriverMXBean []getDriverAdmin()
{
return getPool().getDriverAdmin();
}
/**
* Configure the initial connection
*/
public ConnectionConfig createConnection()
throws ConfigException
{
return getPool().createConnection();
}
/**
* Sets the jdbc-driver config.
*/
public void setPoolDataSource(ConnectionPoolDataSource poolDataSource)
throws SQLException
{
getPool().setPoolDataSource(poolDataSource);
}
/**
* Sets the jdbc-driver config.
*/
public void setXADataSource(XADataSource xaDataSource)
throws SQLException
{
getPool().setXADataSource(xaDataSource);
}
/**
* Sets the jdbc-driver URL
*/
public void setURL(String url)
throws ConfigException
{
getPool().setURL(url);
}
/**
* Return the url
*/
public String getURL()
{
return getPool().getURL();
}
/**
* Returns the connection's user (compat).
*/
public String getUser()
{
return getPool().getUser();
}
/**
* Sets the connection's user.
*/
public void setUser(String user)
{
getPool().setUser(user);
}
/**
* Returns the connection's password
*/
public String getPassword()
{
return getPool().getPassword();
}
/**
* Sets the connection's password
*/
public void setPassword(String password)
{
getPool().setPassword(password);
}
/**
* Get the maximum number of pooled connections.
*/
public int getMaxConnections()
{
return _connectionPool.getMaxConnections();
}
/**
* Sets the maximum number of pooled connections.
*/
public void setMaxConnections(int maxConnections)
throws ConfigException
{
_connectionPool.setMaxConnections(maxConnections);
}
/**
* Get the total number of connections
*/
public int getTotalConnections()
{
return _connectionPool.getConnectionCount();
}
/**
* Sets the time to wait for a connection when all are used.
*/
public void setConnectionWaitTime(Period waitTime)
{
_connectionPool.setConnectionWaitTime(waitTime);
}
/**
* Gets the time to wait for a connection when all are used.
*/
public long getConnectionWaitTime()
{
return _connectionPool.getConnectionWaitTime();
}
/**
* The number of connections to overflow if the connection pool fills
* and there's a timeout.
*/
public void setMaxOverflowConnections(int maxOverflowConnections)
{
_connectionPool.setMaxOverflowConnections(maxOverflowConnections);
}
/**
* The number of connections to create at any one time.
*/
public void setMaxCreateConnections(int maxCreateConnections)
throws ConfigException
{
_connectionPool.setMaxCreateConnections(maxCreateConnections);
}
/**
* Set true if the stack trace should be saved on allocation.
*/
public void setSaveAllocationStackTrace(boolean save)
{
_connectionPool.setSaveAllocationStackTrace(save);
}
/**
* Set true if dangling connections should be closed.
*/
public void setCloseDanglingConnections(boolean isClose)
{
_connectionPool.setCloseDanglingConnections(isClose);
}
/**
* The number of connections to overflow if the connection pool fills
* and there's a timeout.
*/
public int getMaxOverflowConnections()
{
return _connectionPool.getMaxOverflowConnections();
}
/**
* Get the total number of connections in use by the program.
*/
public int getActiveConnections()
{
return _connectionPool.getConnectionActiveCount();
}
/**
* Get the maximum number of connections in the idle pool.
*/
public int getMaxIdleCount()
{
return _connectionPool.getMaxIdleCount();
}
/**
* Set the maximum number of connections in the idle pool.
* being closed.
*/
public void setMaxIdleCount(int count)
{
_connectionPool.setMaxIdleCount(count);
}
/**
* Get the minimum number of connections in the idle pool.
*/
public int getMinIdleCount()
{
return _connectionPool.getMinIdleCount();
}
/**
* Set the minimum number of connections in the idle pool.
* being closed.
*/
public void setMinIdleCount(int count)
{
_connectionPool.setMinIdleCount(count);
}
/**
* Get the time in milliseconds a connection will remain in the pool before
* being closed.
*/
public long getMaxIdleTime()
{
return _connectionPool.getMaxIdleTime();
}
/**
* Set the time in milliseconds a connection will remain in the pool before
* being closed.
*/
public void setMaxIdleTime(Period idleTime)
{
_connectionPool.setMaxIdleTime(idleTime.getPeriod());
}
/**
* Get the time in milliseconds a connection will remain in the pool before
* being closed.
*/
public long getMaxPoolTime()
{
return _connectionPool.getMaxPoolTime();
}
/**
* Set the time in milliseconds a connection will remain in the pool before
* being closed.
*/
public void setMaxPoolTime(Period maxPoolTime)
{
_connectionPool.setMaxPoolTime(maxPoolTime.getPeriod());
}
/**
* Get the time in milliseconds a connection can remain active.
*/
public long getMaxActiveTime()
{
return _connectionPool.getMaxActiveTime();
}
/**
* Set the time in milliseconds a connection can remain active.
*/
public void setMaxActiveTime(Period maxActiveTime)
{
_connectionPool.setMaxActiveTime(maxActiveTime.getPeriod());
}
public void setCommitOnTimeout(boolean commitOnTimeout)
{
getPool().setCommitOnTimeout(commitOnTimeout);
}
/**
* Get the table to 'ping' to see if the connection is still live.
*/
public String getPingTable()
{
return getPool().getPingTable();
}
/**
* Set the table to 'ping' to see if the connection is still live.
*
* @param pingTable name of the SQL table to ping.
*/
public void setPingTable(String pingTable)
{
getPool().setPingTable(pingTable);
}
/**
* Set the query to 'ping' to see if the connection is still live.
*
* @param pingQuery SQL to use for ping.
*/
public void setPingQuery(String pingQuery)
{
getPool().setPingQuery(pingQuery);
}
/**
* If true, the pool will ping when attempting to reuse a connection.
*/
public boolean getPingOnReuse()
{
return getPool().getPingOnReuse();
}
/**
* Set the table to 'ping' to see if the connection is still live.
*/
public void setPingOnReuse(boolean pingOnReuse)
{
getPool().setPingOnReuse(pingOnReuse);
}
/**
* If true, the pool will ping in the idle pool.
*/
public boolean getPingOnIdle()
{
return getPool().getPingOnIdle();
}
/**
* Set the table to 'ping' to see if the connection is still live.
*/
public void setPingOnIdle(boolean pingOnIdle)
{
getPool().setPingOnIdle(pingOnIdle);
}
/**
* Set the table to 'ping' to see if the connection is still live.
*/
public void setPing(boolean ping)
{
getPool().setPing(ping);
}
/**
* Sets the time to ping for ping-on-idle
*/
public void setPingInterval(Period interval)
{
getPool().setPingInterval(interval);
}
/**
* Gets how often the ping for ping-on-idle
*/
public long getPingInterval()
{
return getPool().getPingInterval();
}
/**
* Returns the prepared statement cache size.
*/
public int getPreparedStatementCacheSize()
{
return getPool().getPreparedStatementCacheSize();
}
/**
* Sets the prepared statement cache size.
*/
public void setPreparedStatementCacheSize(int size)
{
getPool().setPreparedStatementCacheSize(size);
}
/**
* Set the transaction manager for this pool.
*/
public void setTransactionManager(TransactionManagerImpl tm)
{
getPool().setTransactionManager(tm);
}
/**
* Set true if statement should be wrapped.
*/
public void setWrapStatements(boolean isWrap)
{
getPool().setWrapStatements(isWrap);
}
/**
* Returns the transaction manager.
*/
/*
public TransactionManager getTransactionManager()
{
return getPool().getTransactionManager();
}
*/
/**
* Sets the transaction timeout.
*/
public void setTransactionTimeout(Period period)
{
getPool().setTransactionTimeout(period);
}
/**
* Returns true if this is transactional.
*/
public boolean isXA()
{
return getPool().isXA();
}
/**
* Returns true if this is transactional.
*/
public void setXA(boolean isTransactional)
{
getPool().setXA(isTransactional);
}
/**
* Returns true if this is transactional.
*/
public void setXAForbidSameRM(boolean isXAForbidSameRM)
{
getPool().setXAForbidSameRM(isXAForbidSameRM);
}
/**
* Set the output for spying.
*/
public void setSpy(boolean isSpy)
throws IOException
{
getPool().setSpy(isSpy);
}
/**
* Returns true for spy
*/
public boolean isSpy()
{
return getPool().isSpy();
}
/**
* HandleAware callback to set the webbeans handle for serialization
*/
public void setSerializationHandle(Object handle)
{
_serializationHandle = handle;
}
/**
* Initialize the pool.
*/
@PostConstruct
public void postConstruct()
throws Exception
{
init();
initNames();
}
/**
* Initialize the pool.
*/
public void init()
throws Exception
{
_localPoolImpl = new EnvironmentLocal("caucho.db-pool." + getName());
_localPoolImpl.set(_poolImpl);
_poolImpl.init();
_connectionPool.setName(getName());
_connectionPool.setShareable(true);
_connectionPool.setXATransaction(_poolImpl.isXATransaction());
_connectionPool.setLocalTransaction(_poolImpl.isLocalTransaction());
ManagedConnectionFactory mcf = _poolImpl.getManagedConnectionFactory();
_dataSource = (DataSource) _connectionPool.init(mcf);
_connectionPool.start();
_localDataSourceImpl = new EnvironmentLocal("caucho.data-source." + getName());
_localDataSourceImpl.set(_dataSource);
}
private void initNames()
throws Exception
{
if (_jndiName != null) {
String name = _jndiName;
Jndi.bindDeepShort(name, this);
}
InjectManager manager = InjectManager.create();
BeanBuilder> factory = manager.createBeanFactory(DataSource.class);
String name = _name;
if (name == null)
name = _jndiName;
if (name == null)
name = _var;
if (_bindingList.size() > 0) {
factory.binding(_bindingList);
}
else {
if (name != null)
factory.qualifier(Names.create(name));
factory.qualifier(CurrentLiteral.CURRENT);
}
if (name != null)
factory.name(name);
// factory.stereotype(CauchoDeployment.class);
manager.addBean(factory.singleton(this));
_queryAdmin.register();
_databaseAdmin.register();
}
/**
* Returns a new or pooled connection.
*/
@Override
public Connection getConnection() throws SQLException
{
return getDataSource().getConnection();
}
/**
* Return a connection. The connection will only be pooled if
* user and password match the configuration. In general, applications
* should use the null-argument getConnection().
*
* @param user database user
* @param password database password
* @return a database connection
*/
@Override
public Connection getConnection(String user, String password)
throws SQLException
{
return getDataSource().getConnection(user, password);
}
/**
* Clears the pool.
*/
public void closeIdleConnections()
{
ConnectionPool connectionPool = _connectionPool;
if (connectionPool != null)
connectionPool.clear();
}
/*
* Closes the specified connection and removes from the pool.
*/
public void markForPoolRemoval(ManagedConnectionImpl mConn)
{
_connectionPool.markForPoolRemoval(mConn);
}
/**
* Returns the login timeout
*/
public int getLoginTimeout() throws SQLException
{
return getDataSource().getLoginTimeout();
}
/**
* Sets the login timeout
*/
public void setLoginTimeout(int timeout) throws SQLException
{
getDataSource().setLoginTimeout(timeout);
}
/**
* Gets the log writer
*/
public PrintWriter getLogWriter() throws SQLException
{
return getDataSource().getLogWriter();
}
/**
* Sets the log writer
*/
public void setLogWriter(PrintWriter log) throws SQLException
{
getDataSource().setLogWriter(log);
}
/**
* Returns the underlying pool.
*/
private DBPoolImpl getPool()
{
if (_poolImpl == null || _poolImpl.isClosed()) {
_poolImpl = _localPoolImpl.get();
if (_poolImpl == null)
throw new IllegalStateException(L.l("DBPool `{0}' no longer exists.",
getName()));
}
return _poolImpl;
}
/**
* Returns the underlying data source.
*/
private DataSource getDataSource()
{
if (_dataSource == null ||
_resinDataSource != null && _resinDataSource.isClosed()) {
_dataSource = _localDataSourceImpl.get();
if (_dataSource instanceof UserDataSource)
_resinDataSource = (UserDataSource) _dataSource;
else
_resinDataSource = null;
if (_dataSource == null)
throw new IllegalStateException(L.l("DBPool `{0}' no longer exists.",
getName()));
}
return _dataSource;
}
public T unwrap(Class iface) throws SQLException
{
if (iface.isAssignableFrom(this.getClass()))
return (T) this;
else if (_dataSource == null)
throw new SQLException(L.l("`{0}' is not a wrapper for `{1}'"));
else
return _dataSource.unwrap(iface);
}
public boolean isWrapperFor(Class> iface) throws SQLException
{
if (iface.isAssignableFrom(this.getClass()))
return true;
else if (_dataSource == null)
return false;
else return _dataSource.isWrapperFor(iface);
}
public Logger getParentLogger()
{
return null;
}
/**
* For serialization, return the handle
*/
private Object writeReplace()
{
return _serializationHandle;
}
public void close()
{
_databaseAdmin.close();
_connectionPool.destroy();
}
/**
* Returns a string description of the pool.
*/
public String toString()
{
return getClass().getSimpleName() + "[" + getName() + "]";
}
}