blob: b0bce93db956e5a54e864db4b620b9bc5a1e1a2a [file] [log] [blame]
package org.apache.commons.dbcp.cpdsadapter;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Hashtable;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.PooledConnection;
import javax.sql.ConnectionPoolDataSource;
import javax.naming.Name;
import javax.naming.Context;
import javax.naming.Referenceable;
import javax.naming.spi.ObjectFactory;
import javax.naming.Reference;
import javax.naming.RefAddr;
import javax.naming.StringRefAddr;
import javax.naming.NamingException;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
/**
* <p>
* An adapter for jdbc drivers that do not include an implementation
* of {@link javax.sql.ConnectionPoolDataSource}, but still include a
* {@link java.sql.DriverManager} implementation.
* <code>ConnectionPoolDataSource</code>s are not used within general
* applications. They are used by <code>DataSource</code> implementations
* that pool <code>Connection</code>s, such as
* {@link org.apache.commons.dbcp.jdbc2pool.Jdbc2PoolDataSource}. A J2EE
* container will normally provide some method of initializing the
* <code>ConnectionPoolDataSource</code> whose attributes are presented
* as bean getters/setters and then deploying it via JNDI. It is then
* available as a source of physical connections to the database, when
* the pooling <code>DataSource</code> needs to create a new
* physical connection.
* </p>
*
* <p>
* Although normally used within a JNDI environment, the DriverAdapterCPDS
* can be instantiated and initialized as any bean and then attached
* directly to a pooling <code>DataSource</code>.
* <code>Jdbc2PoolDataSource</code> can use the
* <code>ConnectionPoolDataSource</code> with or without the use of JNDI.
* </p>
*
* <p>
* The DriverAdapterCPDS also provides <code>PreparedStatement</code> pooling
* which is not generally available in jbdc2
* <code>ConnectionPoolDataSource</code> implementation, but is
* addressed within the jdbc3 specification. The <code>PreparedStatement</code>
* pool in DriverAdapterCPDS has been in the dbcp package for some time, but
* it has not undergone extensive testing in the configuration used here.
* It should be considered experimental and can be toggled with the
* poolPreparedStatements attribute.
* </p>
*
* <p>
* The <a href="package-summary.html">package documentation</a> contains an
* example using catalina and JNDI. The <a
* href="../jdbc2pool/package-summary.html">jdbc2pool package documentation</a>
* shows how to use <code>DriverAdapterCPDS</code> as a source for
* <code>Jdbc2PoolDataSource</code> without the use of JNDI.
* </p>
*
* @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
* @version $Id: DriverAdapterCPDS.java,v 1.2 2003/06/29 12:42:16 mpoeschl Exp $
*/
public class DriverAdapterCPDS
implements ConnectionPoolDataSource, Referenceable, Serializable,
ObjectFactory {
private static final String GET_CONNECTION_CALLED
= "A PooledConnection was already requested from this source, "
+ "further initialization is not allowed.";
/** Description */
private String description;
/** Password */
private String password;
/** Url name */
private String url;
/** User name */
private String user;
/** Driver class name */
private String driver;
/** Login TimeOut in seconds */
private int loginTimeout;
/** Log stream */
private PrintWriter logWriter = null;
// PreparedStatement pool properties
private boolean poolPreparedStatements;
private int maxActive = 10;
private int maxIdle = 10;
private int _timeBetweenEvictionRunsMillis = -1;
private int _numTestsPerEvictionRun = -1;
private int _minEvictableIdleTimeMillis = -1;
private boolean getConnectionCalled = false;
/**
* Default no-arg constructor for Serialization
*/
public DriverAdapterCPDS() {
}
/**
* Attempt to establish a database connection using the default
* user and password.
*/
public PooledConnection getPooledConnection() throws SQLException {
return getPooledConnection(getUser(), getPassword());
}
/**
* Attempt to establish a database connection.
*/
public PooledConnection getPooledConnection(String username,
String password)
throws SQLException {
getConnectionCalled = true;
/*
public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory,
int maxActive, byte whenExhaustedAction, long maxWait,
int maxIdle, boolean testOnBorrow, boolean testOnReturn,
long timeBetweenEvictionRunsMillis,
int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
boolean testWhileIdle) {
*/
KeyedObjectPool stmtPool = null;
if (isPoolPreparedStatements()) {
stmtPool = new GenericKeyedObjectPool(null,
getMaxActive(), GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 0,
getMaxIdle(), false, false, getTimeBetweenEvictionRunsMillis(),
getNumTestsPerEvictionRun(),
getMinEvictableIdleTimeMillis(), false);
}
// Workaround for buggy WebLogic 5.1 classloader - ignore the
// exception upon first invocation.
try {
return new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), username, password),
stmtPool );
}
catch (ClassCircularityError e)
{
return new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), username, password),
stmtPool );
}
}
// ----------------------------------------------------------------------
// Referenceable implementation
/**
* <CODE>Referenceable</CODE> implementation.
*/
public Reference getReference() throws NamingException {
// this class implements its own factory
String factory = getClass().getName();
Reference ref = new Reference(getClass().getName(), factory, null);
ref.add(new StringRefAddr("description", getDescription()));
ref.add(new StringRefAddr("driver", getDriver()));
ref.add(new StringRefAddr("loginTimeout",
String.valueOf(getLoginTimeout())));
ref.add(new StringRefAddr("password", getPassword()));
ref.add(new StringRefAddr("user", getUser()));
ref.add(new StringRefAddr("url", getUrl()));
ref.add(new StringRefAddr("poolPreparedStatements",
String.valueOf(isPoolPreparedStatements())));
ref.add(new StringRefAddr("maxActive",
String.valueOf(getMaxActive())));
ref.add(new StringRefAddr("maxIdle",
String.valueOf(getMaxIdle())));
ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis",
String.valueOf(getTimeBetweenEvictionRunsMillis())));
ref.add(new StringRefAddr("numTestsPerEvictionRun",
String.valueOf(getNumTestsPerEvictionRun())));
ref.add(new StringRefAddr("minEvictableIdleTimeMillis",
String.valueOf(getMinEvictableIdleTimeMillis())));
return ref;
}
// ----------------------------------------------------------------------
// ObjectFactory implementation
/**
* implements ObjectFactory to create an instance of this class
*/
public Object getObjectInstance(Object refObj, Name name,
Context context, Hashtable env)
throws Exception {
// The spec says to return null if we can't create an instance
// of the reference
DriverAdapterCPDS cpds = null;
if (refObj instanceof Reference) {
Reference ref = (Reference)refObj;
if (ref.getClassName().equals(getClass().getName())) {
RefAddr ra = ref.get("description");
if (ra != null && ra.getContent() != null) {
setDescription(ra.getContent().toString());
}
ra = ref.get("driver");
if (ra != null && ra.getContent() != null) {
setDriver(ra.getContent().toString());
}
ra = ref.get("url");
if (ra != null && ra.getContent() != null) {
setUrl(ra.getContent().toString());
}
ra = ref.get("user");
if (ra != null && ra.getContent() != null) {
setUser(ra.getContent().toString());
}
ra = ref.get("password");
if (ra != null && ra.getContent() != null) {
setPassword(ra.getContent().toString());
}
ra = ref.get("poolPreparedStatements");
if (ra != null && ra.getContent() != null) {
setPoolPreparedStatements(
Boolean.getBoolean(ra.getContent().toString()));
}
ra = ref.get("maxActive");
if (ra != null && ra.getContent() != null) {
setMaxActive(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("maxIdle");
if (ra != null && ra.getContent() != null) {
setMaxIdle(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("timeBetweenEvictionRunsMillis");
if (ra != null && ra.getContent() != null) {
setTimeBetweenEvictionRunsMillis(
Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("numTestsPerEvictionRun");
if (ra != null && ra.getContent() != null) {
setNumTestsPerEvictionRun(
Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("minEvictableIdleTimeMillis");
if (ra != null && ra.getContent() != null) {
setMinEvictableIdleTimeMillis(
Integer.parseInt(ra.getContent().toString()));
}
cpds = this;
}
}
return cpds;
}
/**
* Throws an IllegalStateException, if a PooledConnection has already
* been requested.
*/
private void assertInitializationAllowed() throws IllegalStateException {
if (getConnectionCalled) {
throw new IllegalStateException(GET_CONNECTION_CALLED);
}
}
// ----------------------------------------------------------------------
// Properties
/**
* Get the value of description. This property is here for use by
* the code which will deploy this datasource. It is not used
* internally.
*
* @return value of description.
*/
public String getDescription() {
return description;
}
/**
* Set the value of description. This property is here for use by
* the code which will deploy this datasource. It is not used
* internally.
*
* @param v Value to assign to description.
*/
public void setDescription(String v) {
this.description = v;
}
/**
* Get the value of password for the default user.
* @return value of password.
*/
public String getPassword() {
return password;
}
/**
* Set the value of password for the default user.
* @param v Value to assign to password.
*/
public void setPassword(String v) {
assertInitializationAllowed();
this.password = v;
}
/**
* Get the value of url used to locate the database for this datasource.
* @return value of url.
*/
public String getUrl() {
return url;
}
/**
* Set the value of url used to locate the database for this datasource.
* @param v Value to assign to url.
*/
public void setUrl(String v) {
assertInitializationAllowed();
this.url = v;
}
/**
* Get the value of default user (login or username).
* @return value of user.
*/
public String getUser() {
return user;
}
/**
* Set the value of default user (login or username).
* @param v Value to assign to user.
*/
public void setUser(String v) {
assertInitializationAllowed();
this.user = v;
}
/**
* Get the driver classname.
* @return value of driver.
*/
public String getDriver() {
return driver;
}
/**
* Set the driver classname. Setting the driver classname cause the
* driver to be registered with the DriverManager.
* @param v Value to assign to driver.
*/
public void setDriver(String v) throws ClassNotFoundException {
assertInitializationAllowed();
this.driver = v;
// make sure driver is registered
Class.forName(v);
}
/**
* Gets the maximum time in seconds that this data source can wait
* while attempting to connect to a database. NOT USED.
*/
public int getLoginTimeout() {
return loginTimeout;
}
/**
* Get the log writer for this data source. NOT USED.
*/
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Sets the maximum time in seconds that this data source will wait
* while attempting to connect to a database. NOT USED.
*/
public void setLoginTimeout(int seconds) {
loginTimeout = seconds;
}
/**
* Set the log writer for this data source. NOT USED.
*/
public void setLogWriter(java.io.PrintWriter out) {
logWriter = out;
}
// ------------------------------------------------------------------
// PreparedStatement pool properties
/**
* Flag to toggle the pooling of <code>PreparedStatement</code>s
* @return value of poolPreparedStatements.
*/
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
/**
* Flag to toggle the pooling of <code>PreparedStatement</code>s
* @param v true to pool statements.
*/
public void setPoolPreparedStatements(boolean v) {
assertInitializationAllowed();
this.poolPreparedStatements = v;
}
/**
* The maximum number of active statements that can be allocated from
* this pool at the same time, or zero for no limit.
*/
public int getMaxActive() {
return (this.maxActive);
}
/**
* The maximum number of active statements that can be allocated from
* this pool at the same time, or zero for no limit.
*/
public void setMaxActive(int maxActive) {
assertInitializationAllowed();
this.maxActive = maxActive;
}
/**
* The maximum number of statements that can remain idle in the
* pool, without extra ones being released, or zero for no limit.
*/
public int getMaxIdle() {
return (this.maxIdle);
}
/**
* The maximum number of statements that can remain idle in the
* pool, without extra ones being released, or zero for no limit.
*/
public void setMaxIdle(int maxIdle) {
assertInitializationAllowed();
this.maxIdle = maxIdle;
}
/**
* Returns the number of milliseconds to sleep between runs of the
* idle object evictor thread.
* When non-positive, no idle object evictor thread will be
* run.
*
* *see #setTimeBetweenEvictionRunsMillis
*/
public int getTimeBetweenEvictionRunsMillis() {
return _timeBetweenEvictionRunsMillis;
}
/**
* Sets the number of milliseconds to sleep between runs of the
* idle object evictor thread.
* When non-positive, no idle object evictor thread will be
* run.
*
* *see #getTimeBetweenEvictionRunsMillis
*/
public void setTimeBetweenEvictionRunsMillis(
int timeBetweenEvictionRunsMillis) {
assertInitializationAllowed();
_timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
/**
* Returns the number of statements to examine during each run of the
* idle object evictor thread (if any).
*
* *see #setNumTestsPerEvictionRun
* *see #setTimeBetweenEvictionRunsMillis
*/
public int getNumTestsPerEvictionRun() {
return _numTestsPerEvictionRun;
}
/**
* Sets the number of statements to examine during each run of the
* idle object evictor thread (if any).
* <p>
* When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
* tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
* idle objects will be tested per run.
*
* *see #getNumTestsPerEvictionRun
* *see #setTimeBetweenEvictionRunsMillis
*/
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
assertInitializationAllowed();
_numTestsPerEvictionRun = numTestsPerEvictionRun;
}
/**
* Returns the minimum amount of time a statement may sit idle in the pool
* before it is eligible for eviction by the idle object evictor
* (if any).
*
* *see #setMinEvictableIdleTimeMillis
* *see #setTimeBetweenEvictionRunsMillis
*/
public int getMinEvictableIdleTimeMillis() {
return _minEvictableIdleTimeMillis;
}
/**
* Sets the minimum amount of time a statement may sit idle in the pool
* before it is eligable for eviction by the idle object evictor
* (if any).
* When non-positive, no objects will be evicted from the pool
* due to idle time alone.
*
* *see #getMinEvictableIdleTimeMillis
* *see #setTimeBetweenEvictionRunsMillis
*/
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
assertInitializationAllowed();
_minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
}