This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.TrackedUse;
/**
* Tracks db connection usage for recovering and reporting abandoned db connections.
* <p>
* The JDBC Connection, Statement, and ResultSet classes extend this class.
* </p>
*
* @since 2.0
*/
public class AbandonedTrace implements TrackedUse {
/** A list of objects created by children of this object. */
private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>();
/** Last time this connection was used. */
private volatile long lastUsedMillis = 0;
/**
* Creates a new AbandonedTrace without config and without doing abandoned tracing.
*/
public AbandonedTrace() {
init(null);
}
/**
* Constructs a new AbandonedTrace with a parent object.
*
* @param parent
* AbandonedTrace parent object.
*/
public AbandonedTrace(final AbandonedTrace parent) {
init(parent);
}
/**
* Adds an object to the list of objects being traced.
*
* @param trace
* AbandonedTrace object to add.
*/
protected void addTrace(final AbandonedTrace trace) {
synchronized (this.traceList) {
this.traceList.add(new WeakReference<>(trace));
}
setLastUsed();
}
/**
* Clears the list of objects being traced by this object.
*/
protected void clearTrace() {
synchronized (this.traceList) {
this.traceList.clear();
}
}
/**
* Gets the last time this object was used in milliseconds.
*
* @return long time in milliseconds.
*/
@Override
public long getLastUsed() {
return lastUsedMillis;
}
/**
* Gets a list of objects being traced by this object.
*
* @return List of objects.
*/
protected List<AbandonedTrace> getTrace() {
final int size = traceList.size();
if (size == 0) {
return Collections.emptyList();
}
final ArrayList<AbandonedTrace> result = new ArrayList<>(size);
synchronized (this.traceList) {
final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
while (iter.hasNext()) {
final AbandonedTrace trace = iter.next().get();
if (trace == null) {
// Clean-up since we are here anyway
iter.remove();
} else {
result.add(trace);
}
}
}
return result;
}
/**
* Initializes abandoned tracing for this object.
*
* @param parent
* AbandonedTrace parent object.
*/
private void init(final AbandonedTrace parent) {
if (parent != null) {
parent.addTrace(this);
}
}
/**
* Removes this object the source object is tracing.
*
* @param source The object tracing
* @since 2.7.0
*/
protected void removeThisTrace(final Object source) {
if (source instanceof AbandonedTrace) {
AbandonedTrace.class.cast(source).removeTrace(this);
}
}
/**
* Removes a child object this object is tracing.
*
* @param trace
* AbandonedTrace object to remove.
*/
protected void removeTrace(final AbandonedTrace trace) {
synchronized (this.traceList) {
final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
while (iter.hasNext()) {
final AbandonedTrace traceInList = iter.next().get();
if (trace != null && trace.equals(traceInList)) {
iter.remove();
break;
} else if (traceInList == null) {
// Clean-up since we are here anyway
iter.remove();
}
}
}
}
/**
* Sets the time this object was last used to the current time in milliseconds.
*/
protected void setLastUsed() {
lastUsedMillis = System.currentTimeMillis();
}
/**
* Sets the time in milliseconds this object was last used.
*
* @param lastUsedMillis
* time in milliseconds.
*/
protected void setLastUsed(final long lastUsedMillis) {
this.lastUsedMillis = lastUsedMillis;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,602 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.impl.BaseObjectPoolConfig;
import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPoolConfig;
/**
* <p>
* JNDI object factory that creates an instance of <code>BasicDataSource</code> that has been configured based on the
* <code>RefAddr</code> values of the specified <code>Reference</code>, which must match the names and data types of the
* <code>BasicDataSource</code> bean properties with the following exceptions:
* </p>
* <ul>
* <li><code>connectionInitSqls</code> must be passed to this factory as a single String using semi-colon to delimit the
* statements whereas <code>BasicDataSource</code> requires a collection of Strings.</li>
* </ul>
*
* @since 2.0
*/
public class BasicDataSourceFactory implements ObjectFactory {
private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
private static final String PROP_DEFAULT_AUTO_COMMIT = "defaultAutoCommit";
private static final String PROP_DEFAULT_READ_ONLY = "defaultReadOnly";
private static final String PROP_DEFAULT_TRANSACTION_ISOLATION = "defaultTransactionIsolation";
private static final String PROP_DEFAULT_CATALOG = "defaultCatalog";
private static final String PROP_DEFAULT_SCHEMA = "defaultSchema";
private static final String PROP_CACHE_STATE = "cacheState";
private static final String PROP_DRIVER_CLASS_NAME = "driverClassName";
private static final String PROP_LIFO = "lifo";
private static final String PROP_MAX_TOTAL = "maxTotal";
private static final String PROP_MAX_IDLE = "maxIdle";
private static final String PROP_MIN_IDLE = "minIdle";
private static final String PROP_INITIAL_SIZE = "initialSize";
private static final String PROP_MAX_WAIT_MILLIS = "maxWaitMillis";
private static final String PROP_TEST_ON_CREATE = "testOnCreate";
private static final String PROP_TEST_ON_BORROW = "testOnBorrow";
private static final String PROP_TEST_ON_RETURN = "testOnReturn";
private static final String PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis";
private static final String PROP_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
private static final String PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis";
private static final String PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "softMinEvictableIdleTimeMillis";
private static final String PROP_EVICTION_POLICY_CLASS_NAME = "evictionPolicyClassName";
private static final String PROP_TEST_WHILE_IDLE = "testWhileIdle";
private static final String PROP_PASSWORD = "password";
private static final String PROP_URL = "url";
private static final String PROP_USER_NAME = "username";
private static final String PROP_VALIDATION_QUERY = "validationQuery";
private static final String PROP_VALIDATION_QUERY_TIMEOUT = "validationQueryTimeout";
private static final String PROP_JMX_NAME = "jmxName";
private static final String PROP_CONNECTION_FACTORY_CLASS_NAME = "connectionFactoryClassName";
/**
* The property name for connectionInitSqls. The associated value String must be of the form [query;]*
*/
private static final String PROP_CONNECTION_INIT_SQLS = "connectionInitSqls";
private static final String PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED = "accessToUnderlyingConnectionAllowed";
private static final String PROP_REMOVE_ABANDONED_ON_BORROW = "removeAbandonedOnBorrow";
private static final String PROP_REMOVE_ABANDONED_ON_MAINTENANCE = "removeAbandonedOnMaintenance";
private static final String PROP_REMOVE_ABANDONED_TIMEOUT = "removeAbandonedTimeout";
private static final String PROP_LOG_ABANDONED = "logAbandoned";
private static final String PROP_ABANDONED_USAGE_TRACKING = "abandonedUsageTracking";
private static final String PROP_POOL_PREPARED_STATEMENTS = "poolPreparedStatements";
private static final String PROP_MAX_OPEN_PREPARED_STATEMENTS = "maxOpenPreparedStatements";
private static final String PROP_CONNECTION_PROPERTIES = "connectionProperties";
private static final String PROP_MAX_CONN_LIFETIME_MILLIS = "maxConnLifetimeMillis";
private static final String PROP_LOG_EXPIRED_CONNECTIONS = "logExpiredConnections";
private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
private static final String PROP_ENABLE_AUTO_COMMIT_ON_RETURN = "enableAutoCommitOnReturn";
private static final String PROP_DEFAULT_QUERY_TIMEOUT = "defaultQueryTimeout";
private static final String PROP_FAST_FAIL_VALIDATION = "fastFailValidation";
/**
* Value string must be of the form [STATE_CODE,]*
*/
private static final String PROP_DISCONNECTION_SQL_CODES = "disconnectionSqlCodes";
/*
* Block with obsolete properties from DBCP 1.x. Warn users that these are ignored and they should use the 2.x
* properties.
*/
private static final String NUPROP_MAX_ACTIVE = "maxActive";
private static final String NUPROP_REMOVE_ABANDONED = "removeAbandoned";
private static final String NUPROP_MAXWAIT = "maxWait";
/*
* Block with properties expected in a DataSource This props will not be listed as ignored - we know that they may
* appear in Resource, and not listing them as ignored.
*/
private static final String SILENT_PROP_FACTORY = "factory";
private static final String SILENT_PROP_SCOPE = "scope";
private static final String SILENT_PROP_SINGLETON = "singleton";
private static final String SILENT_PROP_AUTH = "auth";
private static final String[] ALL_PROPERTIES = {PROP_DEFAULT_AUTO_COMMIT, PROP_DEFAULT_READ_ONLY,
PROP_DEFAULT_TRANSACTION_ISOLATION, PROP_DEFAULT_CATALOG, PROP_DEFAULT_SCHEMA, PROP_CACHE_STATE,
PROP_DRIVER_CLASS_NAME, PROP_LIFO, PROP_MAX_TOTAL, PROP_MAX_IDLE, PROP_MIN_IDLE, PROP_INITIAL_SIZE,
PROP_MAX_WAIT_MILLIS, PROP_TEST_ON_CREATE, PROP_TEST_ON_BORROW, PROP_TEST_ON_RETURN,
PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS, PROP_NUM_TESTS_PER_EVICTION_RUN, PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS,
PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, PROP_EVICTION_POLICY_CLASS_NAME, PROP_TEST_WHILE_IDLE, PROP_PASSWORD,
PROP_URL, PROP_USER_NAME, PROP_VALIDATION_QUERY, PROP_VALIDATION_QUERY_TIMEOUT, PROP_CONNECTION_INIT_SQLS,
PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED, PROP_REMOVE_ABANDONED_ON_BORROW, PROP_REMOVE_ABANDONED_ON_MAINTENANCE,
PROP_REMOVE_ABANDONED_TIMEOUT, PROP_LOG_ABANDONED, PROP_ABANDONED_USAGE_TRACKING, PROP_POOL_PREPARED_STATEMENTS,
PROP_MAX_OPEN_PREPARED_STATEMENTS, PROP_CONNECTION_PROPERTIES, PROP_MAX_CONN_LIFETIME_MILLIS,
PROP_LOG_EXPIRED_CONNECTIONS, PROP_ROLLBACK_ON_RETURN, PROP_ENABLE_AUTO_COMMIT_ON_RETURN,
PROP_DEFAULT_QUERY_TIMEOUT, PROP_FAST_FAIL_VALIDATION, PROP_DISCONNECTION_SQL_CODES, PROP_JMX_NAME,
PROP_CONNECTION_FACTORY_CLASS_NAME };
/**
* Obsolete properties from DBCP 1.x. with warning strings suggesting new properties. LinkedHashMap will guarantee
* that properties will be listed to output in order of insertion into map.
*/
private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
static {
NUPROP_WARNTEXT.put(NUPROP_MAX_ACTIVE,
"Property " + NUPROP_MAX_ACTIVE + " is not used in DBCP2, use " + PROP_MAX_TOTAL + " instead. "
+ PROP_MAX_TOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + ".");
NUPROP_WARNTEXT.put(NUPROP_REMOVE_ABANDONED,
"Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2," + " use one or both of "
+ PROP_REMOVE_ABANDONED_ON_BORROW + " or " + PROP_REMOVE_ABANDONED_ON_MAINTENANCE + " instead. "
+ "Both have default value set to false.");
NUPROP_WARNTEXT.put(NUPROP_MAXWAIT,
"Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAX_WAIT_MILLIS + " instead. "
+ PROP_MAX_WAIT_MILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS
+ ".");
}
/**
* Silent Properties. These properties will not be listed as ignored - we know that they may appear in JDBC Resource
* references, and we will not list them as ignored.
*/
private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
static {
SILENT_PROPERTIES.add(SILENT_PROP_FACTORY);
SILENT_PROPERTIES.add(SILENT_PROP_SCOPE);
SILENT_PROPERTIES.add(SILENT_PROP_SINGLETON);
SILENT_PROPERTIES.add(SILENT_PROP_AUTH);
}
// -------------------------------------------------- ObjectFactory Methods
/**
* <p>
* Create and return a new <code>BasicDataSource</code> instance. If no instance can be created, return
* <code>null</code> instead.
* </p>
*
* @param obj
* The possibly null object containing location or reference information that can be used in creating an
* object
* @param name
* The name of this object relative to <code>nameCtx</code>
* @param nameCtx
* The context relative to which the <code>name</code> parameter is specified, or <code>null</code> if
* <code>name</code> is relative to the default initial context
* @param environment
* The possibly null environment that is used in creating this object
*
* @throws Exception
* if an exception occurs creating the instance
*/
@Override
public Object getObjectInstance(final Object obj, final Name name, final Context nameCtx,
final Hashtable<?, ?> environment) throws Exception {
// We only know how to deal with <code>javax.naming.Reference</code>s
// that specify a class name of "javax.sql.DataSource"
if (obj == null || !(obj instanceof Reference)) {
return null;
}
final Reference ref = (Reference) obj;
if (!"javax.sql.DataSource".equals(ref.getClassName())) {
return null;
}
// Check property names and log warnings about obsolete and / or unknown properties
final List<String> warnings = new ArrayList<>();
final List<String> infoMessages = new ArrayList<>();
validatePropertyNames(ref, name, warnings, infoMessages);
for (final String warning : warnings) {
log.warn(warning);
}
for (final String infoMessage : infoMessages) {
log.info(infoMessage);
}
final Properties properties = new Properties();
for (final String propertyName : ALL_PROPERTIES) {
final RefAddr ra = ref.get(propertyName);
if (ra != null) {
final String propertyValue = ra.getContent().toString();
properties.setProperty(propertyName, propertyValue);
}
}
return createDataSource(properties);
}
/**
* Collects warnings and info messages. Warnings are generated when an obsolete property is set. Unknown properties
* generate info messages.
*
* @param ref
* Reference to check properties of
* @param name
* Name provided to getObject
* @param warnings
* container for warning messages
* @param infoMessages
* container for info messages
*/
private void validatePropertyNames(final Reference ref, final Name name, final List<String> warnings,
final List<String> infoMessages) {
final List<String> allPropsAsList = Arrays.asList(ALL_PROPERTIES);
final String nameString = name != null ? "Name = " + name.toString() + " " : "";
if (NUPROP_WARNTEXT != null && !NUPROP_WARNTEXT.keySet().isEmpty()) {
for (final String propertyName : NUPROP_WARNTEXT.keySet()) {
final RefAddr ra = ref.get(propertyName);
if (ra != null && !allPropsAsList.contains(ra.getType())) {
final StringBuilder stringBuilder = new StringBuilder(nameString);
final String propertyValue = ra.getContent().toString();
stringBuilder.append(NUPROP_WARNTEXT.get(propertyName)).append(" You have set value of \"")
.append(propertyValue).append("\" for \"").append(propertyName)
.append("\" property, which is being ignored.");
warnings.add(stringBuilder.toString());
}
}
}
final Enumeration<RefAddr> allRefAddrs = ref.getAll();
while (allRefAddrs.hasMoreElements()) {
final RefAddr ra = allRefAddrs.nextElement();
final String propertyName = ra.getType();
// If property name is not in the properties list, we haven't warned on it
// and it is not in the "silent" list, tell user we are ignoring it.
if (!(allPropsAsList.contains(propertyName) || NUPROP_WARNTEXT.keySet().contains(propertyName)
|| SILENT_PROPERTIES.contains(propertyName))) {
final String propertyValue = ra.getContent().toString();
final StringBuilder stringBuilder = new StringBuilder(nameString);
stringBuilder.append("Ignoring unknown property: ").append("value of \"").append(propertyValue)
.append("\" for \"").append(propertyName).append("\" property");
infoMessages.add(stringBuilder.toString());
}
}
}
/**
* Creates and configures a {@link BasicDataSource} instance based on the given properties.
*
* @param properties
* The data source configuration properties.
* @return A new a {@link BasicDataSource} instance based on the given properties.
* @throws Exception
* Thrown when an error occurs creating the data source.
*/
public static BasicDataSource createDataSource(final Properties properties) throws Exception {
final BasicDataSource dataSource = new BasicDataSource();
String value = null;
value = properties.getProperty(PROP_DEFAULT_AUTO_COMMIT);
if (value != null) {
dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULT_READ_ONLY);
if (value != null) {
dataSource.setDefaultReadOnly(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULT_TRANSACTION_ISOLATION);
if (value != null) {
int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
if ("NONE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_NONE;
} else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_COMMITTED;
} else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_UNCOMMITTED;
} else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_REPEATABLE_READ;
} else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_SERIALIZABLE;
} else {
try {
level = Integer.parseInt(value);
} catch (final NumberFormatException e) {
System.err.println("Could not parse defaultTransactionIsolation: " + value);
System.err.println("WARNING: defaultTransactionIsolation not set");
System.err.println("using default value of database driver");
level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
}
}
dataSource.setDefaultTransactionIsolation(level);
}
value = properties.getProperty(PROP_DEFAULT_CATALOG);
if (value != null) {
dataSource.setDefaultCatalog(value);
}
value = properties.getProperty(PROP_DEFAULT_SCHEMA);
if (value != null) {
dataSource.setDefaultSchema(value);
}
value = properties.getProperty(PROP_CACHE_STATE);
if (value != null) {
dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DRIVER_CLASS_NAME);
if (value != null) {
dataSource.setDriverClassName(value);
}
value = properties.getProperty(PROP_LIFO);
if (value != null) {
dataSource.setLifo(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_MAX_TOTAL);
if (value != null) {
dataSource.setMaxTotal(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAX_IDLE);
if (value != null) {
dataSource.setMaxIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MIN_IDLE);
if (value != null) {
dataSource.setMinIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_INITIAL_SIZE);
if (value != null) {
dataSource.setInitialSize(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAX_WAIT_MILLIS);
if (value != null) {
dataSource.setMaxWaitMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_TEST_ON_CREATE);
if (value != null) {
dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TEST_ON_BORROW);
if (value != null) {
dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TEST_ON_RETURN);
if (value != null) {
dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
if (value != null) {
dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_NUM_TESTS_PER_EVICTION_RUN);
if (value != null) {
dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS);
if (value != null) {
dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
if (value != null) {
dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_EVICTION_POLICY_CLASS_NAME);
if (value != null) {
dataSource.setEvictionPolicyClassName(value);
}
value = properties.getProperty(PROP_TEST_WHILE_IDLE);
if (value != null) {
dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_PASSWORD);
if (value != null) {
dataSource.setPassword(value);
}
value = properties.getProperty(PROP_URL);
if (value != null) {
dataSource.setUrl(value);
}
value = properties.getProperty(PROP_USER_NAME);
if (value != null) {
dataSource.setUsername(value);
}
value = properties.getProperty(PROP_VALIDATION_QUERY);
if (value != null) {
dataSource.setValidationQuery(value);
}
value = properties.getProperty(PROP_VALIDATION_QUERY_TIMEOUT);
if (value != null) {
dataSource.setValidationQueryTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
if (value != null) {
dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_BORROW);
if (value != null) {
dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_MAINTENANCE);
if (value != null) {
dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_TIMEOUT);
if (value != null) {
dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_LOG_ABANDONED);
if (value != null) {
dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_ABANDONED_USAGE_TRACKING);
if (value != null) {
dataSource.setAbandonedUsageTracking(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_POOL_PREPARED_STATEMENTS);
if (value != null) {
dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_MAX_OPEN_PREPARED_STATEMENTS);
if (value != null) {
dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
}
value = properties.getProperty(PROP_CONNECTION_INIT_SQLS);
if (value != null) {
dataSource.setConnectionInitSqls(parseList(value, ';'));
}
value = properties.getProperty(PROP_CONNECTION_PROPERTIES);
if (value != null) {
final Properties p = getProperties(value);
final Enumeration<?> e = p.propertyNames();
while (e.hasMoreElements()) {
final String propertyName = (String) e.nextElement();
dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
}
}
value = properties.getProperty(PROP_MAX_CONN_LIFETIME_MILLIS);
if (value != null) {
dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_LOG_EXPIRED_CONNECTIONS);
if (value != null) {
dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_JMX_NAME);
if (value != null) {
dataSource.setJmxName(value);
}
value = properties.getProperty(PROP_ENABLE_AUTO_COMMIT_ON_RETURN);
if (value != null) {
dataSource.setAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
if (value != null) {
dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DEFAULT_QUERY_TIMEOUT);
if (value != null) {
dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
}
value = properties.getProperty(PROP_FAST_FAIL_VALIDATION);
if (value != null) {
dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
if (value != null) {
dataSource.setDisconnectionSqlCodes(parseList(value, ','));
}
value = properties.getProperty(PROP_CONNECTION_FACTORY_CLASS_NAME);
if (value != null) {
dataSource.setConnectionFactoryClassName(value);
}
// DBCP-215
// Trick to make sure that initialSize connections are created
if (dataSource.getInitialSize() > 0) {
dataSource.getLogWriter();
}
// Return the configured DataSource instance
return dataSource;
}
/**
* <p>
* Parse properties from the string. Format of the string must be [propertyName=property;]*
* <p>
*
* @param propText
* @return Properties
* @throws Exception
*/
private static Properties getProperties(final String propText) throws Exception {
final Properties p = new Properties();
if (propText != null) {
p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes(StandardCharsets.ISO_8859_1)));
}
return p;
}
/**
* Parse list of property values from a delimited string
*
* @param value
* delimited list of values
* @param delimiter
* character used to separate values in the list
* @return String Collection of values
*/
private static Collection<String> parseList(final String value, final char delimiter) {
final StringTokenizer tokenizer = new StringTokenizer(value, Character.toString(delimiter));
final Collection<String> tokens = new ArrayList<>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
tokens.add(tokenizer.nextToken());
}
return tokens;
}
}

View File

@@ -0,0 +1,316 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
/**
* Defines the methods that will be made available via JMX.
*
* @since 2.0
*/
public interface BasicDataSourceMXBean {
/**
* See {@link BasicDataSource#getAbandonedUsageTracking()}
*
* @return {@link BasicDataSource#getAbandonedUsageTracking()}
*/
boolean getAbandonedUsageTracking();
/**
* See {@link BasicDataSource#getDefaultAutoCommit()}
*
* @return {@link BasicDataSource#getDefaultAutoCommit()}
*/
Boolean getDefaultAutoCommit();
/**
* See {@link BasicDataSource#getDefaultReadOnly()}
*
* @return {@link BasicDataSource#getDefaultReadOnly()}
*/
Boolean getDefaultReadOnly();
/**
* See {@link BasicDataSource#getDefaultTransactionIsolation()}
*
* @return {@link BasicDataSource#getDefaultTransactionIsolation()}
*/
int getDefaultTransactionIsolation();
/**
* See {@link BasicDataSource#getDefaultCatalog()}
*
* @return {@link BasicDataSource#getDefaultCatalog()}
*/
String getDefaultCatalog();
/**
* See {@link BasicDataSource#getDefaultSchema()}
*
* @return {@link BasicDataSource#getDefaultSchema()}
* @since 2.5.0
*/
String getDefaultSchema();
/**
* See {@link BasicDataSource#getCacheState()}
*
* @return {@link BasicDataSource#getCacheState()}
*/
boolean getCacheState();
/**
* See {@link BasicDataSource#getDriverClassName()}
*
* @return {@link BasicDataSource#getDriverClassName()}
*/
String getDriverClassName();
/**
* See {@link BasicDataSource#getLifo()}
*
* @return {@link BasicDataSource#getLifo()}
*/
boolean getLifo();
/**
* See {@link BasicDataSource#getMaxTotal()}
*
* @return {@link BasicDataSource#getMaxTotal()}
*/
int getMaxTotal();
/**
* See {@link BasicDataSource#getMaxIdle()}
*
* @return {@link BasicDataSource#getMaxIdle()}
*/
int getMaxIdle();
/**
* See {@link BasicDataSource#getMinIdle()}
*
* @return {@link BasicDataSource#getMinIdle()}
*/
int getMinIdle();
/**
* See {@link BasicDataSource#getInitialSize()}
*
* @return {@link BasicDataSource#getInitialSize()}
*/
int getInitialSize();
/**
* See {@link BasicDataSource#getMaxWaitMillis()}
*
* @return {@link BasicDataSource#getMaxWaitMillis()}
*/
long getMaxWaitMillis();
/**
* See {@link BasicDataSource#isPoolPreparedStatements()}
*
* @return {@link BasicDataSource#isPoolPreparedStatements()}
*/
boolean isPoolPreparedStatements();
/**
* See {@link BasicDataSource#getMaxOpenPreparedStatements()}
*
* @return {@link BasicDataSource#getMaxOpenPreparedStatements()}
*/
int getMaxOpenPreparedStatements();
/**
* See {@link BasicDataSource#getTestOnCreate()}
*
* @return {@link BasicDataSource#getTestOnCreate()}
*/
boolean getTestOnCreate();
/**
* See {@link BasicDataSource#getTestOnBorrow()}
*
* @return {@link BasicDataSource#getTestOnBorrow()}
*/
boolean getTestOnBorrow();
/**
* See {@link BasicDataSource#getTimeBetweenEvictionRunsMillis()}
*
* @return {@link BasicDataSource#getTimeBetweenEvictionRunsMillis()}
*/
long getTimeBetweenEvictionRunsMillis();
/**
* See {@link BasicDataSource#getNumTestsPerEvictionRun()}
*
* @return {@link BasicDataSource#getNumTestsPerEvictionRun()}
*/
int getNumTestsPerEvictionRun();
/**
* See {@link BasicDataSource#getMinEvictableIdleTimeMillis()}
*
* @return {@link BasicDataSource#getMinEvictableIdleTimeMillis()}
*/
long getMinEvictableIdleTimeMillis();
/**
* See {@link BasicDataSource#getSoftMinEvictableIdleTimeMillis()}
*
* @return {@link BasicDataSource#getSoftMinEvictableIdleTimeMillis()}
*/
long getSoftMinEvictableIdleTimeMillis();
/**
* See {@link BasicDataSource#getTestWhileIdle()}
*
* @return {@link BasicDataSource#getTestWhileIdle()}
*/
boolean getTestWhileIdle();
/**
* See {@link BasicDataSource#getNumActive()}
*
* @return {@link BasicDataSource#getNumActive()}
*/
int getNumActive();
/**
* See {@link BasicDataSource#getNumIdle()}
*
* @return {@link BasicDataSource#getNumIdle()}
*/
int getNumIdle();
/**
* See {@link BasicDataSource#getPassword()}
*
* @return {@link BasicDataSource#getPassword()}
*/
String getPassword();
/**
* See {@link BasicDataSource#getUrl()}
*
* @return {@link BasicDataSource#getUrl()}
*/
String getUrl();
/**
* See {@link BasicDataSource#getUsername()}
*
* @return {@link BasicDataSource#getUsername()}
*/
String getUsername();
/**
* See {@link BasicDataSource#getValidationQuery()}
*
* @return {@link BasicDataSource#getValidationQuery()}
*/
String getValidationQuery();
/**
* See {@link BasicDataSource#getValidationQueryTimeout()}
*
* @return {@link BasicDataSource#getValidationQueryTimeout()}
*/
int getValidationQueryTimeout();
/**
* See {@link BasicDataSource#getConnectionInitSqlsAsArray()}
*
* @return {@link BasicDataSource#getConnectionInitSqlsAsArray()}
*/
String[] getConnectionInitSqlsAsArray();
/**
* See {@link BasicDataSource#isAccessToUnderlyingConnectionAllowed()}
*
* @return {@link BasicDataSource#isAccessToUnderlyingConnectionAllowed()}
*/
boolean isAccessToUnderlyingConnectionAllowed();
/**
* See {@link BasicDataSource#getMaxConnLifetimeMillis()}
*
* @return {@link BasicDataSource#getMaxConnLifetimeMillis()}
*/
long getMaxConnLifetimeMillis();
/**
* See {@link BasicDataSource#getLogExpiredConnections()}
*
* @return {@link BasicDataSource#getLogExpiredConnections()}
* @since 2.1
*/
boolean getLogExpiredConnections();
/**
* See {@link BasicDataSource#getRemoveAbandonedOnBorrow()}
*
* @return {@link BasicDataSource#getRemoveAbandonedOnBorrow()}
*/
boolean getRemoveAbandonedOnBorrow();
/**
* See {@link BasicDataSource#getRemoveAbandonedOnMaintenance()}
*
* @return {@link BasicDataSource#getRemoveAbandonedOnMaintenance()}
*/
boolean getRemoveAbandonedOnMaintenance();
/**
* See {@link BasicDataSource#getRemoveAbandonedTimeout()}
*
* @return {@link BasicDataSource#getRemoveAbandonedTimeout()}
*/
int getRemoveAbandonedTimeout();
/**
* See {@link BasicDataSource#getLogAbandoned()}
*
* @return {@link BasicDataSource#getLogAbandoned()}
*/
boolean getLogAbandoned();
/**
* See {@link BasicDataSource#isClosed()}
*
* @return {@link BasicDataSource#isClosed()}
*/
boolean isClosed();
/**
* See {@link BasicDataSource#getFastFailValidation()}
*
* @return {@link BasicDataSource#getFastFailValidation()}
* @since 2.1
*/
boolean getFastFailValidation();
/**
* See {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
*
* @return {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
* @since 2.1
*/
String[] getDisconnectionSqlCodesAsArray();
}

View File

@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Abstract factory interface for creating {@link java.sql.Connection}s.
*
* @since 2.0
*/
public interface ConnectionFactory {
/**
* Create a new {@link java.sql.Connection} in an implementation specific fashion.
*
* @return a new {@link java.sql.Connection}
* @throws SQLException
* if a database error occurs creating the connection
*/
Connection createConnection() throws SQLException;
}

View File

@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
/*
* Creates {@link ConnectionFactory} instances.
*
* @since 2.7.0
*/
class ConnectionFactoryFactory {
/**
* Creates a new {@link DriverConnectionFactory} allowing for an override through
* {@link BasicDataSource#getDriverClassName()}.
*
* @param basicDataSource Configures creation.
* @param driver The JDBC driver.
* @return a new {@link DriverConnectionFactory} allowing for a {@link BasicDataSource#getDriverClassName()}
* override.
* @throws SQLException Thrown when instantiation fails.
*/
static ConnectionFactory createConnectionFactory(final BasicDataSource basicDataSource, final Driver driver)
throws SQLException {
final Properties connectionProperties = basicDataSource.getConnectionProperties();
final String url = basicDataSource.getUrl();
// Set up the driver connection factory we will use
final String user = basicDataSource.getUsername();
if (user != null) {
connectionProperties.put("user", user);
} else {
basicDataSource.log("DBCP DataSource configured without a 'username'");
}
final String pwd = basicDataSource.getPassword();
if (pwd != null) {
connectionProperties.put("password", pwd);
} else {
basicDataSource.log("DBCP DataSource configured without a 'password'");
}
final String connectionFactoryClassName = basicDataSource.getConnectionFactoryClassName();
if (connectionFactoryClassName != null) {
try {
final Class<?> connectionFactoryFromCCL = Class.forName(connectionFactoryClassName);
return (ConnectionFactory) connectionFactoryFromCCL
.getConstructor(Driver.class, String.class, Properties.class)
.newInstance(driver, url, connectionProperties);
} catch (final Exception t) {
final String message = "Cannot load ConnectionFactory implementation '" + connectionFactoryClassName
+ "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
// Defaults to DriverConnectionFactory
return new DriverConnectionFactory(driver, url, connectionProperties);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
/**
* Constants for use with JMX.
*
* @since 2.0
*/
public class Constants {
public static final String JMX_CONNECTION_POOL_BASE_EXT = ",connectionpool=";
public static final String JMX_CONNECTION_POOL_PREFIX = "connections";
public static final String JMX_CONNECTION_BASE_EXT = JMX_CONNECTION_POOL_BASE_EXT + JMX_CONNECTION_POOL_PREFIX
+ ",connection=";
public static final String JMX_STATEMENT_POOL_BASE_EXT = JMX_CONNECTION_BASE_EXT;
public static final String JMX_STATEMENT_POOL_PREFIX = ",statementpool=statements";
}

View File

@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
/**
* A {@link DataSource}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DataSourceConnectionFactory implements ConnectionFactory {
private final DataSource dataSource;
private final String userName;
private final char[] userPassword;
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
*/
public DataSourceConnectionFactory(final DataSource dataSource) {
this(dataSource, null, (char[]) null);
}
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
* @param userName
* The user name.
* @param userPassword
* The user password.
* @since 2.4.0
*/
public DataSourceConnectionFactory(final DataSource dataSource, final String userName, final char[] userPassword) {
this.dataSource = dataSource;
this.userName = userName;
this.userPassword = Utils.clone(userPassword);
}
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
* @param userName
* The user name.
* @param password
* The user password.
*/
public DataSourceConnectionFactory(final DataSource dataSource, final String userName, final String password) {
this.dataSource = dataSource;
this.userName = userName;
this.userPassword = Utils.toCharArray(password);
}
@Override
public Connection createConnection() throws SQLException {
if (null == userName && null == userPassword) {
return dataSource.getConnection();
}
return dataSource.getConnection(userName, Utils.toString(userPassword));
}
/**
* @return The data source.
* @since 2.6.0
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* @return The user name.
* @since 2.6.0
*/
public String getUserName() {
return userName;
}
/**
* @return The user password.
* @since 2.6.0
*/
public char[] getUserPassword() {
return userPassword;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,654 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
/**
* A base delegating implementation of {@link PreparedStatement}.
* <p>
* All of the methods from the {@link PreparedStatement} interface simply check to see that the
* {@link PreparedStatement} is active, and call the corresponding method on the "delegate" provided in my constructor.
* <p>
* Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
* Statement ensures that the Connection which created it can close any open Statement's on Connection close.
*
* @since 2.0
*/
public class DelegatingPreparedStatement extends DelegatingStatement implements PreparedStatement {
/**
* Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
* which created it.
*
* @param statement
* the {@link PreparedStatement} to delegate all calls to.
* @param connection
* the {@link DelegatingConnection} that created this statement.
*/
public DelegatingPreparedStatement(final DelegatingConnection<?> connection, final PreparedStatement statement) {
super(connection, statement);
}
@Override
public void addBatch() throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().addBatch();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void clearParameters() throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().clearParameters();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public boolean execute() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return getDelegatePreparedStatement().execute();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public ResultSet executeQuery() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return DelegatingResultSet.wrapResultSet(this, getDelegatePreparedStatement().executeQuery());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int executeUpdate() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return getDelegatePreparedStatement().executeUpdate();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
private PreparedStatement getDelegatePreparedStatement() {
return (PreparedStatement) getDelegate();
}
@Override
public ResultSetMetaData getMetaData() throws SQLException {
checkOpen();
try {
return getDelegatePreparedStatement().getMetaData();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public java.sql.ParameterMetaData getParameterMetaData() throws SQLException {
checkOpen();
try {
return getDelegatePreparedStatement().getParameterMetaData();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public void setArray(final int i, final Array x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setArray(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBigDecimal(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int i, final Blob x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBoolean(final int parameterIndex, final boolean x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBoolean(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setByte(final int parameterIndex, final byte x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setByte(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBytes(final int parameterIndex, final byte[] x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBytes(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader, final int length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int i, final Clob x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDate(final int parameterIndex, final Date x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDate(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDate(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDouble(final int parameterIndex, final double x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDouble(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFloat(final int parameterIndex, final float x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setFloat(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setInt(final int parameterIndex, final int x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setInt(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setLong(final int parameterIndex, final long x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setLong(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNCharacterStream(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNCharacterStream(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNCharacterStream(final int parameterIndex, final Reader value, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNCharacterStream(parameterIndex, value, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final NClob value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNString(final int parameterIndex, final String value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNString(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNull(final int parameterIndex, final int sqlType) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNull(parameterIndex, sqlType);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNull(final int paramIndex, final int sqlType, final String typeName) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNull(paramIndex, sqlType, typeName);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x, targetSqlType);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scale)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x, targetSqlType, scale);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setRef(final int i, final Ref x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setRef(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setRowId(final int parameterIndex, final RowId value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setRowId(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setShort(final int parameterIndex, final short x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setShort(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setSQLXML(final int parameterIndex, final SQLXML value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setSQLXML(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setString(final int parameterIndex, final String x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setString(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTime(final int parameterIndex, final Time x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTime(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTime(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTimestamp(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTimestamp(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
/** @deprecated Use setAsciiStream(), setCharacterStream() or setNCharacterStream() */
@Deprecated
@Override
public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setUnicodeStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setURL(final int parameterIndex, final java.net.URL x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setURL(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Returns a String representation of this object.
*
* @return String
*/
@SuppressWarnings("resource")
@Override
public synchronized String toString() {
final Statement statement = getDelegate();
return statement == null ? "NULL" : statement.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,710 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* A base delegating implementation of {@link Statement}.
* <p>
* All of the methods from the {@link Statement} interface simply check to see that the {@link Statement} is active, and
* call the corresponding method on the "delegate" provided in my constructor.
* <p>
* Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
* Statement ensures that the Connection which created it can close any open Statement's on Connection close.
*
* @since 2.0
*/
public class DelegatingStatement extends AbandonedTrace implements Statement {
/** My delegate. */
private Statement statement;
/** The connection that created me. **/
private DelegatingConnection<?> connection;
private boolean closed = false;
/**
* Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
* which created it.
*
* @param statement
* the {@link Statement} to delegate all calls to.
* @param connection
* the {@link DelegatingConnection} that created this statement.
*/
public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) {
super(connection);
this.statement = statement;
this.connection = connection;
}
/**
*
* @throws SQLException
* thrown by the delegating statement.
* @since 2.4.0 made public, was protected in 2.3.0.
*/
public void activate() throws SQLException {
if (statement instanceof DelegatingStatement) {
((DelegatingStatement) statement).activate();
}
}
@Override
public void addBatch(final String sql) throws SQLException {
checkOpen();
try {
statement.addBatch(sql);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void cancel() throws SQLException {
checkOpen();
try {
statement.cancel();
} catch (final SQLException e) {
handleException(e);
}
}
protected void checkOpen() throws SQLException {
if (isClosed()) {
throw new SQLException(this.getClass().getName() + " with address: \"" + this.toString() + "\" is closed.");
}
}
@Override
public void clearBatch() throws SQLException {
checkOpen();
try {
statement.clearBatch();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void clearWarnings() throws SQLException {
checkOpen();
try {
statement.clearWarnings();
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Close this DelegatingStatement, and close any ResultSets that were not explicitly closed.
*/
@Override
public void close() throws SQLException {
if (isClosed()) {
return;
}
final List<Exception> thrownList = new ArrayList<>();
try {
if (connection != null) {
connection.removeTrace(this);
connection = null;
}
// The JDBC spec requires that a statement close any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See bug 17301 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final int size = resultSetList.size();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[size]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
if (connection != null) {
// Does not rethrow e.
connection.handleExceptionNoThrow(e);
}
thrownList.add(e);
}
}
}
clearTrace();
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
if (connection != null) {
// Does not rethrow e.
connection.handleExceptionNoThrow(e);
}
thrownList.add(e);
}
}
} finally {
closed = true;
statement = null;
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
}
@Override
public void closeOnCompletion() throws SQLException {
checkOpen();
try {
Jdbc41Bridge.closeOnCompletion(statement);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public boolean execute(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, autoGeneratedKeys);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, columnIndexes);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, columnNames);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public int[] executeBatch() throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeBatch();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public ResultSet executeQuery(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql));
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int executeUpdate(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, autoGeneratedKeys);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, columnIndexes);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, columnNames);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
protected void finalize() throws Throwable {
// This is required because of statement pooling. The poolable
// statements will always be strongly held by the statement pool. If the
// delegating statements that wrap the poolable statement are not
// strongly held they will be garbage collected but at that point the
// poolable statements need to be returned to the pool else there will
// be a leak of statements from the pool. Closing this statement will
// close all the wrapped statements and return any poolable statements
// to the pool.
close();
super.finalize();
}
@Override
public Connection getConnection() throws SQLException {
checkOpen();
return getConnectionInternal(); // return the delegating connection that created this
}
protected DelegatingConnection<?> getConnectionInternal() {
return connection;
}
/**
* Returns my underlying {@link Statement}.
*
* @return my underlying {@link Statement}.
* @see #getInnermostDelegate
*/
public Statement getDelegate() {
return statement;
}
@Override
public int getFetchDirection() throws SQLException {
checkOpen();
try {
return statement.getFetchDirection();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getFetchSize() throws SQLException {
checkOpen();
try {
return statement.getFetchSize();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public ResultSet getGeneratedKeys() throws SQLException {
checkOpen();
try {
return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
/**
* If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively
* invokes this method on my delegate.
* <p>
* Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when
* no non-{@code DelegatingStatement} delegate can be found by traversing this chain.
* </p>
* <p>
* This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain
* a "genuine" {@link Statement}.
* </p>
*
* @return The innermost delegate.
*
* @see #getDelegate
*/
@SuppressWarnings("resource")
public Statement getInnermostDelegate() {
Statement s = statement;
while (s != null && s instanceof DelegatingStatement) {
s = ((DelegatingStatement) s).getDelegate();
if (this == s) {
return null;
}
}
return s;
}
@Override
public int getMaxFieldSize() throws SQLException {
checkOpen();
try {
return statement.getMaxFieldSize();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getMaxRows() throws SQLException {
checkOpen();
try {
return statement.getMaxRows();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public boolean getMoreResults() throws SQLException {
checkOpen();
try {
return statement.getMoreResults();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean getMoreResults(final int current) throws SQLException {
checkOpen();
try {
return statement.getMoreResults(current);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public int getQueryTimeout() throws SQLException {
checkOpen();
try {
return statement.getQueryTimeout();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public ResultSet getResultSet() throws SQLException {
checkOpen();
try {
return DelegatingResultSet.wrapResultSet(this, statement.getResultSet());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int getResultSetConcurrency() throws SQLException {
checkOpen();
try {
return statement.getResultSetConcurrency();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getResultSetHoldability() throws SQLException {
checkOpen();
try {
return statement.getResultSetHoldability();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getResultSetType() throws SQLException {
checkOpen();
try {
return statement.getResultSetType();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getUpdateCount() throws SQLException {
checkOpen();
try {
return statement.getUpdateCount();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public SQLWarning getWarnings() throws SQLException {
checkOpen();
try {
return statement.getWarnings();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
protected void handleException(final SQLException e) throws SQLException {
if (connection != null) {
connection.handleException(e);
} else {
throw e;
}
}
/*
* Note: This method was protected prior to JDBC 4.
*/
@Override
public boolean isClosed() throws SQLException {
return closed;
}
protected boolean isClosedInternal() {
return closed;
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
checkOpen();
try {
return Jdbc41Bridge.isCloseOnCompletion(statement);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean isPoolable() throws SQLException {
checkOpen();
try {
return statement.isPoolable();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean isWrapperFor(final Class<?> iface) throws SQLException {
if (iface.isAssignableFrom(getClass())) {
return true;
} else if (iface.isAssignableFrom(statement.getClass())) {
return true;
} else {
return statement.isWrapperFor(iface);
}
}
/**
*
* @throws SQLException
* thrown by the delegating statement.
* @since 2.4.0 made public, was protected in 2.3.0.
*/
public void passivate() throws SQLException {
if (statement instanceof DelegatingStatement) {
((DelegatingStatement) statement).passivate();
}
}
protected void setClosedInternal(final boolean closed) {
this.closed = closed;
}
@Override
public void setCursorName(final String name) throws SQLException {
checkOpen();
try {
statement.setCursorName(name);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Sets my delegate.
*
* @param statement
* my delegate.
*/
public void setDelegate(final Statement statement) {
this.statement = statement;
}
@Override
public void setEscapeProcessing(final boolean enable) throws SQLException {
checkOpen();
try {
statement.setEscapeProcessing(enable);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFetchDirection(final int direction) throws SQLException {
checkOpen();
try {
statement.setFetchDirection(direction);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFetchSize(final int rows) throws SQLException {
checkOpen();
try {
statement.setFetchSize(rows);
} catch (final SQLException e) {
handleException(e);
}
}
private void setLastUsedInParent() {
if (connection != null) {
connection.setLastUsed();
}
}
@Override
public void setMaxFieldSize(final int max) throws SQLException {
checkOpen();
try {
statement.setMaxFieldSize(max);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setMaxRows(final int max) throws SQLException {
checkOpen();
try {
statement.setMaxRows(max);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setPoolable(final boolean poolable) throws SQLException {
checkOpen();
try {
statement.setPoolable(poolable);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setQueryTimeout(final int seconds) throws SQLException {
checkOpen();
try {
statement.setQueryTimeout(seconds);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Returns a String representation of this object.
*
* @return String
*/
@Override
public synchronized String toString() {
return statement == null ? "NULL" : statement.toString();
}
@Override
public <T> T unwrap(final Class<T> iface) throws SQLException {
if (iface.isAssignableFrom(getClass())) {
return iface.cast(this);
} else if (iface.isAssignableFrom(statement.getClass())) {
return iface.cast(statement);
} else {
return statement.unwrap(iface);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
/**
* A {@link Driver}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DriverConnectionFactory implements ConnectionFactory {
private final String connectionString;
private final Driver driver;
private final Properties properties;
/**
* Constructs a connection factory for a given Driver.
*
* @param driver
* The Driver.
* @param connectString
* The connection string.
* @param properties
* The connection properties.
*/
public DriverConnectionFactory(final Driver driver, final String connectString, final Properties properties) {
this.driver = driver;
this.connectionString = connectString;
this.properties = properties;
}
@Override
public Connection createConnection() throws SQLException {
return driver.connect(connectionString, properties);
}
/**
* @return The connection String.
* @since 2.6.0
*/
public String getConnectionString() {
return connectionString;
}
/**
* @return The Driver.
* @since 2.6.0
*/
public Driver getDriver() {
return driver;
}
/**
* @return The Properties.
* @since 2.6.0
*/
public Properties getProperties() {
return properties;
}
@Override
public String toString() {
return this.getClass().getName() + " [" + String.valueOf(driver) + ";" + String.valueOf(connectionString) + ";"
+ String.valueOf(properties) + "]";
}
}

View File

@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
/*
* Creates {@link Driver} instances.
*
* @since 2.7.0
*/
class DriverFactory {
static Driver createDriver(final BasicDataSource basicDataSource) throws SQLException {
// Load the JDBC driver class
Driver driverToUse = basicDataSource.getDriver();
String driverClassName = basicDataSource.getDriverClassName();
ClassLoader driverClassLoader = basicDataSource.getDriverClassLoader();
String url = basicDataSource.getUrl();
if (driverToUse == null) {
Class<?> driverFromCCL = null;
if (driverClassName != null) {
try {
try {
if (driverClassLoader == null) {
driverFromCCL = Class.forName(driverClassName);
} else {
driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
}
} catch (final ClassNotFoundException cnfe) {
driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
}
} catch (final Exception t) {
final String message = "Cannot load JDBC driver class '" + driverClassName + "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
try {
if (driverFromCCL == null) {
driverToUse = DriverManager.getDriver(url);
} else {
// Usage of DriverManager is not possible, as it does not
// respect the ContextClassLoader
// N.B. This cast may cause ClassCastException which is
// handled below
driverToUse = (Driver) driverFromCCL.getConstructor().newInstance();
if (!driverToUse.acceptsURL(url)) {
throw new SQLException("No suitable driver", "08001");
}
}
} catch (final Exception t) {
final String message = "Cannot create JDBC driver of class '"
+ (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
return driverToUse;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* A {@link DriverManager}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DriverManagerConnectionFactory implements ConnectionFactory {
static {
// Related to DBCP-212
// Driver manager does not sync loading of drivers that use the service
// provider interface. This will cause issues is multi-threaded
// environments. This hack makes sure the drivers are loaded before
// DBCP tries to use them.
DriverManager.getDrivers();
}
private final String connectionUri;
private final String userName;
private final char[] userPassword;
private final Properties properties;
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @since 2.2
*/
public DriverManagerConnectionFactory(final String connectionUri) {
this.connectionUri = connectionUri;
this.properties = new Properties();
this.userName = null;
this.userPassword = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param properties
* a list of arbitrary string tag/value pairs as connection arguments; normally at least a "user" and
* "password" property should be included.
*/
public DriverManagerConnectionFactory(final String connectionUri, final Properties properties) {
this.connectionUri = connectionUri;
this.properties = properties;
this.userName = null;
this.userPassword = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param userName
* the database user
* @param userPassword
* the user's password
*/
public DriverManagerConnectionFactory(final String connectionUri, final String userName,
final char[] userPassword) {
this.connectionUri = connectionUri;
this.userName = userName;
this.userPassword = Utils.clone(userPassword);
this.properties = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param userName
* the database user
* @param userPassword
* the user's password
*/
public DriverManagerConnectionFactory(final String connectionUri, final String userName,
final String userPassword) {
this.connectionUri = connectionUri;
this.userName = userName;
this.userPassword = Utils.toCharArray(userPassword);
this.properties = null;
}
@Override
public Connection createConnection() throws SQLException {
if (null == properties) {
if (userName == null && userPassword == null) {
return DriverManager.getConnection(connectionUri);
}
return DriverManager.getConnection(connectionUri, userName, Utils.toString(userPassword));
}
return DriverManager.getConnection(connectionUri, properties);
}
/**
* @return The connection URI.
* @since 2.6.0
*/
public String getConnectionUri() {
return connectionUri;
}
/**
* @return The Properties.
* @since 2.6.0
*/
public Properties getProperties() {
return properties;
}
/**
* @return The user name.
* @since 2.6.0
*/
public String getUserName() {
return userName;
}
}

View File

@@ -0,0 +1,482 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.sql.CommonDataSource;
/**
* Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
* {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
*
* @since 2.6.0
*/
public class Jdbc41Bridge {
/**
* Delegates to {@link Connection#abort(Executor)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
* </p>
*
* @param connection
* the receiver
* @param executor
* See {@link Connection#abort(Executor)}.
* @throws SQLException
* See {@link Connection#abort(Executor)}.
* @see Connection#abort(Executor)
*/
public static void abort(final Connection connection, final Executor executor) throws SQLException {
try {
connection.abort(executor);
} catch (final AbstractMethodError e) {
connection.close();
}
}
/**
* Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
* {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
* </p>
*
* @param databaseMetaData
* See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @throws SQLException
* See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @see DatabaseMetaData#generatedKeyAlwaysReturned()
*/
public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
try {
return databaseMetaData.generatedKeyAlwaysReturned();
} catch (final AbstractMethodError e) {
// do nothing
return false;
}
}
/**
* Delegates to {@link Connection#getNetworkTimeout()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
* </p>
*
* @param connection
* the receiver
* @return See {@link Connection#getNetworkTimeout()}
* @throws SQLException
* See {@link Connection#getNetworkTimeout()}
* @see Connection#getNetworkTimeout()
*/
public static int getNetworkTimeout(final Connection connection) throws SQLException {
try {
return connection.getNetworkTimeout();
} catch (final AbstractMethodError e) {
return 0;
}
}
/**
* Delegates to {@link ResultSet#getObject(int, Class)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
* </p>
*
* @param <T>
* See {@link ResultSet#getObject(int, Class)}
* @param resultSet
* See {@link ResultSet#getObject(int, Class)}
* @param columnIndex
* See {@link ResultSet#getObject(int, Class)}
* @param type
* See {@link ResultSet#getObject(int, Class)}
* @return See {@link ResultSet#getObject(int, Class)}
* @throws SQLException
* See {@link ResultSet#getObject(int, Class)}
* @see ResultSet#getObject(int, Class)
*/
@SuppressWarnings("unchecked")
public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
throws SQLException {
try {
return resultSet.getObject(columnIndex, type);
} catch (final AbstractMethodError e) {
if (type == String.class) {
return (T) resultSet.getString(columnIndex);
}
// Numbers
if (type == Integer.class) {
return (T) Integer.valueOf(resultSet.getInt(columnIndex));
}
if (type == Long.class) {
return (T) Long.valueOf(resultSet.getLong(columnIndex));
}
if (type == Double.class) {
return (T) Double.valueOf(resultSet.getDouble(columnIndex));
}
if (type == Float.class) {
return (T) Float.valueOf(resultSet.getFloat(columnIndex));
}
if (type == Short.class) {
return (T) Short.valueOf(resultSet.getShort(columnIndex));
}
if (type == BigDecimal.class) {
return (T) resultSet.getBigDecimal(columnIndex);
}
if (type == Byte.class) {
return (T) Byte.valueOf(resultSet.getByte(columnIndex));
}
// Dates
if (type == Date.class) {
return (T) resultSet.getDate(columnIndex);
}
if (type == Time.class) {
return (T) resultSet.getTime(columnIndex);
}
if (type == Timestamp.class) {
return (T) resultSet.getTimestamp(columnIndex);
}
// Streams
if (type == InputStream.class) {
return (T) resultSet.getBinaryStream(columnIndex);
}
if (type == Reader.class) {
return (T) resultSet.getCharacterStream(columnIndex);
}
// Other
if (type == Object.class) {
return (T) resultSet.getObject(columnIndex);
}
if (type == Boolean.class) {
return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
}
if (type == Array.class) {
return (T) resultSet.getArray(columnIndex);
}
if (type == Blob.class) {
return (T) resultSet.getBlob(columnIndex);
}
if (type == Clob.class) {
return (T) resultSet.getClob(columnIndex);
}
if (type == Ref.class) {
return (T) resultSet.getRef(columnIndex);
}
if (type == RowId.class) {
return (T) resultSet.getRowId(columnIndex);
}
if (type == SQLXML.class) {
return (T) resultSet.getSQLXML(columnIndex);
}
if (type == URL.class) {
return (T) resultSet.getURL(columnIndex);
}
throw new SQLFeatureNotSupportedException(
String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, Integer.valueOf(columnIndex), type));
}
}
/**
* Delegates to {@link ResultSet#getObject(String, Class)} without throwing a {@link AbstractMethodError}.
*
* @param <T>
* See {@link ResultSet#getObject(String, Class)}
* @param resultSet
* See {@link ResultSet#getObject(String, Class)}
* @param columnLabel
* See {@link ResultSet#getObject(String, Class)}
* @param type
* See {@link ResultSet#getObject(String, Class)}
* @return See {@link ResultSet#getObject(String, Class)}
* @throws SQLException
* See {@link ResultSet#getObject(String, Class)}
* @see ResultSet#getObject(int, Class)
*/
@SuppressWarnings("unchecked")
public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
throws SQLException {
try {
return resultSet.getObject(columnLabel, type);
} catch (final AbstractMethodError e) {
// Numbers
if (type == Integer.class) {
return (T) Integer.valueOf(resultSet.getInt(columnLabel));
}
if (type == Long.class) {
return (T) Long.valueOf(resultSet.getLong(columnLabel));
}
if (type == Double.class) {
return (T) Double.valueOf(resultSet.getDouble(columnLabel));
}
if (type == Float.class) {
return (T) Float.valueOf(resultSet.getFloat(columnLabel));
}
if (type == Short.class) {
return (T) Short.valueOf(resultSet.getShort(columnLabel));
}
if (type == BigDecimal.class) {
return (T) resultSet.getBigDecimal(columnLabel);
}
if (type == Byte.class) {
return (T) Byte.valueOf(resultSet.getByte(columnLabel));
}
// Dates
if (type == Date.class) {
return (T) resultSet.getDate(columnLabel);
}
if (type == Time.class) {
return (T) resultSet.getTime(columnLabel);
}
if (type == Timestamp.class) {
return (T) resultSet.getTimestamp(columnLabel);
}
// Streams
if (type == InputStream.class) {
return (T) resultSet.getBinaryStream(columnLabel);
}
if (type == Reader.class) {
return (T) resultSet.getCharacterStream(columnLabel);
}
// Other
if (type == Object.class) {
return (T) resultSet.getObject(columnLabel);
}
if (type == Boolean.class) {
return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
}
if (type == Array.class) {
return (T) resultSet.getArray(columnLabel);
}
if (type == Blob.class) {
return (T) resultSet.getBlob(columnLabel);
}
if (type == Clob.class) {
return (T) resultSet.getClob(columnLabel);
}
if (type == Ref.class) {
return (T) resultSet.getRef(columnLabel);
}
if (type == RowId.class) {
return (T) resultSet.getRowId(columnLabel);
}
if (type == SQLXML.class) {
return (T) resultSet.getSQLXML(columnLabel);
}
if (type == URL.class) {
return (T) resultSet.getURL(columnLabel);
}
throw new SQLFeatureNotSupportedException(
String.format("resultSet=%s, columnLabel=%s, type=%s", resultSet, columnLabel, type));
}
}
/**
* Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
* {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
* then return null.
* </p>
*
* @param databaseMetaData
* the receiver
* @param catalog
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param schemaPattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param tableNamePattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param columnNamePattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @throws SQLException
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
*/
public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
throws SQLException {
try {
return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
} catch (final AbstractMethodError e) {
// do nothing
return null;
}
}
/**
* Delegates to {@link Connection#getSchema()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
* </p>
*
* @param connection
* the receiver
* @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
* @throws SQLException
* See {@link Connection#getSchema()}.
* @see Connection#getSchema()
*/
public static String getSchema(final Connection connection) throws SQLException {
try {
return connection.getSchema();
} catch (final AbstractMethodError e) {
// do nothing
return null;
}
}
/**
* Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
* </p>
*
* @param connection
* the receiver
* @param executor
* See {@link Connection#setNetworkTimeout(Executor, int)}
* @param milliseconds
* {@link Connection#setNetworkTimeout(Executor, int)}
* @throws SQLException
* {@link Connection#setNetworkTimeout(Executor, int)}
* @see Connection#setNetworkTimeout(Executor, int)
*/
public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
throws SQLException {
try {
connection.setNetworkTimeout(executor, milliseconds);
} catch (final AbstractMethodError e) {
// do nothing
}
}
/**
* Delegates to {@link Connection#setSchema(String)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
* </p>
*
* @param connection
* the receiver
* @param schema
* See {@link Connection#setSchema(String)}.
* @throws SQLException
* See {@link Connection#setSchema(String)}.
* @see Connection#setSchema(String)
*/
public static void setSchema(final Connection connection, final String schema) throws SQLException {
try {
connection.setSchema(schema);
} catch (final AbstractMethodError e) {
// do nothing
}
}
/**
* Delegates to {@link Statement#closeOnCompletion()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
* is closed to then throw an SQLException.
* </p>
*
* @param statement
* See {@link Statement#closeOnCompletion()}
* @throws SQLException
* See {@link Statement#closeOnCompletion()}
* @see Statement#closeOnCompletion()
*/
public static void closeOnCompletion(final Statement statement) throws SQLException {
try {
statement.closeOnCompletion();
} catch (final AbstractMethodError e) {
if (statement.isClosed()) {
throw new SQLException("Statement closed");
}
}
}
/**
* Delegates to {@link Statement#isCloseOnCompletion()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
* connection is closed to then throw an SQLException.
* </p>
*
* @param statement
* See {@link Statement#isCloseOnCompletion()}
* @return See {@link Statement#isCloseOnCompletion()}
* @throws SQLException
* See {@link Statement#isCloseOnCompletion()}
* @see Statement#closeOnCompletion()
*/
public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
try {
return statement.isCloseOnCompletion();
} catch (final AbstractMethodError e) {
if (statement.isClosed()) {
throw new SQLException("Statement closed");
}
return false;
}
}
/**
* Delegates to {@link CommonDataSource#getParentLogger()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
* </p>
*
* @param commonDataSource
* See {@link CommonDataSource#getParentLogger()}
* @return See {@link CommonDataSource#getParentLogger()}
* @throws SQLFeatureNotSupportedException
* See {@link CommonDataSource#getParentLogger()}
*/
public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
try {
return commonDataSource.getParentLogger();
} catch (final AbstractMethodError e) {
throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
/**
* Exception thrown when a connection's maximum lifetime has been exceeded.
*
* @since 2.1
*/
class LifetimeExceededException extends Exception {
private static final long serialVersionUID = -3783783104516492659L;
/**
* Create a LifetimeExceededException.
*/
public LifetimeExceededException() {
super();
}
/**
* Create a LifetimeExceededException with the given message.
*
* @param message
* The message with which to create the exception
*/
public LifetimeExceededException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.util.List;
/**
* An exception wrapping a list of exceptions.
*
* @since 2.4.0
*/
public class ListException extends Exception {
private static final long serialVersionUID = 1L;
private final List<Throwable> exceptionList;
/**
* Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently
* be initialized by a call to {@link #initCause}.
*
* @param message
* the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}
* method.
* @param exceptionList
* a list of exceptions.
*/
public ListException(final String message, final List<Throwable> exceptionList) {
super(message);
this.exceptionList = exceptionList;
}
/**
* Gets the list of exceptions.
*
* @return the list of exceptions.
*/
public List<Throwable> getExceptionList() {
return exceptionList;
}
}

View File

@@ -0,0 +1,26 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
connectionFactory.lifetimeExceeded=The lifetime of the connection [{0}] milliseconds exceeds the maximum permitted value of [{1}] milliseconds
pool.close.fail=Cannot close connection pool.
poolableConnection.validate.fastFail=Fatal SQLException was thrown previously on this connection.
poolableConnectionFactory.validateObject.fail=Failed to validate a poolable connection.
poolingDataSource.factoryConfig=PoolableConnectionFactory not linked to pool. Calling setPool() to fix the configuration.
swallowedExceptionLogger.onSwallowedException=An internal object pool swallowed an Exception.

View File

@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
poolableConnection.validate.fastFail=Fatale SQLException wurde bereits vorher von dieser Verbindung geworfen

View File

@@ -0,0 +1,26 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
connectionFactory.lifetimeExceeded=La durée de vie de la connection de [{0}] millisecondes excède la valeur maximale permis de [{1}] millisecondes
pool.close.fail=Impossible de fermer le pool de connections
poolableConnection.validate.fastFail=Une exception fatale SQLException avait déjà été lancée pour cette connection
poolableConnectionFactory.validateObject.fail=Impossible de valider la connection poolable
poolingDataSource.factoryConfig=La PoolableConnectionFactory n'est pas liée au pool, il faut appeler setPool() pour y remédier et corriger la configuration
swallowedExceptionLogger.onSwallowedException=Un object interne du pool a avalé une exception

View File

@@ -0,0 +1,26 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
connectionFactory.lifetimeExceeded=コネクション[{0}]ミリ秒の有効期間が[{1}]ミリ秒の許容最大値を超えています
pool.close.fail=コネクションプールを停止できません。
poolableConnection.validate.fastFail=このコネクションは過去に致命的な SQLException を送出したことがあります。
poolableConnectionFactory.validateObject.fail=プール可能なコネクションを検証できません。
poolingDataSource.factoryConfig=PoolableConnectionFactory がコネクションプールに接続していません。構成を修復するには setPool() を呼び出してください。
swallowedExceptionLogger.onSwallowedException=内部オブジェクトプールが例外を飲み込みました。

View File

@@ -0,0 +1,26 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
connectionFactory.lifetimeExceeded=해당 연결의 존속시간 [{0}] 밀리초가, 최대 허용치인 [{1}] 밀리초를 초과합니다.
pool.close.fail=데이터베이스 연결 풀을 닫을 수 없습니다.
poolableConnection.validate.fastFail=이 연결에서, 심각한 SQLException이 이전에 발생했습니다.
poolableConnectionFactory.validateObject.fail=Poolable connection이 유효한지 확인하지 못했습니다.
poolingDataSource.factoryConfig=PoolableConnectionFactory가 풀에 연결되지 않았습니다. setPool()을 호출하여 이 설정 문제를 해결합니다.
swallowedExceptionLogger.onSwallowedException=내부 객체 풀이 예외 발생을 무시했습니다.

View File

@@ -0,0 +1,20 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
poolableConnection.validate.fastFail=此连接上预先抛出了致命的 SQLException。
poolingDataSource.factoryConfig=PoolableConnectionFactory 未连接到连接池。请调用 setPool() 修复此配置。
swallowedExceptionLogger.onSwallowedException=一个内部对象池吞并了一个异常。

View File

@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.lang.management.ManagementFactory;
import java.util.Objects;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* Internal wrapper class that allows JMX to be a noop if absent or disabled.
*
* @since 2.2.1
*/
class ObjectNameWrapper {
private static final Log log = LogFactory.getLog(ObjectNameWrapper.class);
private static MBeanServer MBEAN_SERVER = getPlatformMBeanServer();
private static MBeanServer getPlatformMBeanServer() {
try {
return ManagementFactory.getPlatformMBeanServer();
} catch (LinkageError | Exception e) {
// ignore - JMX not available
log.debug("Failed to get platform MBeanServer", e);
return null;
}
}
public static ObjectName unwrap(final ObjectNameWrapper wrapper) {
return wrapper == null ? null : wrapper.unwrap();
}
public static ObjectNameWrapper wrap(final ObjectName objectName) {
return new ObjectNameWrapper(objectName);
}
public static ObjectNameWrapper wrap(final String name) throws MalformedObjectNameException {
return wrap(new ObjectName(name));
}
private final ObjectName objectName;
public ObjectNameWrapper(final ObjectName objectName) {
this.objectName = objectName;
}
public void registerMBean(final Object object) {
if (MBEAN_SERVER == null || objectName == null) {
return;
}
try {
MBEAN_SERVER.registerMBean(object, objectName);
} catch (LinkageError | Exception e) {
log.warn("Failed to complete JMX registration for " + objectName, e);
}
}
/**
* @since 2.7.0
*/
@Override
public String toString() {
return Objects.toString(objectName);
}
public void unregisterMBean() {
if (MBEAN_SERVER == null || objectName == null) {
return;
}
if (MBEAN_SERVER.isRegistered(objectName)) {
try {
MBEAN_SERVER.unregisterMBean(objectName);
} catch (LinkageError | Exception e) {
log.warn("Failed to complete JMX unregistration for " + objectName, e);
}
}
}
public ObjectName unwrap() {
return objectName;
}
}

View File

@@ -0,0 +1,998 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import org.apache.tomcat.dbcp.dbcp2.PoolingConnection.StatementType;
/**
* A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
*
* @since 2.0
*/
public class PStmtKey {
/**
* Builder for prepareCall(String sql).
*/
private class PreparedCallSQL implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql);
}
}
/**
* Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency).
*/
private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
}
}
/**
* Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
*/
private class PreparedCallWithResultSetHoldability implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
resultSetHoldability.intValue());
}
}
/**
* Builder for prepareStatement(String sql).
*/
private class PreparedStatementSQL implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
}
/**
* Builder for prepareStatement(String sql, int autoGeneratedKeys).
*/
private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, autoGeneratedKeys.intValue());
}
}
/**
* Builder for prepareStatement(String sql, int[] columnIndexes).
*/
private class PreparedStatementWithColumnIndexes implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, columnIndexes);
}
}
/**
* Builder for prepareStatement(String sql, String[] columnNames).
*/
private class PreparedStatementWithColumnNames implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, columnNames);
}
}
/**
* Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency).
*/
private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
}
}
/**
* Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
*/
private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
resultSetHoldability.intValue());
}
}
/**
* Interface for Prepared or Callable Statement.
*/
private interface StatementBuilder {
Statement createStatement(Connection connection) throws SQLException;
}
/**
* SQL defining Prepared or Callable Statement
*/
private final String sql;
/**
* Result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>,
* or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
*/
private final Integer resultSetType;
/**
* Result set concurrency. A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
private final Integer resultSetConcurrency;
/**
* Result set holdability. One of the following <code>ResultSet</code> constants:
* <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*/
private final Integer resultSetHoldability;
/** Database catalog. */
private final String catalog;
/** Database schema. */
private final String schema;
/**
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*/
private final Integer autoGeneratedKeys;
/**
* An array of column indexes indicating the columns that should be returned from the inserted row or rows.
*/
private final int[] columnIndexes;
/**
* An array of column names indicating the columns that should be returned from the inserted row or rows.
*/
private final String[] columnNames;
/**
* Statement type, prepared or callable.
*/
private final StatementType statementType;
/** Statement builder */
private transient StatementBuilder builder;
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @deprecated Use {@link #PStmtKey(String, String, String)}.
*/
@Deprecated
public PStmtKey(final String sql) {
this(sql, null, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @deprecated Use {@link #PStmtKey(String, String, String)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog) {
this(sql, catalog, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @deprecated Use @link {@link #PStmtKey(String, String, String, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)}
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetHoldability();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetHoldability();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetConcurrency();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetConcurrency();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
* @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
this.builder = new PreparedStatementWithColumnIndexes();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementSQL();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param statementType
* The SQL statement type, prepared or callable.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)}
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final StatementType statementType,
final Integer autoGeneratedKeys) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = statementType;
this.autoGeneratedKeys = autoGeneratedKeys;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithAutoGeneratedKeys();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema) {
this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) {
this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) {
this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetHoldability();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetHoldability();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetConcurrency();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetConcurrency();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int[] columnIndexes) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
this.builder = new PreparedStatementWithColumnIndexes();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementSQL();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param statementType
* The SQL statement type, prepared or callable.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType,
final Integer autoGeneratedKeys) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = statementType;
this.autoGeneratedKeys = autoGeneratedKeys;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithAutoGeneratedKeys();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final String[] columnNames) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
builder = new PreparedStatementWithColumnNames();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
builder = new PreparedStatementWithColumnNames();
}
/**
* Creates a new Statement from the given Connection.
*
* @param connection
* The Connection to use to create the statement.
* @return The statement.
* @throws SQLException
* Thrown when there is a problem creating the statement.
*/
public Statement createStatement(final Connection connection) throws SQLException {
if (builder == null) {
throw new IllegalStateException("Prepared statement key is invalid.");
}
return builder.createStatement(connection);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PStmtKey other = (PStmtKey) obj;
if (autoGeneratedKeys == null) {
if (other.autoGeneratedKeys != null) {
return false;
}
} else if (!autoGeneratedKeys.equals(other.autoGeneratedKeys)) {
return false;
}
if (catalog == null) {
if (other.catalog != null) {
return false;
}
} else if (!catalog.equals(other.catalog)) {
return false;
}
if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
return false;
}
if (!Arrays.equals(columnNames, other.columnNames)) {
return false;
}
if (resultSetConcurrency == null) {
if (other.resultSetConcurrency != null) {
return false;
}
} else if (!resultSetConcurrency.equals(other.resultSetConcurrency)) {
return false;
}
if (resultSetHoldability == null) {
if (other.resultSetHoldability != null) {
return false;
}
} else if (!resultSetHoldability.equals(other.resultSetHoldability)) {
return false;
}
if (resultSetType == null) {
if (other.resultSetType != null) {
return false;
}
} else if (!resultSetType.equals(other.resultSetType)) {
return false;
}
if (schema == null) {
if (other.schema != null) {
return false;
}
} else if (!schema.equals(other.schema)) {
return false;
}
if (sql == null) {
if (other.sql != null) {
return false;
}
} else if (!sql.equals(other.sql)) {
return false;
}
if (statementType != other.statementType) {
return false;
}
return true;
}
/**
* Gets a flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*
* @return a flag indicating whether auto-generated keys should be returned.
*/
public Integer getAutoGeneratedKeys() {
return autoGeneratedKeys;
}
/**
* The catalog.
*
* @return The catalog.
*/
public String getCatalog() {
return catalog;
}
/**
* Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
*
* @return An array of column indexes.
*/
public int[] getColumnIndexes() {
return columnIndexes;
}
/**
* Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
*
* @return An array of column names.
*/
public String[] getColumnNames() {
return columnNames;
}
/**
* Gets the result set concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*
* @return The result set concurrency type.
*/
public Integer getResultSetConcurrency() {
return resultSetConcurrency;
}
/**
* Gets the result set holdability, one of the following <code>ResultSet</code> constants:
* <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*
* @return The result set holdability.
*/
public Integer getResultSetHoldability() {
return resultSetHoldability;
}
/**
* Gets the result set type, one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
*
* @return the result set type.
*/
public Integer getResultSetType() {
return resultSetType;
}
/**
* The schema.
*
* @return The catalog.
*/
public String getSchema() {
return schema;
}
/**
* Gets the SQL statement.
*
* @return the SQL statement.
*/
public String getSql() {
return sql;
}
/**
* The SQL statement type.
*
* @return The SQL statement type.
*/
public StatementType getStmtType() {
return statementType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((autoGeneratedKeys == null) ? 0 : autoGeneratedKeys.hashCode());
result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
result = prime * result + Arrays.hashCode(columnIndexes);
result = prime * result + Arrays.hashCode(columnNames);
result = prime * result + ((resultSetConcurrency == null) ? 0 : resultSetConcurrency.hashCode());
result = prime * result + ((resultSetHoldability == null) ? 0 : resultSetHoldability.hashCode());
result = prime * result + ((resultSetType == null) ? 0 : resultSetType.hashCode());
result = prime * result + ((schema == null) ? 0 : schema.hashCode());
result = prime * result + ((sql == null) ? 0 : sql.hashCode());
result = prime * result + ((statementType == null) ? 0 : statementType.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer buf = new StringBuffer();
buf.append("PStmtKey: sql=");
buf.append(sql);
buf.append(", catalog=");
buf.append(catalog);
buf.append(", schema=");
buf.append(schema);
buf.append(", resultSetType=");
buf.append(resultSetType);
buf.append(", resultSetConcurrency=");
buf.append(resultSetConcurrency);
buf.append(", resultSetHoldability=");
buf.append(resultSetHoldability);
buf.append(", autoGeneratedKeys=");
buf.append(autoGeneratedKeys);
buf.append(", columnIndexes=");
buf.append(Arrays.toString(columnIndexes));
buf.append(", columnNames=");
buf.append(Arrays.toString(columnNames));
buf.append(", statementType=");
buf.append(statementType);
return buf.toString();
}
}

View File

@@ -0,0 +1,145 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
/**
* A {@link DelegatingCallableStatement} that cooperates with {@link PoolingConnection} to implement a pool of
* {@link CallableStatement}s.
* <p>
* The {@link #close} method returns this statement to its containing pool. (See {@link PoolingConnection}.)
*
* @see PoolingConnection
* @since 2.0
*/
public class PoolableCallableStatement extends DelegatingCallableStatement {
/**
* The {@link KeyedObjectPool} from which this CallableStatement was obtained.
*/
private final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool;
/**
* Key for this statement in the containing {@link KeyedObjectPool}.
*/
private final PStmtKey key;
/**
* Constructor.
*
* @param callableStatement
* the underlying {@link CallableStatement}
* @param key
* the key for this statement in the {@link KeyedObjectPool}
* @param pool
* the {@link KeyedObjectPool} from which this CallableStatement was obtained
* @param connection
* the {@link DelegatingConnection} that created this CallableStatement
*/
public PoolableCallableStatement(final CallableStatement callableStatement, final PStmtKey key,
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool,
final DelegatingConnection<Connection> connection) {
super(connection, callableStatement);
this.pool = pool;
this.key = key;
// Remove from trace now because this statement will be
// added by the activate method.
removeThisTrace(getConnectionInternal());
}
/**
* Returns the CallableStatement to the pool. If {{@link #isClosed()}, this is a No-op.
*/
@Override
public void close() throws SQLException {
// calling close twice should have no effect
if (!isClosed()) {
try {
pool.returnObject(key, this);
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close CallableStatement (return to pool failed)", e);
}
}
}
/**
* Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection that created
* it.
*
* @since 2.4.0 made public, was protected in 2.3.0.
*/
@Override
public void activate() throws SQLException {
setClosedInternal(false);
if (getConnectionInternal() != null) {
getConnectionInternal().addTrace(this);
}
super.activate();
}
/**
* Passivates to prepare for return to the pool. Removes the trace associated with this CallableStatement from the
* Connection that created it. Also closes any associated ResultSets.
*
* @since 2.4.0 made public, was protected in 2.3.0.
*/
@Override
public void passivate() throws SQLException {
setClosedInternal(true);
removeThisTrace(getConnectionInternal());
// The JDBC spec requires that a statement close any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See DBCP-10 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final List<Exception> thrownList = new ArrayList<>();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[resultSetList.size()]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
thrownList.add(e);
}
}
}
clearTrace();
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
super.passivate();
}
}

View File

@@ -0,0 +1,348 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
/**
* A delegating connection that, rather than closing the underlying connection, returns itself to an {@link ObjectPool}
* when closed.
*
* @since 2.0
*/
public class PoolableConnection extends DelegatingConnection<Connection> implements PoolableConnectionMXBean {
private static MBeanServer MBEAN_SERVER;
static {
try {
MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();
} catch (NoClassDefFoundError | Exception ex) {
// ignore - JMX not available
}
}
/** The pool to which I should return. */
private final ObjectPool<PoolableConnection> pool;
private final ObjectNameWrapper jmxObjectName;
// Use a prepared statement for validation, retaining the last used SQL to
// check if the validation query has changed.
private PreparedStatement validationPreparedStatement;
private String lastValidationSql;
/**
* Indicate that unrecoverable SQLException was thrown when using this connection. Such a connection should be
* considered broken and not pass validation in the future.
*/
private boolean fatalSqlExceptionThrown = false;
/**
* SQL_STATE codes considered to signal fatal conditions. Overrides the defaults in
* {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}).
*/
private final Collection<String> disconnectionSqlCodes;
/** Whether or not to fast fail validation after fatal connection errors */
private final boolean fastFailValidation;
/**
*
* @param conn
* my underlying connection
* @param pool
* the pool to which I should return when closed
* @param jmxObjectName
* JMX name
* @param disconnectSqlCodes
* SQL_STATE codes considered fatal disconnection errors
* @param fastFailValidation
* true means fatal disconnection errors cause subsequent validations to fail immediately (no attempt to
* run query or isValid)
*/
public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
final ObjectName jmxObjectName, final Collection<String> disconnectSqlCodes,
final boolean fastFailValidation) {
super(conn);
this.pool = pool;
this.jmxObjectName = ObjectNameWrapper.wrap(jmxObjectName);
this.disconnectionSqlCodes = disconnectSqlCodes;
this.fastFailValidation = fastFailValidation;
if (jmxObjectName != null) {
try {
MBEAN_SERVER.registerMBean(this, jmxObjectName);
} catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
// For now, simply skip registration
}
}
}
/**
*
* @param conn
* my underlying connection
* @param pool
* the pool to which I should return when closed
* @param jmxName
* JMX name
*/
public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
final ObjectName jmxName) {
this(conn, pool, jmxName, null, true);
}
@Override
protected void passivate() throws SQLException {
super.passivate();
setClosedInternal(true);
}
/**
* {@inheritDoc}
* <p>
* This method should not be used by a client to determine whether or not a connection should be return to the
* connection pool (by calling {@link #close()}). Clients should always attempt to return a connection to the pool
* once it is no longer required.
*/
@Override
public boolean isClosed() throws SQLException {
if (isClosedInternal()) {
return true;
}
if (getDelegateInternal().isClosed()) {
// Something has gone wrong. The underlying connection has been
// closed without the connection being returned to the pool. Return
// it now.
close();
return true;
}
return false;
}
/**
* Returns me to my pool.
*/
@Override
public synchronized void close() throws SQLException {
if (isClosedInternal()) {
// already closed
return;
}
boolean isUnderlyingConnectionClosed;
try {
isUnderlyingConnectionClosed = getDelegateInternal().isClosed();
} catch (final SQLException e) {
try {
pool.invalidateObject(this);
} catch (final IllegalStateException ise) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final Exception ie) {
// DO NOTHING the original exception will be rethrown
}
throw new SQLException("Cannot close connection (isClosed check failed)", e);
}
/*
* Can't set close before this code block since the connection needs to be open when validation runs. Can't set
* close after this code block since by then the connection will have been returned to the pool and may have
* been borrowed by another thread. Therefore, the close flag is set in passivate().
*/
if (isUnderlyingConnectionClosed) {
// Abnormal close: underlying connection closed unexpectedly, so we
// must destroy this proxy
try {
pool.invalidateObject(this);
} catch (final IllegalStateException e) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final Exception e) {
throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
}
} else {
// Normal close: underlying connection is still open, so we
// simply need to return this proxy to the pool
try {
pool.returnObject(this);
} catch (final IllegalStateException e) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection (return to pool failed)", e);
}
}
}
/**
* Actually close my underlying {@link Connection}.
*/
@Override
public void reallyClose() throws SQLException {
if (jmxObjectName != null) {
jmxObjectName.unregisterMBean();
}
if (validationPreparedStatement != null) {
try {
validationPreparedStatement.close();
} catch (final SQLException sqle) {
// Ignore
}
}
super.closeInternal();
}
/**
* Expose the {@link #toString()} method via a bean getter so it can be read as a property via JMX.
*/
@Override
public String getToString() {
return toString();
}
/**
* Validates the connection, using the following algorithm:
* <ol>
* <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
* thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
* <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
* returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
* <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
* least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
* </ol>
*
* @param sql
* The validation SQL query.
* @param timeoutSeconds
* The validation timeout in seconds.
* @throws SQLException
* Thrown when validation fails or an SQLException occurs during validation
*/
public void validate(final String sql, int timeoutSeconds) throws SQLException {
if (fastFailValidation && fatalSqlExceptionThrown) {
throw new SQLException(Utils.getMessage("poolableConnection.validate.fastFail"));
}
if (sql == null || sql.length() == 0) {
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
if (!isValid(timeoutSeconds)) {
throw new SQLException("isValid() returned false");
}
return;
}
if (!sql.equals(lastValidationSql)) {
lastValidationSql = sql;
// Has to be the innermost delegate else the prepared statement will
// be closed when the pooled connection is passivated.
validationPreparedStatement = getInnermostDelegateInternal().prepareStatement(sql);
}
if (timeoutSeconds > 0) {
validationPreparedStatement.setQueryTimeout(timeoutSeconds);
}
try (ResultSet rs = validationPreparedStatement.executeQuery()) {
if (!rs.next()) {
throw new SQLException("validationQuery didn't return a row");
}
} catch (final SQLException sqle) {
throw sqle;
}
}
/**
* Checks the SQLState of the input exception and any nested SQLExceptions it wraps.
* <p>
* If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the
* configured list of fatal exception codes. If this property is not set, codes are compared against the default
* codes in {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
* Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
* </p>
*
* @param e
* SQLException to be examined
* @return true if the exception signals a disconnection
*/
private boolean isDisconnectionSqlException(final SQLException e) {
boolean fatalException = false;
final String sqlState = e.getSQLState();
if (sqlState != null) {
fatalException = disconnectionSqlCodes == null
? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX)
|| Utils.DISCONNECTION_SQL_CODES.contains(sqlState)
: disconnectionSqlCodes.contains(sqlState);
if (!fatalException) {
final SQLException nextException = e.getNextException();
if (nextException != null && nextException != e) {
fatalException = isDisconnectionSqlException(e.getNextException());
}
}
}
return fatalException;
}
@Override
protected void handleException(final SQLException e) throws SQLException {
fatalSqlExceptionThrown |= isDisconnectionSqlException(e);
super.handleException(e);
}
/**
* @return The disconnection SQL codes.
* @since 2.6.0
*/
public Collection<String> getDisconnectionSqlCodes() {
return disconnectionSqlCodes;
}
/**
* @return Whether to fail-fast.
* @since 2.6.0
*/
public boolean isFastFailValidation() {
return fastFailValidation;
}
}

View File

@@ -0,0 +1,657 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.PooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* A {@link PooledObjectFactory} that creates {@link PoolableConnection}s.
*
* @since 2.0
*/
public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
/**
* Internal constant to indicate the level is not set.
*/
static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
private final ConnectionFactory connectionFactory;
private final ObjectName dataSourceJmxObjectName;
private volatile String validationQuery;
private volatile int validationQueryTimeoutSeconds = -1;
private Collection<String> connectionInitSqls;
private Collection<String> disconnectionSqlCodes;
private boolean fastFailValidation = true;
private volatile ObjectPool<PoolableConnection> pool;
private Boolean defaultReadOnly;
private Boolean defaultAutoCommit;
private boolean autoCommitOnReturn = true;
private boolean rollbackOnReturn = true;
private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
private String defaultCatalog;
private String defaultSchema;
private boolean cacheState;
private boolean poolStatements;
private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
private long maxConnLifetimeMillis = -1;
private final AtomicLong connectionIndex = new AtomicLong(0);
private Integer defaultQueryTimeoutSeconds;
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param connFactory
* the {@link ConnectionFactory} from which to obtain base {@link Connection}s
* @param dataSourceJmxObjectName
* The JMX object name, may be null.
*/
public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) {
this.connectionFactory = connFactory;
this.dataSourceJmxObjectName = dataSourceJmxObjectName;
}
@Override
public void activateObject(final PooledObject<PoolableConnection> p) throws Exception {
validateLifetime(p);
final PoolableConnection conn = p.getObject();
conn.activate();
if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) {
conn.setAutoCommit(defaultAutoCommit.booleanValue());
}
if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION
&& conn.getTransactionIsolation() != defaultTransactionIsolation) {
conn.setTransactionIsolation(defaultTransactionIsolation);
}
if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) {
conn.setReadOnly(defaultReadOnly.booleanValue());
}
if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) {
conn.setCatalog(defaultCatalog);
}
if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(conn))) {
Jdbc41Bridge.setSchema(conn, defaultSchema);
}
conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
}
@Override
public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
p.getObject().reallyClose();
}
/**
* @return The cache state.
* @since Made public in 2.6.0.
*/
public boolean getCacheState() {
return cacheState;
}
/**
* @return The connection factory.
* @since Made public in 2.6.0.
*/
public ConnectionFactory getConnectionFactory() {
return connectionFactory;
}
protected AtomicLong getConnectionIndex() {
return connectionIndex;
}
/**
* @return The collection of initialization SQL statements.
* @since 2.6.0
*/
public Collection<String> getConnectionInitSqls() {
return connectionInitSqls;
}
/**
* @return The data source JMX ObjectName
* @since Made public in 2.6.0.
*/
public ObjectName getDataSourceJmxName() {
return dataSourceJmxObjectName;
}
/**
* @return The data source JMS ObjectName.
* @since 2.6.0
*/
public ObjectName getDataSourceJmxObjectName() {
return dataSourceJmxObjectName;
}
/**
* @return Default auto-commit value.
* @since 2.6.0
*/
public Boolean getDefaultAutoCommit() {
return defaultAutoCommit;
}
/**
* @return Default catalog.
* @since 2.6.0
*/
public String getDefaultCatalog() {
return defaultCatalog;
}
/**
* @return Default query timeout in seconds.
*/
public Integer getDefaultQueryTimeout() {
return defaultQueryTimeoutSeconds;
}
/**
* @return Default query timeout in seconds.
* @since 2.6.0
*/
public Integer getDefaultQueryTimeoutSeconds() {
return defaultQueryTimeoutSeconds;
}
/**
* @return Default read-only-value.
* @since 2.6.0
*/
public Boolean getDefaultReadOnly() {
return defaultReadOnly;
}
/**
* @return Default schema.
* @since 2.6.0
*/
public String getDefaultSchema() {
return defaultSchema;
}
/**
* @return Default transaction isolation.
* @since 2.6.0
*/
public int getDefaultTransactionIsolation() {
return defaultTransactionIsolation;
}
/**
* SQL_STATE codes considered to signal fatal conditions.
* <p>
* Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with
* {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #isFastFailValidation()} is
* {@code true}, whenever connections created by this factory generate exceptions with SQL_STATE codes in this list,
* they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at isValid or
* validation query).
* </p>
* <p>
* If {@link #isFastFailValidation()} is {@code false} setting this property has no effect.
* </p>
*
* @return SQL_STATE codes overriding defaults
* @since 2.1
*/
public Collection<String> getDisconnectionSqlCodes() {
return disconnectionSqlCodes;
}
/**
* @return Maximum connection lifetime in milliseconds.
* @since 2.6.0
*/
public long getMaxConnLifetimeMillis() {
return maxConnLifetimeMillis;
}
protected int getMaxOpenPreparedStatements() {
return maxOpenPreparedStatements;
}
/**
* Returns the {@link ObjectPool} in which {@link Connection}s are pooled.
*
* @return the connection pool
*/
public synchronized ObjectPool<PoolableConnection> getPool() {
return pool;
}
/**
* @return Whether to pool statements.
* @since Made public in 2.6.0.
*/
public boolean getPoolStatements() {
return poolStatements;
}
/**
* @return Validation query.
* @since 2.6.0
*/
public String getValidationQuery() {
return validationQuery;
}
/**
* @return Validation query timeout in seconds.
* @since 2.6.0
*/
public int getValidationQueryTimeoutSeconds() {
return validationQueryTimeoutSeconds;
}
protected void initializeConnection(final Connection conn) throws SQLException {
final Collection<String> sqls = connectionInitSqls;
if (conn.isClosed()) {
throw new SQLException("initializeConnection: connection closed");
}
if (null != sqls) {
try (Statement stmt = conn.createStatement()) {
for (final String sql : sqls) {
Objects.requireNonNull(sql, "null connectionInitSqls element");
stmt.execute(sql);
}
}
}
}
/**
* @return Whether to auto-commit on return.
* @since 2.6.0
*/
public boolean isAutoCommitOnReturn() {
return autoCommitOnReturn;
}
/**
* @return Whether to auto-commit on return.
* @deprecated Use {@link #isAutoCommitOnReturn()}.
*/
@Deprecated
public boolean isEnableAutoCommitOnReturn() {
return autoCommitOnReturn;
}
/**
* True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
* SQL_STATE indicating fatal disconnection errors.
*
* @return true if connections created by this factory will fast fail validation.
* @see #setDisconnectionSqlCodes(Collection)
* @since 2.1
* @since 2.5.0 Defaults to true, previous versions defaulted to false.
*/
public boolean isFastFailValidation() {
return fastFailValidation;
}
/**
* @return Whether to rollback on return.
*/
public boolean isRollbackOnReturn() {
return rollbackOnReturn;
}
@Override
public PooledObject<PoolableConnection> makeObject() throws Exception {
Connection conn = connectionFactory.createConnection();
if (conn == null) {
throw new IllegalStateException("Connection factory returned null from createConnection");
}
try {
initializeConnection(conn);
} catch (final SQLException sqle) {
// Make sure the connection is closed
try {
conn.close();
} catch (final SQLException ignore) {
// ignore
}
// Rethrow original exception so it is visible to caller
throw sqle;
}
final long connIndex = connectionIndex.getAndIncrement();
if (poolStatements) {
conn = new PoolingConnection(conn);
final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
config.setMaxTotalPerKey(-1);
config.setBlockWhenExhausted(false);
config.setMaxWaitMillis(0);
config.setMaxIdlePerKey(1);
config.setMaxTotal(maxOpenPreparedStatements);
if (dataSourceJmxObjectName != null) {
final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString());
base.append(Constants.JMX_CONNECTION_BASE_EXT);
base.append(Long.toString(connIndex));
config.setJmxNameBase(base.toString());
config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
} else {
config.setJmxEnabled(false);
}
final PoolingConnection poolingConn = (PoolingConnection) conn;
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(
poolingConn, config);
poolingConn.setStatementPool(stmtPool);
poolingConn.setCacheState(cacheState);
}
// Register this connection with JMX
ObjectName connJmxName;
if (dataSourceJmxObjectName == null) {
connJmxName = null;
} else {
connJmxName = new ObjectName(
dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex);
}
final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName, disconnectionSqlCodes,
fastFailValidation);
pc.setCacheState(cacheState);
return new DefaultPooledObject<>(pc);
}
@Override
public void passivateObject(final PooledObject<PoolableConnection> p) throws Exception {
validateLifetime(p);
final PoolableConnection conn = p.getObject();
Boolean connAutoCommit = null;
if (rollbackOnReturn) {
connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
if (!connAutoCommit.booleanValue() && !conn.isReadOnly()) {
conn.rollback();
}
}
conn.clearWarnings();
// DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should
// have autoCommit enabled
if (autoCommitOnReturn) {
if (connAutoCommit == null) {
connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
}
if (!connAutoCommit.booleanValue()) {
conn.setAutoCommit(true);
}
}
conn.passivate();
}
public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
this.autoCommitOnReturn = autoCommitOnReturn;
}
public void setCacheState(final boolean cacheState) {
this.cacheState = cacheState;
}
/**
* Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off
* connection initialization.
*
* @param connectionInitSqls
* SQL statement to initialize {@link Connection}s.
*/
public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
this.connectionInitSqls = connectionInitSqls;
}
/**
* Sets the default "auto commit" setting for borrowed {@link Connection}s
*
* @param defaultAutoCommit
* the default "auto commit" setting for borrowed {@link Connection}s
*/
public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
this.defaultAutoCommit = defaultAutoCommit;
}
/**
* Sets the default "catalog" setting for borrowed {@link Connection}s
*
* @param defaultCatalog
* the default "catalog" setting for borrowed {@link Connection}s
*/
public void setDefaultCatalog(final String defaultCatalog) {
this.defaultCatalog = defaultCatalog;
}
public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
}
/**
* Sets the default "read only" setting for borrowed {@link Connection}s
*
* @param defaultReadOnly
* the default "read only" setting for borrowed {@link Connection}s
*/
public void setDefaultReadOnly(final Boolean defaultReadOnly) {
this.defaultReadOnly = defaultReadOnly;
}
/**
* Sets the default "schema" setting for borrowed {@link Connection}s
*
* @param defaultSchema
* the default "schema" setting for borrowed {@link Connection}s
* @since 2.5.0
*/
public void setDefaultSchema(final String defaultSchema) {
this.defaultSchema = defaultSchema;
}
/**
* Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s
*
* @param defaultTransactionIsolation
* the default "Transaction Isolation" setting for returned {@link Connection}s
*/
public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
this.defaultTransactionIsolation = defaultTransactionIsolation;
}
/**
* @param disconnectionSqlCodes
* The disconnection SQL codes.
* @see #getDisconnectionSqlCodes()
* @since 2.1
*/
public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
this.disconnectionSqlCodes = disconnectionSqlCodes;
}
/**
* @param autoCommitOnReturn Whether to auto-commit on return.
* @deprecated Use {@link #setAutoCommitOnReturn(boolean)}.
*/
@Deprecated
public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
this.autoCommitOnReturn = autoCommitOnReturn;
}
/**
* @see #isFastFailValidation()
* @param fastFailValidation
* true means connections created by this factory will fast fail validation
* @since 2.1
*/
public void setFastFailValidation(final boolean fastFailValidation) {
this.fastFailValidation = fastFailValidation;
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
*
* @param maxConnLifetimeMillis
* The maximum lifetime in milliseconds.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* Sets the maximum number of open prepared statements.
*
* @param maxOpenPreparedStatements
* The maximum number of open prepared statements.
*/
public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}
/**
* Deprecated due to typo in method name.
*
* @param maxOpenPreparedStatements
* The maximum number of open prepared statements.
* @deprecated Use {@link #setMaxOpenPreparedStatements(int)}.
*/
@Deprecated // Due to typo in method name.
public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
setMaxOpenPreparedStatements(maxOpenPreparedStatements);
}
/**
* Sets the {@link ObjectPool} in which to pool {@link Connection}s.
*
* @param pool
* the {@link ObjectPool} in which to pool those {@link Connection}s
*/
public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
if (null != this.pool && pool != this.pool) {
try {
this.pool.close();
} catch (final Exception e) {
// ignored !?!
}
}
this.pool = pool;
}
public void setPoolStatements(final boolean poolStatements) {
this.poolStatements = poolStatements;
}
public void setRollbackOnReturn(final boolean rollbackOnReturn) {
this.rollbackOnReturn = rollbackOnReturn;
}
/**
* Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If
* not specified, {@link Connection#isValid(int)} will be used to validate connections.
*
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s.
*/
public void setValidationQuery(final String validationQuery) {
this.validationQuery = validationQuery;
}
/**
* Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
* response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
*
* @param validationQueryTimeoutSeconds
* new validation query timeout value in seconds
*/
public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
}
public void validateConnection(final PoolableConnection conn) throws SQLException {
if (conn.isClosed()) {
throw new SQLException("validateConnection: connection closed");
}
conn.validate(validationQuery, validationQueryTimeoutSeconds);
}
private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded",
Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis)));
}
}
}
@Override
public boolean validateObject(final PooledObject<PoolableConnection> p) {
try {
validateLifetime(p);
validateConnection(p.getObject());
return true;
} catch (final Exception e) {
if (log.isDebugEnabled()) {
log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
}
return false;
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.SQLException;
/**
* Defines the attributes and methods that will be exposed via JMX for {@link PoolableConnection} instances.
*
* @since 2.0
*/
public interface PoolableConnectionMXBean {
// Read-only properties
boolean isClosed() throws SQLException;
// SQLWarning getWarnings() throws SQLException;
String getToString();
// Read-write properties
boolean getAutoCommit() throws SQLException;
void setAutoCommit(boolean autoCommit) throws SQLException;
boolean getCacheState();
void setCacheState(boolean cacheState);
String getCatalog() throws SQLException;
void setCatalog(String catalog) throws SQLException;
int getHoldability() throws SQLException;
void setHoldability(int holdability) throws SQLException;
boolean isReadOnly() throws SQLException;
void setReadOnly(boolean readOnly) throws SQLException;
String getSchema() throws SQLException;
void setSchema(String schema) throws SQLException;
int getTransactionIsolation() throws SQLException;
void setTransactionIsolation(int level) throws SQLException;
// Methods
void clearCachedState();
void clearWarnings() throws SQLException;
void close() throws SQLException;
void reallyClose() throws SQLException;
}

View File

@@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
/**
* A {@link DelegatingPreparedStatement} that cooperates with {@link PoolingConnection} to implement a pool of
* {@link PreparedStatement}s.
* <p>
* My {@link #close} method returns me to my containing pool. (See {@link PoolingConnection}.)
* </p>
*
* @param <K>
* the key type
*
* @see PoolingConnection
* @since 2.0
*/
public class PoolablePreparedStatement<K> extends DelegatingPreparedStatement {
/**
* The {@link KeyedObjectPool} from which I was obtained.
*/
private final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool;
/**
* My "key" as used by {@link KeyedObjectPool}.
*/
private final K key;
private volatile boolean batchAdded = false;
/**
* Constructor.
*
* @param stmt
* my underlying {@link PreparedStatement}
* @param key
* my key" as used by {@link KeyedObjectPool}
* @param pool
* the {@link KeyedObjectPool} from which I was obtained.
* @param conn
* the {@link java.sql.Connection Connection} from which I was created
*/
public PoolablePreparedStatement(final PreparedStatement stmt, final K key,
final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool, final DelegatingConnection<?> conn) {
super(conn, stmt);
this.pool = pool;
this.key = key;
// Remove from trace now because this statement will be
// added by the activate method.
removeThisTrace(getConnectionInternal());
}
/**
* Add batch.
*/
@Override
public void addBatch() throws SQLException {
super.addBatch();
batchAdded = true;
}
/**
* Clear Batch.
*/
@Override
public void clearBatch() throws SQLException {
batchAdded = false;
super.clearBatch();
}
/**
* Return me to my pool.
*/
@Override
public void close() throws SQLException {
// calling close twice should have no effect
if (!isClosed()) {
try {
pool.returnObject(key, this);
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close preparedstatement (return to pool failed)", e);
}
}
}
@Override
public void activate() throws SQLException {
setClosedInternal(false);
if (getConnectionInternal() != null) {
getConnectionInternal().addTrace(this);
}
super.activate();
}
@Override
public void passivate() throws SQLException {
// DBCP-372. clearBatch with throw an exception if called when the
// connection is marked as closed.
if (batchAdded) {
clearBatch();
}
setClosedInternal(true);
removeThisTrace(getConnectionInternal());
// The JDBC spec requires that a statement closes any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See bug 17301 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final List<Exception> thrownList = new ArrayList<>();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[resultSetList.size()]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
thrownList.add(e);
}
}
}
clearTrace();
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
super.passivate();
}
}

View File

@@ -0,0 +1,602 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.NoSuchElementException;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
* <p>
* The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each
* time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of
* the returned statement doesn't actually close the statement, but rather returns it to the pool. (See
* {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
* </p>
*
* @see PoolablePreparedStatement
* @since 2.0
*/
public class PoolingConnection extends DelegatingConnection<Connection>
implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
/**
* Statement types.
*
* @since 2.0 protected enum.
* @since 2.4.0 public enum.
*/
public enum StatementType {
/**
* Callable statement.
*/
CALLABLE_STATEMENT,
/**
* Prepared statement.
*/
PREPARED_STATEMENT
}
/** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pstmtPool;
/**
* Constructor.
*
* @param connection
* the underlying {@link Connection}.
*/
public PoolingConnection(final Connection connection) {
super(connection);
}
/**
* {@link KeyedPooledObjectFactory} method for activating pooled statements.
*
* @param key
* ignored
* @param pooledObject
* wrapped pooled statement to be activated
*/
@Override
public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().activate();
}
/**
* Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the
* underlying connection.
*/
@Override
public synchronized void close() throws SQLException {
try {
if (null != pstmtPool) {
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldpool = pstmtPool;
pstmtPool = null;
try {
oldpool.close();
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection", e);
}
}
} finally {
try {
getDelegateInternal().close();
} finally {
setClosedInternal(true);
}
}
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param columnIndexes
* column indexes
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int columnIndexes[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
}
protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
resultSetHoldability);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
resultSetHoldability, statementType);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param columnNames
* column names
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final String columnNames[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
}
/**
* {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements.
* Closes the underlying statement.
*
* @param key
* ignored
* @param pooledObject
* the wrapped pooled statement to be destroyed.
*/
@Override
public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().getInnermostDelegate().close();
}
private String getCatalogOrNull() {
String catalog = null;
try {
catalog = getCatalog();
} catch (final SQLException e) {
// Ignored
}
return catalog;
}
private String getSchemaOrNull() {
String catalog = null;
try {
catalog = getSchema();
} catch (final SQLException e) {
// Ignored
}
return catalog;
}
/**
* {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or
* {@link PoolableCallableStatement}s. The <code>stmtType</code> field in the key determines whether a
* PoolablePreparedStatement or PoolableCallableStatement is created.
*
* @param key
* the key for the {@link PreparedStatement} to be created
* @see #createKey(String, int, int, StatementType)
*/
@SuppressWarnings("resource")
@Override
public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
if (null == key) {
throw new IllegalArgumentException("Prepared statement key is null or invalid.");
}
if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
final PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate());
@SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pstmtPool, this);
return new DefaultPooledObject<DelegatingPreparedStatement>(pps);
}
final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate());
final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pstmtPool, this);
return new DefaultPooledObject<DelegatingPreparedStatement>(pcs);
}
/**
* Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
*
* @param sql The statement to be normalized.
*
* @return The canonical form of the supplied SQL statement.
*/
protected String normalizeSQL(final String sql) {
return sql.trim();
}
/**
* {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s.
* Invokes {@link PreparedStatement#clearParameters}.
*
* @param key
* ignored
* @param pooledObject
* a wrapped {@link PreparedStatement}
*/
@Override
public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
@SuppressWarnings("resource")
final DelegatingPreparedStatement dps = pooledObject.getObject();
dps.clearParameters();
dps.passivate();
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql) throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(
createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param columnIndexes
* column indexes
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, columnIndexes));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param columnNames
* column names
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, columnNames));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Sets the prepared statement pool.
*
* @param pool
* the prepared statement pool.
*/
public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) {
pstmtPool = pool;
}
@Override
public synchronized String toString() {
if (pstmtPool != null) {
return "PoolingConnection: " + pstmtPool.toString();
}
return "PoolingConnection: null";
}
/**
* {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently always returns true.
*
* @param key
* ignored
* @param pooledObject
* ignored
* @return {@code true}
*/
@Override
public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
return true;
}
}

View File

@@ -0,0 +1,257 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool;
/**
* A simple {@link DataSource} implementation that obtains {@link Connection}s from the specified {@link ObjectPool}.
*
* @param <C>
* The connection type
*
* @since 2.0
*/
public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
private static final Log log = LogFactory.getLog(PoolingDataSource.class);
/** Controls access to the underlying connection */
private boolean accessToUnderlyingConnectionAllowed;
/**
* Constructs a new instance backed by the given connection pool.
*
* @param pool
* the given connection pool.
*/
public PoolingDataSource(final ObjectPool<C> pool) {
Objects.requireNonNull(pool, "Pool must not be null.");
this.pool = pool;
// Verify that pool's factory refers back to it. If not, log a warning and try to fix.
if (this.pool instanceof GenericObjectPool<?>) {
final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) this.pool)
.getFactory();
Objects.requireNonNull(pcf, "PoolableConnectionFactory must not be null.");
if (pcf.getPool() != this.pool) {
log.warn(Utils.getMessage("poolingDataSource.factoryConfig"));
@SuppressWarnings("unchecked") // PCF must have a pool of PCs
final ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) this.pool;
pcf.setPool(p);
}
}
}
/**
* Closes and free all {@link Connection}s from the pool.
*
* @since 2.1
*/
@Override
public void close() throws RuntimeException, SQLException {
try {
pool.close();
} catch (final RuntimeException rte) {
throw new RuntimeException(Utils.getMessage("pool.close.fail"), rte);
} catch (final Exception e) {
throw new SQLException(Utils.getMessage("pool.close.fail"), e);
}
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying {@link Connection} is allowed, false otherwise.
*/
public boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public boolean isWrapperFor(final Class<?> iface) throws SQLException {
return false;
}
@Override
public <T> T unwrap(final Class<T> iface) throws SQLException {
throw new SQLException("PoolingDataSource is not a wrapper.");
}
/* JDBC_4_ANT_KEY_END */
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
// --- DataSource methods -----------------------------------------
/**
* Returns a {@link java.sql.Connection} from my pool, according to the contract specified by
* {@link ObjectPool#borrowObject}.
*/
@Override
public Connection getConnection() throws SQLException {
try {
final C conn = pool.borrowObject();
if (conn == null) {
return null;
}
return new PoolGuardConnectionWrapper<>(conn);
} catch (final SQLException e) {
throw e;
} catch (final NoSuchElementException e) {
throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
} catch (final RuntimeException e) {
throw e;
} catch (final InterruptedException e) {
// Reset the interrupt status so it is visible to callers
Thread.currentThread().interrupt();
throw new SQLException("Cannot get a connection, general error", e);
} catch (final Exception e) {
throw new SQLException("Cannot get a connection, general error", e);
}
}
/**
* Throws {@link UnsupportedOperationException}
*
* @throws UnsupportedOperationException
* always thrown
*/
@Override
public Connection getConnection(final String uname, final String passwd) throws SQLException {
throw new UnsupportedOperationException();
}
/**
* Returns my log writer.
*
* @return my log writer
* @see DataSource#getLogWriter
*/
@Override
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Throws {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException
* As this implementation does not support this feature.
*/
@Override
public int getLoginTimeout() {
throw new UnsupportedOperationException("Login timeout is not supported.");
}
/**
* Throws {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException
* As this implementation does not support this feature.
*/
@Override
public void setLoginTimeout(final int seconds) {
throw new UnsupportedOperationException("Login timeout is not supported.");
}
/**
* Sets my log writer.
*
* @see DataSource#setLogWriter
*/
@Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
/** My log writer. */
private PrintWriter logWriter = null;
private final ObjectPool<C> pool;
protected ObjectPool<C> getPool() {
return pool;
}
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* @since 2.0
*/
private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
PoolGuardConnectionWrapper(final D delegate) {
super(delegate);
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getDelegate()
*/
@Override
public D getDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
@Override
public Connection getInnermostDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
}
@Override
public void close() throws SQLException {
if (getDelegateInternal() != null) {
super.close();
super.setDelegate(null);
}
}
@Override
public boolean isClosed() throws SQLException {
return getDelegateInternal() == null ? true : super.isClosed();
}
}
}

View File

@@ -0,0 +1,263 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
/**
* A {@link Driver} implementation that obtains {@link Connection}s from a registered {@link ObjectPool}.
*
* @since 2.0
*/
public class PoolingDriver implements Driver {
/** Register myself with the {@link DriverManager}. */
static {
try {
DriverManager.registerDriver(new PoolingDriver());
} catch (final Exception e) {
// ignore
}
}
/** The map of registered pools. */
protected static final HashMap<String, ObjectPool<? extends Connection>> pools = new HashMap<>();
/** Controls access to the underlying connection */
private final boolean accessToUnderlyingConnectionAllowed;
/**
* Constructs a new driver with <code>accessToUnderlyingConnectionAllowed</code> enabled.
*/
public PoolingDriver() {
this(true);
}
/**
* For unit testing purposes.
*
* @param accessToUnderlyingConnectionAllowed
* Do {@link DelegatingConnection}s created by this driver permit access to the delegate?
*/
protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
protected boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
}
/**
* Gets the connection pool for the given name.
*
* @param name
* The pool name
* @return The pool
* @throws SQLException
* Thrown when the named pool is not registered.
*/
public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) throws SQLException {
final ObjectPool<? extends Connection> pool = pools.get(name);
if (null == pool) {
throw new SQLException("Pool not registered: " + name);
}
return pool;
}
/**
* Registers a named pool.
*
* @param name
* The pool name.
* @param pool
* The pool.
*/
public synchronized void registerPool(final String name, final ObjectPool<? extends Connection> pool) {
pools.put(name, pool);
}
/**
* Closes a named pool.
*
* @param name
* The pool name.
* @throws SQLException
* Thrown when a problem is caught closing the pool.
*/
public synchronized void closePool(final String name) throws SQLException {
@SuppressWarnings("resource")
final ObjectPool<? extends Connection> pool = pools.get(name);
if (pool != null) {
pools.remove(name);
try {
pool.close();
} catch (final Exception e) {
throw new SQLException("Error closing pool " + name, e);
}
}
}
/**
* Gets the pool names.
*
* @return the pool names.
*/
public synchronized String[] getPoolNames() {
final Set<String> names = pools.keySet();
return names.toArray(new String[names.size()]);
}
@Override
public boolean acceptsURL(final String url) throws SQLException {
return url == null ? false : url.startsWith(URL_PREFIX);
}
@Override
public Connection connect(final String url, final Properties info) throws SQLException {
if (acceptsURL(url)) {
final ObjectPool<? extends Connection> pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
try {
final Connection conn = pool.borrowObject();
if (conn == null) {
return null;
}
return new PoolGuardConnectionWrapper(pool, conn);
} catch (final SQLException e) {
throw e;
} catch (final NoSuchElementException e) {
throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e);
}
}
return null;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
/**
* Invalidates the given connection.
*
* @param conn
* connection to invalidate
* @throws SQLException
* if the connection is not a <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating
* the connection
*/
public void invalidateConnection(final Connection conn) throws SQLException {
if (conn instanceof PoolGuardConnectionWrapper) { // normal case
final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
@SuppressWarnings("unchecked")
final ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
try {
pool.invalidateObject(pgconn.getDelegateInternal());
} catch (final Exception e) {
// Ignore.
}
} else {
throw new SQLException("Invalid connection class");
}
}
@Override
public int getMajorVersion() {
return MAJOR_VERSION;
}
@Override
public int getMinorVersion() {
return MINOR_VERSION;
}
@Override
public boolean jdbcCompliant() {
return true;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {
return new DriverPropertyInfo[0];
}
/** My URL prefix */
public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
// version numbers
protected static final int MAJOR_VERSION = 1;
protected static final int MINOR_VERSION = 0;
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* @since 2.0
*/
private class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> {
private final ObjectPool<? extends Connection> pool;
PoolGuardConnectionWrapper(final ObjectPool<? extends Connection> pool, final Connection delegate) {
super(delegate);
this.pool = pool;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getDelegate()
*/
@Override
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getDelegate();
}
return null;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
@Override
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getInnermostDelegate();
}
return null;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.sql.SQLException;
import java.util.List;
/**
* An SQLException based on a list of Throwable causes.
* <p>
* The first exception in the list is used as this exception's cause and is accessible with the usual
* {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
* </p>
*
* @since 2.7.0
*/
public class SQLExceptionList extends SQLException {
private static final long serialVersionUID = 1L;
private final List<? extends Throwable> causeList;
/**
* Creates a new exception caused by a list of exceptions.
*
* @param causeList a list of cause exceptions.
*/
public SQLExceptionList(List<? extends Throwable> causeList) {
super(String.format("%,d exceptions: %s", Integer.valueOf(causeList == null ? 0 : causeList.size()), causeList),
causeList == null ? null : causeList.get(0));
this.causeList = causeList;
}
/**
* Gets the cause list.
*
* @return The list of causes.
*/
public List<? extends Throwable> getCauseList() {
return causeList;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2;
import org.apache.juli.logging.Log;
import org.apache.tomcat.dbcp.pool2.SwallowedExceptionListener;
/**
* Class for logging swallowed exceptions.
*
* @since 2.0
*/
public class SwallowedExceptionLogger implements SwallowedExceptionListener {
private final Log log;
private final boolean logExpiredConnections;
/**
* Create a SwallowedExceptionLogger with the given logger. By default, expired connection logging is turned on.
*
* @param log
* logger
*/
public SwallowedExceptionLogger(final Log log) {
this(log, true);
}
/**
* Create a SwallowedExceptionLogger with the given logger and expired connection logging property.
*
* @param log
* logger
* @param logExpiredConnections
* false suppresses logging of expired connection events
*/
public SwallowedExceptionLogger(final Log log, final boolean logExpiredConnections) {
this.log = log;
this.logExpiredConnections = logExpiredConnections;
}
@Override
public void onSwallowException(final Exception e) {
if (logExpiredConnections || !(e instanceof LifetimeExceededException)) {
log.warn(Utils.getMessage("swallowedExceptionLogger.onSwallowedException"), e);
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tomcat.dbcp.dbcp2;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Set;
/**
* Utility methods.
*
* @since 2.0
*/
public final class Utils {
private static final ResourceBundle messages = ResourceBundle
.getBundle(Utils.class.getPackage().getName() + ".LocalStrings");
/**
* Whether the security manager is enabled.
*/
public static final boolean IS_SECURITY_ENABLED = System.getSecurityManager() != null;
/** Any SQL_STATE starting with this value is considered a fatal disconnect */
public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
/**
* SQL codes of fatal connection errors.
* <ul>
* <li>57P01 (Admin shutdown)</li>
* <li>57P02 (Crash shutdown)</li>
* <li>57P03 (Cannot connect now)</li>
* <li>01002 (SQL92 disconnect error)</li>
* <li>JZ0C0 (Sybase disconnect error)</li>
* <li>JZ0C1 (Sybase disconnect error)</li>
* </ul>
*/
public static final Set<String> DISCONNECTION_SQL_CODES;
static {
DISCONNECTION_SQL_CODES = new HashSet<>();
DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
}
/**
* Clones the given char[] if not null.
*
* @param value
* may be null.
* @return a cloned char[] or null.
*/
public static char[] clone(final char[] value) {
return value == null ? null : value.clone();
}
/**
* Closes the AutoCloseable (which may be null).
*
* @param autoCloseable
* an AutoCloseable, may be {@code null}
* @since 2.6.0
*/
public static void closeQuietly(final AutoCloseable autoCloseable) {
if (autoCloseable != null) {
try {
autoCloseable.close();
} catch (final Exception e) {
// ignored
}
}
}
/**
* Gets the correct i18n message for the given key.
*
* @param key
* The key to look up an i18n message.
* @return The i18n message.
*/
public static String getMessage(final String key) {
return getMessage(key, (Object[]) null);
}
/**
* Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
*
* @param key
* A message key.
* @param args
* The message arguments.
* @return An i18n message.
*/
public static String getMessage(final String key, final Object... args) {
final String msg = messages.getString(key);
if (args == null || args.length == 0) {
return msg;
}
final MessageFormat mf = new MessageFormat(msg);
return mf.format(args, new StringBuffer(), null).toString();
}
/**
* Converts the given String to a char[].
*
* @param value
* may be null.
* @return a char[] or null.
*/
public static char[] toCharArray(final String value) {
return value != null ? value.toCharArray() : null;
}
/**
* Converts the given char[] to a String.
*
* @param value
* may be null.
* @return a String or null.
*/
public static String toString(final char[] value) {
return value == null ? null : String.valueOf(value);
}
private Utils() {
// not instantiable
}
}

View File

@@ -0,0 +1,308 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.tomcat.dbcp.dbcp2.DelegatingCallableStatement;
import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
/**
* This class is the <code>Connection</code> that will be returned from
* <code>PooledConnectionImpl.getConnection()</code>. Most methods are wrappers around the JDBC 1.x
* <code>Connection</code>. A few exceptions include preparedStatement and close. In accordance with the JDBC
* specification this Connection cannot be used after closed() is called. Any further usage will result in an
* SQLException.
* <p>
* ConnectionImpl extends DelegatingConnection to enable access to the underlying connection.
* </p>
*
* @since 2.0
*/
class ConnectionImpl extends DelegatingConnection<Connection> {
private final boolean accessToUnderlyingConnectionAllowed;
/** The object that instantiated this object */
private final PooledConnectionImpl pooledConnection;
/**
* Creates a <code>ConnectionImpl</code>.
*
* @param pooledConnection
* The PooledConnection that is calling the ctor.
* @param connection
* The JDBC 1.x Connection to wrap.
* @param accessToUnderlyingConnectionAllowed
* if true, then access is allowed to the underlying connection
*/
ConnectionImpl(final PooledConnectionImpl pooledConnection, final Connection connection,
final boolean accessToUnderlyingConnectionAllowed) {
super(connection);
this.pooledConnection = pooledConnection;
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
}
/**
* Marks the Connection as closed, and notifies the pool that the pooled connection is available.
* <p>
* In accordance with the JDBC specification this Connection cannot be used after closed() is called. Any further
* usage will result in an SQLException.
* </p>
*
* @throws SQLException
* The database connection couldn't be closed.
*/
@Override
public void close() throws SQLException {
if (!isClosedInternal()) {
try {
passivate();
} finally {
setClosedInternal(true);
pooledConnection.notifyListeners();
}
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
* specified using JDBC call escape syntax.
* @return a default <code>CallableStatement</code> object containing the pre-compiled SQL statement.
* @exception SQLException
* Thrown if a database access error occurs or this method is called on a closed connection.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql) throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this, pooledConnection.prepareCall(sql));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a <code>CallableStatement</code> object containing the pre-compiled SQL statement that will produce
* <code>ResultSet</code> objects with the given type and concurrency.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type and concurrency.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this,
pooledConnection.prepareCall(sql, resultSetType, resultSetConcurrency));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* one of the following <code>ResultSet</code> constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* one of the following <code>ResultSet</code> constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* one of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a new <code>CallableStatement</code> object, containing the pre-compiled SQL statement, that will
* generate <code>ResultSet</code> objects with the given type, concurrency, and holdability.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type, concurrency, and holdability.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this,
pooledConnection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>PreparedStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* SQL statement to be prepared
* @return the prepared statement
* @throws SQLException
* if this connection is closed or an error occurs in the wrapped connection.
*/
@Override
public PreparedStatement prepareStatement(final String sql) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>PreparedStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @throws SQLException
* if this connection is closed or an error occurs in the wrapped connection.
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this,
pooledConnection.prepareStatement(sql, resultSetType, resultSetConcurrency));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this,
pooledConnection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, autoGeneratedKeys));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, columnIndexes));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, columnNames));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
//
// Methods for accessing the delegate connection
//
/**
* If false, getDelegate() and getInnermostDelegate() will return null.
*
* @return true if access is allowed to the underlying connection
* @see ConnectionImpl
*/
public boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
}
/**
* Get the delegated connection, if allowed.
*
* @return the internal connection, or null if access is not allowed.
* @see #isAccessToUnderlyingConnectionAllowed()
*/
@Override
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return getDelegateInternal();
}
return null;
}
/**
* Get the innermost connection, if allowed.
*
* @return the innermost internal connection, or null if access is not allowed.
* @see #isAccessToUnderlyingConnectionAllowed()
*/
@Override
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getInnermostDelegateInternal();
}
return null;
}
}

View File

@@ -0,0 +1,779 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Hashtable;
import java.util.Properties;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.BaseObjectPoolConfig;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* <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.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource}. 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 jdbc2
* <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="../datasources/package-summary.html">datasources package documentation</a> shows how to use
* <code>DriverAdapterCPDS</code> as a source for <code>Jdbc2PoolDataSource</code> without the use of JNDI.
* </p>
*
* @since 2.0
*/
public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory {
private static final String KEY_USER = "user";
private static final String KEY_PASSWORD = "password";
private static final long serialVersionUID = -4820523787212147844L;
private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, "
+ "further initialization is not allowed.";
static {
// Attempt to prevent deadlocks - see DBCP - 272
DriverManager.getDrivers();
}
/** Description */
private String description;
/** Url name */
private String url;
/** User name */
private String userName;
/** User password */
private char[] userPassword;
/** Driver class name */
private String driver;
/** Login TimeOut in seconds */
private int loginTimeout;
/** Log stream. NOT USED */
private transient PrintWriter logWriter;
// PreparedStatement pool properties
private boolean poolPreparedStatements;
private int maxIdle = 10;
private long timeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
private int numTestsPerEvictionRun = -1;
private int minEvictableIdleTimeMillis = -1;
private int maxPreparedStatements = -1;
/** Whether or not getConnection has been called */
private volatile boolean getConnectionCalled;
/** Connection properties passed to JDBC Driver */
private Properties connectionProperties;
/**
* Controls access to the underlying connection
*/
private boolean accessToUnderlyingConnectionAllowed;
/**
* Default no-arg constructor for Serialization
*/
public DriverAdapterCPDS() {
}
/**
* Throws an IllegalStateException, if a PooledConnection has already been requested.
*/
private void assertInitializationAllowed() throws IllegalStateException {
if (getConnectionCalled) {
throw new IllegalStateException(GET_CONNECTION_CALLED);
}
}
private boolean getBooleanContentString(RefAddr ra) {
return Boolean.valueOf(getStringContent(ra)).booleanValue();
}
/**
* Gets the connection properties passed to the JDBC driver.
*
* @return the JDBC connection properties used when creating connections.
*/
public Properties getConnectionProperties() {
return connectionProperties;
}
/**
* Gets 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, may be null.
* @see #setDescription(String)
*/
public String getDescription() {
return description;
}
/**
* Gets the driver class name.
*
* @return value of driver.
*/
public String getDriver() {
return driver;
}
private int getIntegerStringContent(final RefAddr ra) {
return Integer.parseInt(getStringContent(ra));
}
/**
* Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT
* USED.
*/
@Override
public int getLoginTimeout() {
return loginTimeout;
}
/**
* Gets the log writer for this data source. NOT USED.
*/
@Override
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
* negative for no limit.
*
* @return the value of maxIdle
*/
public int getMaxIdle() {
return this.maxIdle;
}
/**
* Gets the maximum number of prepared statements.
*
* @return maxPrepartedStatements value
*/
public int getMaxPreparedStatements() {
return maxPreparedStatements;
}
/**
* Gets 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
* @return the minimum amount of time a statement may sit idle in the pool.
*/
public int getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
/**
* Gets the number of statements to examine during each run of the idle object evictor thread (if any.)
*
* @see #setNumTestsPerEvictionRun
* @see #setTimeBetweenEvictionRunsMillis
* @return the number of statements to examine during each run of the idle object evictor thread (if any.)
*/
public int getNumTestsPerEvictionRun() {
return numTestsPerEvictionRun;
}
/**
* Implements {@link ObjectFactory} to create an instance of this class
*/
@Override
public Object getObjectInstance(final Object refObj, final Name name, final Context context,
final 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) {
final Reference ref = (Reference) refObj;
if (ref.getClassName().equals(getClass().getName())) {
RefAddr ra = ref.get("description");
if (isNotEmpty(ra)) {
setDescription(getStringContent(ra));
}
ra = ref.get("driver");
if (isNotEmpty(ra)) {
setDriver(getStringContent(ra));
}
ra = ref.get("url");
if (isNotEmpty(ra)) {
setUrl(getStringContent(ra));
}
ra = ref.get(KEY_USER);
if (isNotEmpty(ra)) {
setUser(getStringContent(ra));
}
ra = ref.get(KEY_PASSWORD);
if (isNotEmpty(ra)) {
setPassword(getStringContent(ra));
}
ra = ref.get("poolPreparedStatements");
if (isNotEmpty(ra)) {
setPoolPreparedStatements(getBooleanContentString(ra));
}
ra = ref.get("maxIdle");
if (isNotEmpty(ra)) {
setMaxIdle(getIntegerStringContent(ra));
}
ra = ref.get("timeBetweenEvictionRunsMillis");
if (isNotEmpty(ra)) {
setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra));
}
ra = ref.get("numTestsPerEvictionRun");
if (isNotEmpty(ra)) {
setNumTestsPerEvictionRun(getIntegerStringContent(ra));
}
ra = ref.get("minEvictableIdleTimeMillis");
if (isNotEmpty(ra)) {
setMinEvictableIdleTimeMillis(getIntegerStringContent(ra));
}
ra = ref.get("maxPreparedStatements");
if (isNotEmpty(ra)) {
setMaxPreparedStatements(getIntegerStringContent(ra));
}
ra = ref.get("accessToUnderlyingConnectionAllowed");
if (isNotEmpty(ra)) {
setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra));
}
cpds = this;
}
}
return cpds;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
/**
* Gets the value of password for the default user.
*
* @return value of password.
*/
public String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password for the default user.
*
* @return value of password.
* @since 2.4.0
*/
public char[] getPasswordCharArray() {
return userPassword;
}
/**
* Attempts to establish a database connection using the default user and password.
*/
@Override
public PooledConnection getPooledConnection() throws SQLException {
return getPooledConnection(getUser(), getPassword());
}
/**
* Attempts to establish a database connection.
*
* @param pooledUserName
* name to be used for the connection
* @param pooledUserPassword
* password to be used fur the connection
*/
@Override
public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword)
throws SQLException {
getConnectionCalled = true;
PooledConnectionImpl pooledConnection = null;
// Workaround for buggy WebLogic 5.1 classloader - ignore the exception upon first invocation.
try {
if (connectionProperties != null) {
update(connectionProperties, KEY_USER, pooledUserName);
update(connectionProperties, KEY_PASSWORD, pooledUserPassword);
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), connectionProperties));
} else {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
}
pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
} catch (final ClassCircularityError e) {
if (connectionProperties != null) {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), connectionProperties));
} else {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
}
pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
}
KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null;
if (isPoolPreparedStatements()) {
final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
config.setMaxTotalPerKey(Integer.MAX_VALUE);
config.setBlockWhenExhausted(false);
config.setMaxWaitMillis(0);
config.setMaxIdlePerKey(getMaxIdle());
if (getMaxPreparedStatements() <= 0) {
// since there is no limit, create a prepared statement pool with an eviction thread;
// evictor settings are the same as the connection pool settings.
config.setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis());
config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
config.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
} else {
// since there is a limit, create a prepared statement pool without an eviction thread;
// pool has LRU functionality so when the limit is reached, 15% of the pool is cleared.
// see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method
config.setMaxTotal(getMaxPreparedStatements());
config.setTimeBetweenEvictionRunsMillis(-1);
config.setNumTestsPerEvictionRun(0);
config.setMinEvictableIdleTimeMillis(0);
}
stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config);
pooledConnection.setStatementPool(stmtPool);
}
return pooledConnection;
}
/**
* Implements {@link Referenceable}.
*/
@Override
public Reference getReference() throws NamingException {
// this class implements its own factory
final String factory = getClass().getName();
final 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(KEY_PASSWORD, getPassword()));
ref.add(new StringRefAddr(KEY_USER, getUser()));
ref.add(new StringRefAddr("url", getUrl()));
ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements())));
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())));
ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements())));
return ref;
}
private String getStringContent(RefAddr ra) {
return ra.getContent().toString();
}
/**
* Gets 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.
*
* @return the value of the evictor thread timer
* @see #setTimeBetweenEvictionRunsMillis(long)
*/
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
/**
* Gets the value of url used to locate the database for this datasource.
*
* @return value of url.
*/
public String getUrl() {
return url;
}
/**
* Gets the value of default user (login or user name).
*
* @return value of user.
*/
public String getUser() {
return userName;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
private boolean isNotEmpty(RefAddr ra) {
return ra != null && ra.getContent() != null;
}
/**
* Whether to toggle the pooling of <code>PreparedStatement</code>s
*
* @return value of poolPreparedStatements.
*/
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
/**
* Sets the connection properties passed to the JDBC driver.
* <p>
* If <code>props</code> contains "user" and/or "password" properties, the corresponding instance properties are
* set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()}
* when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when
* {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or
* {@link #setPassword(String)} overwrite the values of these properties if <code>connectionProperties</code> is not
* null.
* </p>
*
* @param props
* Connection properties to use when creating new connections.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setConnectionProperties(final Properties props) {
assertInitializationAllowed();
connectionProperties = props;
if (connectionProperties != null) {
if (connectionProperties.containsKey(KEY_USER)) {
setUser(connectionProperties.getProperty(KEY_USER));
}
if (connectionProperties.containsKey(KEY_PASSWORD)) {
setPassword(connectionProperties.getProperty(KEY_PASSWORD));
}
}
}
/**
* Sets 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(final String v) {
this.description = v;
}
/**
* Sets the driver class name. Setting the driver class name cause the driver to be registered with the
* DriverManager.
*
* @param v
* Value to assign to driver.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
* @throws ClassNotFoundException
* if the class cannot be located
*/
public void setDriver(final String v) throws ClassNotFoundException {
assertInitializationAllowed();
this.driver = v;
// make sure driver is registered
Class.forName(v);
}
/**
* Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. NOT
* USED.
*/
@Override
public void setLoginTimeout(final int seconds) {
loginTimeout = seconds;
}
/**
* Sets the log writer for this data source. NOT USED.
*/
@Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
/**
* Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
* negative for no limit.
*
* @param maxIdle
* The maximum number of statements that can remain idle
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setMaxIdle(final int maxIdle) {
assertInitializationAllowed();
this.maxIdle = maxIdle;
}
/**
* Sets the maximum number of prepared statements.
*
* @param maxPreparedStatements
* the new maximum number of prepared statements
*/
public void setMaxPreparedStatements(final int maxPreparedStatements) {
this.maxPreparedStatements = maxPreparedStatements;
}
/**
* Sets 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). When non-positive, no objects will be evicted from the pool due to idle time alone.
*
* @param minEvictableIdleTimeMillis
* minimum time to set (in ms)
* @see #getMinEvictableIdleTimeMillis()
* @see #setTimeBetweenEvictionRunsMillis(long)
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) {
assertInitializationAllowed();
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
/**
* 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, <code>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</code>
* 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.
* </p>
*
* @param numTestsPerEvictionRun
* number of statements to examine per run
* @see #getNumTestsPerEvictionRun()
* @see #setTimeBetweenEvictionRunsMillis(long)
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
assertInitializationAllowed();
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
}
/**
* Sets the value of password for the default user.
*
* @param userPassword
* Value to assign to password.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPassword(final char[] userPassword) {
assertInitializationAllowed();
this.userPassword = Utils.clone(userPassword);
update(connectionProperties, KEY_PASSWORD, Utils.toString(this.userPassword));
}
/**
* Sets the value of password for the default user.
*
* @param userPassword
* Value to assign to password.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPassword(final String userPassword) {
assertInitializationAllowed();
this.userPassword = Utils.toCharArray(userPassword);
update(connectionProperties, KEY_PASSWORD, userPassword);
}
/**
* Whether to toggle the pooling of <code>PreparedStatement</code>s
*
* @param poolPreparedStatements
* true to pool statements.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPoolPreparedStatements(final boolean poolPreparedStatements) {
assertInitializationAllowed();
this.poolPreparedStatements = poolPreparedStatements;
}
/**
* 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.
*
* @param timeBetweenEvictionRunsMillis
* 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()
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
assertInitializationAllowed();
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
/**
* Sets the value of URL string used to locate the database for this datasource.
*
* @param v
* Value to assign to url.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setUrl(final String v) {
assertInitializationAllowed();
this.url = v;
}
/**
* Sets the value of default user (login or user name).
*
* @param v
* Value to assign to user.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setUser(final String v) {
assertInitializationAllowed();
this.userName = v;
update(connectionProperties, KEY_USER, v);
}
/**
* Does not print the userName and userPassword field nor the 'user' or 'password' in the connectionProperties.
*
* @since 2.6.0
*/
@Override
public synchronized String toString() {
final StringBuilder builder = new StringBuilder(super.toString());
builder.append("[description=");
builder.append(description);
builder.append(", url=");
// TODO What if the connection string contains a 'user' or 'password' query parameter but that connection string is not in a legal URL format?
builder.append(url);
builder.append(", driver=");
builder.append(driver);
builder.append(", loginTimeout=");
builder.append(loginTimeout);
builder.append(", poolPreparedStatements=");
builder.append(poolPreparedStatements);
builder.append(", maxIdle=");
builder.append(maxIdle);
builder.append(", timeBetweenEvictionRunsMillis=");
builder.append(timeBetweenEvictionRunsMillis);
builder.append(", numTestsPerEvictionRun=");
builder.append(numTestsPerEvictionRun);
builder.append(", minEvictableIdleTimeMillis=");
builder.append(minEvictableIdleTimeMillis);
builder.append(", maxPreparedStatements=");
builder.append(maxPreparedStatements);
builder.append(", getConnectionCalled=");
builder.append(getConnectionCalled);
builder.append(", connectionProperties=");
Properties tmpProps = connectionProperties;
final String pwdKey = "password";
if (connectionProperties != null && connectionProperties.contains(pwdKey)) {
tmpProps = (Properties) connectionProperties.clone();
tmpProps.remove(pwdKey);
}
builder.append(tmpProps);
builder.append(", accessToUnderlyingConnectionAllowed=");
builder.append(accessToUnderlyingConnectionAllowed);
builder.append("]");
return builder.toString();
}
private void update(final Properties properties, final String key, final String value) {
if (properties != null && key != null) {
if (value == null) {
properties.remove(key);
} else {
properties.setProperty(key, value);
}
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
/**
* A key uniquely identifying a {@link java.sql.PreparedStatement PreparedStatement}.
*
* @since 2.0
* @deprecated Use {@link PStmtKey}.
*/
@Deprecated
public class PStmtKeyCPDS extends PStmtKey {
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
*/
public PStmtKeyCPDS(final String sql) {
super(sql);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*/
public PStmtKeyCPDS(final String sql, final int autoGeneratedKeys) {
super(sql, null, autoGeneratedKeys);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency) {
super(sql, resultSetType, resultSetConcurrency);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*/
public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
super(sql, null, resultSetType, resultSetConcurrency, resultSetHoldability);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
*/
public PStmtKeyCPDS(final String sql, final int columnIndexes[]) {
super(sql, null, columnIndexes);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
*/
public PStmtKeyCPDS(final String sql, final String columnNames[]) {
super(sql, null, columnNames);
}
}

View File

@@ -0,0 +1,731 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Vector;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;
import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.Jdbc41Bridge;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
import org.apache.tomcat.dbcp.dbcp2.PoolableCallableStatement;
import org.apache.tomcat.dbcp.dbcp2.PoolablePreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.PoolingConnection.StatementType;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* Implementation of PooledConnection that is returned by PooledConnectionDataSource.
*
* @since 2.0
*/
class PooledConnectionImpl
implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
/**
* The JDBC database connection that represents the physical db connection.
*/
private Connection connection;
/**
* A DelegatingConnection used to create a PoolablePreparedStatementStub.
*/
private final DelegatingConnection<?> delegatingConnection;
/**
* The JDBC database logical connection.
*/
private Connection logicalConnection;
/**
* ConnectionEventListeners.
*/
private final Vector<ConnectionEventListener> eventListeners;
/**
* StatementEventListeners.
*/
private final Vector<StatementEventListener> statementEventListeners = new Vector<>();
/**
* Flag set to true, once {@link #close()} is called.
*/
private boolean closed;
/** My pool of {@link PreparedStatement}s. */
private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
/**
* Controls access to the underlying connection.
*/
private boolean accessToUnderlyingConnectionAllowed;
/**
* Wraps the real connection.
*
* @param connection
* the connection to be wrapped.
*/
PooledConnectionImpl(final Connection connection) {
this.connection = connection;
if (connection instanceof DelegatingConnection) {
this.delegatingConnection = (DelegatingConnection<?>) connection;
} else {
this.delegatingConnection = new DelegatingConnection<>(connection);
}
eventListeners = new Vector<>();
closed = false;
}
/**
* My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
*
* @param key
* Ignored.
* @param pooledObject
* Ignored.
*/
@Override
public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().activate();
}
/**
* {@inheritDoc}
*/
@Override
public void addConnectionEventListener(final ConnectionEventListener listener) {
if (!eventListeners.contains(listener)) {
eventListeners.add(listener);
}
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public void addStatementEventListener(final StatementEventListener listener) {
if (!statementEventListeners.contains(listener)) {
statementEventListeners.add(listener);
}
}
/* JDBC_4_ANT_KEY_END */
/**
* Throws an SQLException, if isClosed is true
*/
private void assertOpen() throws SQLException {
if (closed) {
throw new SQLException(CLOSED);
}
}
/**
* Closes the physical connection and marks this <code>PooledConnection</code> so that it may not be used to
* generate any more logical <code>Connection</code>s.
*
* @throws SQLException
* Thrown when an error occurs or the connection is already closed.
*/
@Override
public void close() throws SQLException {
assertOpen();
closed = true;
try {
if (pStmtPool != null) {
try {
pStmtPool.close();
} finally {
pStmtPool = null;
}
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection (return to pool failed)", e);
} finally {
try {
connection.close();
} finally {
connection = null;
}
}
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @return a {@link PStmtKey} for the given arguments.
*/
protected PStmtKey createKey(final String sql) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int columnIndexes[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, resultSetHoldability);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
* @since 2.4.0
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, resultSetHoldability, statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
* @since 2.4.0
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final String columnNames[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
}
/**
* My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
*
* @param key
* ignored
* @param pooledObject
* the wrapped {@link PreparedStatement} to be destroyed.
*/
@Override
public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().getInnermostDelegate().close();
}
/**
* Closes the physical connection and checks that the logical connection was closed as well.
*/
@Override
protected void finalize() throws Throwable {
// Closing the Connection ensures that if anyone tries to use it,
// an error will occur.
try {
connection.close();
} catch (final Exception ignored) {
// ignore
}
// make sure the last connection is marked as closed
if (logicalConnection != null && !logicalConnection.isClosed()) {
throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
}
}
private String getCatalogOrNull() {
try {
return connection == null ? null : connection.getCatalog();
} catch (final SQLException e) {
return null;
}
}
private String getSchemaOrNull() {
try {
return connection == null ? null : Jdbc41Bridge.getSchema(connection);
} catch (final SQLException e) {
return null;
}
}
/**
* Returns a JDBC connection.
*
* @return The database connection.
* @throws SQLException
* if the connection is not open or the previous logical connection is still open
*/
@Override
public Connection getConnection() throws SQLException {
assertOpen();
// make sure the last connection is marked as closed
if (logicalConnection != null && !logicalConnection.isClosed()) {
// should notify pool of error so the pooled connection can
// be removed !FIXME!
throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
}
// the spec requires that this return a new Connection instance.
logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
return logicalConnection;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
/**
* My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
*
* @param key
* The key for the {@link PreparedStatement} to be created.
*/
@SuppressWarnings("resource")
@Override
public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
if (null == key) {
throw new IllegalArgumentException("Prepared statement key is null or invalid.");
}
if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
@SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
delegatingConnection);
return new DefaultPooledObject<DelegatingPreparedStatement>(pps);
}
final CallableStatement statement = (CallableStatement) key.createStatement(connection);
@SuppressWarnings("unchecked")
final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
(DelegatingConnection<Connection>) delegatingConnection);
return new DefaultPooledObject<DelegatingPreparedStatement>(pcs);
}
/**
* Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
* @param sql
* The SQL statement.
* @return the normalized SQL statement.
*/
protected String normalizeSQL(final String sql) {
return sql.trim();
}
/**
* Sends a connectionClosed event.
*/
void notifyListeners() {
final ConnectionEvent event = new ConnectionEvent(this);
final Object[] listeners = eventListeners.toArray();
for (final Object listener : listeners) {
((ConnectionEventListener) listener).connectionClosed(event);
}
}
/**
* My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently invokes
* {@link PreparedStatement#clearParameters}.
*
* @param key
* ignored
* @param pooledObject
* a wrapped {@link PreparedStatement}
*/
@Override
public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
@SuppressWarnings("resource")
final DelegatingPreparedStatement dps = pooledObject.getObject();
dps.clearParameters();
dps.passivate();
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
* specified using JDBC call escape syntax.
* @return a default <code>CallableStatement</code> object containing the pre-compiled SQL statement.
* @exception SQLException
* Thrown if a database access error occurs or this method is called on a closed connection.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql) throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql);
}
try {
return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a <code>CallableStatement</code> object containing the pre-compiled SQL statement that will produce
* <code>ResultSet</code> objects with the given type and concurrency.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type and concurrency.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
}
try {
return (CallableStatement) pStmtPool.borrowObject(
createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* one of the following <code>ResultSet</code> constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* one of the following <code>ResultSet</code> constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* one of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a new <code>CallableStatement</code> object, containing the pre-compiled SQL statement, that will
* generate <code>ResultSet</code> objects with the given type, concurrency, and holdability.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type, concurrency, and holdability.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
try {
return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* the SQL statement.
* @return a {@link PoolablePreparedStatement}
*/
PreparedStatement prepareStatement(final String sql) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql);
}
try {
return pStmtPool.borrowObject(createKey(sql));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* an SQL statement that may contain one or more '?' IN parameter placeholders.
* @param autoGeneratedKeys
* a flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @return a {@link PoolablePreparedStatement}
* @see Connection#prepareStatement(String, int)
*/
PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, autoGeneratedKeys);
}
try {
return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, columnIndexes);
}
try {
return pStmtPool.borrowObject(createKey(sql, columnIndexes));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain one or
* more '?' IN parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*
* @return a {@link PoolablePreparedStatement}.
* @see Connection#prepareStatement(String, int, int)
*/
PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
try {
return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
try {
return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, columnNames);
}
try {
return pStmtPool.borrowObject(createKey(sql, columnNames));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeConnectionEventListener(final ConnectionEventListener listener) {
eventListeners.remove(listener);
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public void removeStatementEventListener(final StatementEventListener listener) {
statementEventListeners.remove(listener);
}
/* JDBC_4_ANT_KEY_END */
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false.)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
pStmtPool = statementPool;
}
/**
* My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
*
* @param key
* Ignored.
* @param pooledObject
* Ignored.
* @return {@code true}
*/
@Override
public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
return true;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* This package contains one public class which is a
* <code>ConnectionPoolDataSource</code> (CPDS) implementation that can be used to
* adapt older <code>Driver</code> based JDBC implementations. Below is an
* example of setting up the CPDS to be available via JNDI in the
* catalina servlet container.
* </p>
* <p>In server.xml, the following would be added to the &lt;Context&gt; for your
* webapp:
* </p>
*
* <pre>
* &lt;Resource name="jdbc/bookstoreCPDS" auth="Container"
* type="org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS"/&gt;
* &lt;ResourceParams name="jdbc/bookstoreCPDS"&gt;
* &lt;parameter&gt;
* &lt;name&gt;factory&lt;/name&gt;
* &lt;value&gt;org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;&lt;name&gt;user&lt;/name&gt;&lt;value&gt;root&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;&lt;name&gt;password&lt;/name&gt;&lt;value&gt;&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;driver&lt;/name&gt;
* &lt;value&gt;org.gjt.mm.mysql.Driver&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;url&lt;/name&gt;
* &lt;value&gt;jdbc:mysql://localhost:3306/bookstore&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;/ResourceParams&gt;
* </pre>
*
* <p>
* In web.xml. Note that elements must be given in the order of the dtd
* described in the servlet specification:
* </p>
*
* <pre>
* &lt;resource-ref&gt;
* &lt;description&gt;
* Resource reference to a factory for java.sql.Connection
* instances that may be used for talking to a particular
* database that is configured in the server.xml file.
* &lt;/description&gt;
* &lt;res-ref-name&gt;
* jdbc/bookstoreCPDS
* &lt;/res-ref-name&gt;
* &lt;res-type&gt;
* org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS
* &lt;/res-type&gt;
* &lt;res-auth&gt;
* Container
* &lt;/res-auth&gt;
* &lt;/resource-ref&gt;
* </pre>
*
* <p>
* Catalina deploys all objects configured similarly to above within the
* <strong>java:comp/env</strong> namespace.
* </p>
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;

View File

@@ -0,0 +1,426 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.PooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link PooledObjectFactory} that creates
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnection PoolableConnection}s.
*
* @since 2.0
*/
class CPDSConnectionFactory
implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager {
private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection.";
private final ConnectionPoolDataSource cpds;
private final String validationQuery;
private final int validationQueryTimeoutSeconds;
private final boolean rollbackAfterValidation;
private ObjectPool<PooledConnectionAndInfo> pool;
private final String userName;
private char[] userPassword;
private long maxConnLifetimeMillis = -1;
/**
* Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
*/
private final Set<PooledConnection> validatingSet = Collections
.newSetFromMap(new ConcurrentHashMap<PooledConnection, Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnection's
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* Timeout in seconds before validation fails
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
* @param userName
* The user name to use to create connections
* @param userPassword
* The password to use to create connections
* @since 2.4.0
*/
public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
final char[] userPassword) {
this.cpds = cpds;
this.validationQuery = validationQuery;
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
this.userName = userName;
this.userPassword = userPassword;
this.rollbackAfterValidation = rollbackAfterValidation;
}
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnection's
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* Timeout in seconds before validation fails
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
* @param userName
* The user name to use to create connections
* @param userPassword
* The password to use to create connections
*/
public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
final String userPassword) {
this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName,
Utils.toCharArray(userPassword));
}
/**
* (Testing API) Gets the value of password for the default user.
*
* @return value of password.
*/
char[] getPasswordCharArray() {
return userPassword;
}
/**
* Returns the object pool used to pool connections created by this factory.
*
* @return ObjectPool managing pooled connections
*/
public ObjectPool<PooledConnectionAndInfo> getPool() {
return pool;
}
/**
*
* @param pool
* the {@link ObjectPool} in which to pool those {@link Connection}s
*/
public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) {
this.pool = pool;
}
@Override
public synchronized PooledObject<PooledConnectionAndInfo> makeObject() {
PooledConnectionAndInfo pci;
try {
PooledConnection pc = null;
if (userName == null) {
pc = cpds.getPooledConnection();
} else {
pc = cpds.getPooledConnection(userName, Utils.toString(userPassword));
}
if (pc == null) {
throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
}
// should we add this object as a listener or the pool.
// consider the validateObject method in decision
pc.addConnectionEventListener(this);
pci = new PooledConnectionAndInfo(pc, userName, userPassword);
pcMap.put(pc, pci);
} catch (final SQLException e) {
throw new RuntimeException(e.getMessage());
}
return new DefaultPooledObject<>(pci);
}
/**
* Closes the PooledConnection and stops listening for events from it.
*/
@Override
public void destroyObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
doDestroyObject(p.getObject());
}
private void doDestroyObject(final PooledConnectionAndInfo pci) throws Exception {
final PooledConnection pc = pci.getPooledConnection();
pc.removeConnectionEventListener(this);
pcMap.remove(pc);
pc.close();
}
@Override
public boolean validateObject(final PooledObject<PooledConnectionAndInfo> p) {
try {
validateLifetime(p);
} catch (final Exception e) {
return false;
}
boolean valid = false;
final PooledConnection pconn = p.getObject().getPooledConnection();
Connection conn = null;
validatingSet.add(pconn);
if (null == validationQuery) {
int timeoutSeconds = validationQueryTimeoutSeconds;
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
try {
conn = pconn.getConnection();
valid = conn.isValid(timeoutSeconds);
} catch (final SQLException e) {
valid = false;
} finally {
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
} else {
Statement stmt = null;
ResultSet rset = null;
// logical Connection from the PooledConnection must be closed
// before another one can be requested and closing it will
// generate an event. Keep track so we know not to return
// the PooledConnection
validatingSet.add(pconn);
try {
conn = pconn.getConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(validationQuery);
if (rset.next()) {
valid = true;
} else {
valid = false;
}
if (rollbackAfterValidation) {
conn.rollback();
}
} catch (final Exception e) {
valid = false;
} finally {
Utils.closeQuietly(rset);
Utils.closeQuietly(stmt);
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
}
return valid;
}
@Override
public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
@Override
public void activateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
// ***********************************************************************
// java.sql.ConnectionEventListener implementation
// ***********************************************************************
/**
* This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
* user calls the close() method of this connection object. What we need to do here is to release this
* PooledConnection from our pool...
*/
@Override
public void connectionClosed(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
// if this event occurred because we were validating, ignore it
// otherwise return the connection to the pool.
if (!validatingSet.contains(pc)) {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.returnObject(pci);
} catch (final Exception e) {
System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
pc.removeConnectionEventListener(this);
try {
doDestroyObject(pci);
} catch (final Exception e2) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e2.printStackTrace();
}
}
}
}
/**
* If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
*/
@Override
public void connectionErrorOccurred(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
if (null != event.getSQLException()) {
System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
}
pc.removeConnectionEventListener(this);
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(pci);
} catch (final Exception e) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e.printStackTrace();
}
}
// ***********************************************************************
// PooledConnectionManager implementation
// ***********************************************************************
/**
* Invalidates the PooledConnection in the pool. The CPDSConnectionFactory closes the connection and pool counters
* are updated appropriately. Also closes the pool. This ensures that all idle connections are closed and
* connections that are checked out are closed on return.
*/
@Override
public void invalidate(final PooledConnection pc) throws SQLException {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(pci); // Destroy instance and update pool counters
pool.close(); // Clear any other instances in this pool and kill others as they come back
} catch (final Exception ex) {
throw new SQLException("Error invalidating connection", ex);
}
}
/**
* Sets the database password used when creating new connections.
*
* @param userPassword
* new password
*/
public synchronized void setPassword(final char[] userPassword) {
this.userPassword = Utils.clone(userPassword);
}
/**
* Sets the database password used when creating new connections.
*
* @param userPassword
* new password
*/
@Override
public synchronized void setPassword(final String userPassword) {
this.userPassword = Utils.toCharArray(userPassword);
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation.
*
* @param maxConnLifetimeMillis
* A value of zero or less indicates an infinite lifetime. The default value is -1.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* Verifies that the user name matches the user whose connections are being managed by this factory and closes the
* pool if this is the case; otherwise does nothing.
*/
@Override
public void closePool(final String userName) throws SQLException {
synchronized (this) {
if (userName == null || !userName.equals(this.userName)) {
return;
}
}
try {
pool.close();
} catch (final Exception ex) {
throw new SQLException("Error closing connection pool", ex);
}
}
private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", Long.valueOf(lifetime),
Long.valueOf(maxConnLifetimeMillis)));
}
}
}
/**
* @since 2.6.0
*/
@Override
public synchronized String toString() {
final StringBuilder builder = new StringBuilder(super.toString());
builder.append("[cpds=");
builder.append(cpds);
builder.append(", validationQuery=");
builder.append(validationQuery);
builder.append(", validationQueryTimeoutSeconds=");
builder.append(validationQueryTimeoutSeconds);
builder.append(", rollbackAfterValidation=");
builder.append(rollbackAfterValidation);
builder.append(", pool=");
builder.append(pool);
builder.append(", maxConnLifetimeMillis=");
builder.append(maxConnLifetimeMillis);
builder.append(", validatingSet=");
builder.append(validatingSet);
builder.append(", pcMap=");
builder.append(pcMap);
builder.append("]");
return builder.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,340 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.tomcat.dbcp.dbcp2.ListException;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s or <code>PerUserPoolDataSource</code>s
*
* @since 2.0
*/
abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
private static final Map<String, InstanceKeyDataSource> instanceMap = new ConcurrentHashMap<>();
static synchronized String registerNewInstance(final InstanceKeyDataSource ds) {
int max = 0;
final Iterator<String> iterator = instanceMap.keySet().iterator();
while (iterator.hasNext()) {
final String s = iterator.next();
if (s != null) {
try {
max = Math.max(max, Integer.parseInt(s));
} catch (final NumberFormatException e) {
// no sweat, ignore those keys
}
}
}
final String instanceKey = String.valueOf(max + 1);
// Put a placeholder here for now, so other instances will not
// take our key. We will replace with a pool when ready.
instanceMap.put(instanceKey, ds);
return instanceKey;
}
static void removeInstance(final String key) {
if (key != null) {
instanceMap.remove(key);
}
}
/**
* Closes all pools associated with this class.
*
* @throws Exception
* a {@link ListException} containing all exceptions thrown by {@link InstanceKeyDataSource#close()}
* @see InstanceKeyDataSource#close()
* @see ListException
* @since 2.4.0 throws a {@link ListException} instead of, in 2.3.0 and before, the first exception thrown by
* {@link InstanceKeyDataSource#close()}.
*/
public static void closeAll() throws Exception {
// Get iterator to loop over all instances of this data source.
final List<Throwable> exceptionList = new ArrayList<>(instanceMap.size());
final Iterator<Entry<String, InstanceKeyDataSource>> instanceIterator = instanceMap.entrySet().iterator();
while (instanceIterator.hasNext()) {
// Bullet-proof to avoid anything else but problems from InstanceKeyDataSource#close().
final Entry<String, InstanceKeyDataSource> next = instanceIterator.next();
if (next != null) {
@SuppressWarnings("resource")
final InstanceKeyDataSource value = next.getValue();
if (value != null) {
try {
value.close();
} catch (final Exception e) {
exceptionList.add(e);
}
}
}
}
instanceMap.clear();
if (!exceptionList.isEmpty()) {
throw new ListException("Could not close all InstanceKeyDataSource instances.", exceptionList);
}
}
/**
* Implements ObjectFactory to create an instance of SharedPoolDataSource or PerUserPoolDataSource
*/
@Override
public Object getObjectInstance(final Object refObj, final Name name, final Context context,
final Hashtable<?, ?> env) throws IOException, ClassNotFoundException {
// The spec says to return null if we can't create an instance
// of the reference
Object obj = null;
if (refObj instanceof Reference) {
final Reference ref = (Reference) refObj;
if (isCorrectClass(ref.getClassName())) {
final RefAddr refAddr = ref.get("instanceKey");
if (refAddr != null && refAddr.getContent() != null) {
// object was bound to JNDI via Referenceable API.
obj = instanceMap.get(refAddr.getContent());
} else {
// Tomcat JNDI creates a Reference out of server.xml
// <ResourceParam> configuration and passes it to an
// instance of the factory given in server.xml.
String key = null;
if (name != null) {
key = name.toString();
obj = instanceMap.get(key);
}
if (obj == null) {
final InstanceKeyDataSource ds = getNewInstance(ref);
setCommonProperties(ref, ds);
obj = ds;
if (key != null) {
instanceMap.put(key, ds);
}
}
}
}
}
return obj;
}
private void setCommonProperties(final Reference ref, final InstanceKeyDataSource ikds)
throws IOException, ClassNotFoundException {
RefAddr refAddr = ref.get("dataSourceName");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDataSourceName(refAddr.getContent().toString());
}
refAddr = ref.get("description");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDescription(refAddr.getContent().toString());
}
refAddr = ref.get("jndiEnvironment");
if (refAddr != null && refAddr.getContent() != null) {
final byte[] serialized = (byte[]) refAddr.getContent();
ikds.setJndiEnvironment((Properties) deserialize(serialized));
}
refAddr = ref.get("loginTimeout");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setLoginTimeout(Integer.parseInt(refAddr.getContent().toString()));
}
// Pool properties
refAddr = ref.get("blockWhenExhausted");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultBlockWhenExhausted(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("evictionPolicyClassName");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultEvictionPolicyClassName(refAddr.getContent().toString());
}
// Pool properties
refAddr = ref.get("lifo");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultLifo(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("maxIdlePerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxIdle(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("maxTotalPerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxTotal(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("maxWaitMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxWaitMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("minEvictableIdleTimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMinEvictableIdleTimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("minIdlePerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMinIdle(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("numTestsPerEvictionRun");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultNumTestsPerEvictionRun(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("softMinEvictableIdleTimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultSoftMinEvictableIdleTimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("testOnCreate");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnCreate(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testOnBorrow");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnBorrow(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testOnReturn");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnReturn(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testWhileIdle");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestWhileIdle(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("timeBetweenEvictionRunsMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTimeBetweenEvictionRunsMillis(Long.parseLong(refAddr.getContent().toString()));
}
// Connection factory properties
refAddr = ref.get("validationQuery");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setValidationQuery(refAddr.getContent().toString());
}
refAddr = ref.get("validationQueryTimeout");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setValidationQueryTimeout(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("rollbackAfterValidation");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setRollbackAfterValidation(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("maxConnLifetimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setMaxConnLifetimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
// Connection properties
refAddr = ref.get("defaultAutoCommit");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultAutoCommit(Boolean.valueOf(refAddr.getContent().toString()));
}
refAddr = ref.get("defaultTransactionIsolation");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTransactionIsolation(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("defaultReadOnly");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultReadOnly(Boolean.valueOf(refAddr.getContent().toString()));
}
}
/**
* @param className
* The class name to test.
*
* @return true if and only if className is the value returned from getClass().getName().toString()
*/
protected abstract boolean isCorrectClass(String className);
/**
* Creates an instance of the subclass and sets any properties contained in the Reference.
*
* @param ref
* The properties to be set on the created DataSource
*
* @return A configured DataSource of the appropriate type.
*
* @throws ClassNotFoundException
* If a class cannot be found during the deserialization of a configuration parameter.
* @throws IOException
* If an I/O error occurs during the deserialization of a configuration parameter.
*/
protected abstract InstanceKeyDataSource getNewInstance(Reference ref) throws IOException, ClassNotFoundException;
/**
* Deserializes the provided byte array to create an object.
*
* @param data
* Data to deserialize to create the configuration parameter.
*
* @return The Object created by deserializing the data.
*
* @throws ClassNotFoundException
* If a class cannot be found during the deserialization of a configuration parameter.
* @throws IOException
* If an I/O error occurs during the deserialization of a configuration parameter.
*/
protected static final Object deserialize(final byte[] data) throws IOException, ClassNotFoundException {
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new ByteArrayInputStream(data));
return in.readObject();
} finally {
if (in != null) {
try {
in.close();
} catch (final IOException ex) {
// ignore
}
}
}
}
}

View File

@@ -0,0 +1,351 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link KeyedPooledObjectFactory} that creates {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnection
* PoolableConnection}s.
*
* @since 2.0
*/
class KeyedCPDSConnectionFactory implements KeyedPooledObjectFactory<UserPassKey, PooledConnectionAndInfo>,
ConnectionEventListener, PooledConnectionManager {
private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but "
+ "I have no record of the underlying PooledConnection.";
private final ConnectionPoolDataSource cpds;
private final String validationQuery;
private final int validationQueryTimeoutSeconds;
private final boolean rollbackAfterValidation;
private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
private long maxConnLifetimeMillis = -1;
/**
* Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
*/
private final Set<PooledConnection> validatingSet = Collections
.newSetFromMap(new ConcurrentHashMap<PooledConnection, Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
/**
* Create a new {@code KeyedPoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnections
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* The time, in seconds, to allow for the validation query to complete
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
*/
public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
this.cpds = cpds;
this.validationQuery = validationQuery;
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
this.rollbackAfterValidation = rollbackAfterValidation;
}
public void setPool(final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool) {
this.pool = pool;
}
/**
* Returns the keyed object pool used to pool connections created by this factory.
*
* @return KeyedObjectPool managing pooled connections
*/
public KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> getPool() {
return pool;
}
/**
* Creates a new {@link PooledConnectionAndInfo} from the given {@link UserPassKey}.
*
* @param upkey
* {@link UserPassKey} containing user credentials
* @throws SQLException
* if the connection could not be created.
* @see org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory#makeObject(java.lang.Object)
*/
@Override
public synchronized PooledObject<PooledConnectionAndInfo> makeObject(final UserPassKey upkey) throws Exception {
PooledConnectionAndInfo pci = null;
PooledConnection pc = null;
final String userName = upkey.getUsername();
final String password = upkey.getPassword();
if (userName == null) {
pc = cpds.getPooledConnection();
} else {
pc = cpds.getPooledConnection(userName, password);
}
if (pc == null) {
throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
}
// should we add this object as a listener or the pool.
// consider the validateObject method in decision
pc.addConnectionEventListener(this);
pci = new PooledConnectionAndInfo(pc, userName, upkey.getPasswordCharArray());
pcMap.put(pc, pci);
return new DefaultPooledObject<>(pci);
}
/**
* Closes the PooledConnection and stops listening for events from it.
*/
@Override
public void destroyObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
final PooledConnection pc = p.getObject().getPooledConnection();
pc.removeConnectionEventListener(this);
pcMap.remove(pc);
pc.close();
}
/**
* Validates a pooled connection.
*
* @param key
* ignored
* @param pooledObject
* wrapped {@link PooledConnectionAndInfo} containing the connection to validate
* @return true if validation succeeds
*/
@Override
public boolean validateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> pooledObject) {
try {
validateLifetime(pooledObject);
} catch (final Exception e) {
return false;
}
boolean valid = false;
final PooledConnection pconn = pooledObject.getObject().getPooledConnection();
Connection conn = null;
validatingSet.add(pconn);
if (null == validationQuery) {
int timeoutSeconds = validationQueryTimeoutSeconds;
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
try {
conn = pconn.getConnection();
valid = conn.isValid(timeoutSeconds);
} catch (final SQLException e) {
valid = false;
} finally {
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
} else {
Statement stmt = null;
ResultSet rset = null;
// logical Connection from the PooledConnection must be closed
// before another one can be requested and closing it will
// generate an event. Keep track so we know not to return
// the PooledConnection
validatingSet.add(pconn);
try {
conn = pconn.getConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(validationQuery);
if (rset.next()) {
valid = true;
} else {
valid = false;
}
if (rollbackAfterValidation) {
conn.rollback();
}
} catch (final Exception e) {
valid = false;
} finally {
Utils.closeQuietly(rset);
Utils.closeQuietly(stmt);
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
}
return valid;
}
@Override
public void passivateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
@Override
public void activateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
// ***********************************************************************
// java.sql.ConnectionEventListener implementation
// ***********************************************************************
/**
* This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
* user calls the close() method of this connection object. What we need to do here is to release this
* PooledConnection from our pool...
*/
@Override
public void connectionClosed(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
// if this event occurred because we were validating, or if this
// connection has been marked for removal, ignore it
// otherwise return the connection to the pool.
if (!validatingSet.contains(pc)) {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.returnObject(pci.getUserPassKey(), pci);
} catch (final Exception e) {
System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
pc.removeConnectionEventListener(this);
try {
pool.invalidateObject(pci.getUserPassKey(), pci);
} catch (final Exception e3) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e3.printStackTrace();
}
}
}
}
/**
* If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
*/
@Override
public void connectionErrorOccurred(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
if (null != event.getSQLException()) {
System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
}
pc.removeConnectionEventListener(this);
final PooledConnectionAndInfo info = pcMap.get(pc);
if (info == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(info.getUserPassKey(), info);
} catch (final Exception e) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + info);
e.printStackTrace();
}
}
// ***********************************************************************
// PooledConnectionManager implementation
// ***********************************************************************
/**
* Invalidates the PooledConnection in the pool. The KeyedCPDSConnectionFactory closes the connection and pool
* counters are updated appropriately. Also clears any idle instances associated with the user name that was used to
* create the PooledConnection. Connections associated with this user are not affected and they will not be
* automatically closed on return to the pool.
*/
@Override
public void invalidate(final PooledConnection pc) throws SQLException {
final PooledConnectionAndInfo info = pcMap.get(pc);
if (info == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
final UserPassKey key = info.getUserPassKey();
try {
pool.invalidateObject(key, info); // Destroy and update pool counters
pool.clear(key); // Remove any idle instances with this key
} catch (final Exception ex) {
throw new SQLException("Error invalidating connection", ex);
}
}
/**
* Does nothing. This factory does not cache user credentials.
*/
@Override
public void setPassword(final String password) {
// Does nothing. This factory does not cache user credentials.
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation.
*
* @param maxConnLifetimeMillis
* A value of zero or less indicates an infinite lifetime. The default value is -1.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* This implementation does not fully close the KeyedObjectPool, as this would affect all users. Instead, it clears
* the pool associated with the given user. This method is not currently used.
*/
@Override
public void closePool(final String userName) throws SQLException {
try {
pool.clear(new UserPassKey(userName));
} catch (final Exception ex) {
throw new SQLException("Error closing connection pool", ex);
}
}
private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", Long.valueOf(lifetime),
Long.valueOf(maxConnLifetimeMillis)));
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.io.IOException;
import java.util.Map;
import javax.naming.RefAddr;
import javax.naming.Reference;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
*
* @since 2.0
*/
public class PerUserPoolDataSourceFactory extends InstanceKeyDataSourceFactory {
private static final String PER_USER_POOL_CLASSNAME = PerUserPoolDataSource.class.getName();
@Override
protected boolean isCorrectClass(final String className) {
return PER_USER_POOL_CLASSNAME.equals(className);
}
@SuppressWarnings("unchecked") // Avoid warnings on deserialization
@Override
protected InstanceKeyDataSource getNewInstance(final Reference ref) throws IOException, ClassNotFoundException {
final PerUserPoolDataSource pupds = new PerUserPoolDataSource();
RefAddr ra = ref.get("defaultMaxTotal");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxTotal(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("defaultMaxIdle");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxIdle(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("defaultMaxWaitMillis");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxWaitMillis(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("perUserDefaultAutoCommit");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultAutoCommit((Map<String, Boolean>) deserialize(serialized));
}
ra = ref.get("perUserDefaultTransactionIsolation");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultTransactionIsolation((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxTotal");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxTotal((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxIdle");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxIdle((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxWaitMillis");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxWaitMillis((Map<String, Long>) deserialize(serialized));
}
ra = ref.get("perUserDefaultReadOnly");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultReadOnly((Map<String, Boolean>) deserialize(serialized));
}
return pupds;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.io.Serializable;
/**
* @since 2.0
*/
class PoolKey implements Serializable {
private static final long serialVersionUID = 2252771047542484533L;
private final String dataSourceName;
private final String userName;
PoolKey(final String dataSourceName, final String userName) {
this.dataSourceName = dataSourceName;
this.userName = userName;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PoolKey other = (PoolKey) obj;
if (dataSourceName == null) {
if (other.dataSourceName != null) {
return false;
}
} else if (!dataSourceName.equals(other.dataSourceName)) {
return false;
}
if (userName == null) {
if (other.userName != null) {
return false;
}
} else if (!userName.equals(other.userName)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dataSourceName == null) ? 0 : dataSourceName.hashCode());
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer(50);
sb.append("PoolKey(");
sb.append(userName).append(", ").append(dataSourceName);
sb.append(')');
return sb.toString();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
/**
* Immutable poolable object holding a PooledConnection along with the user name and password used to create the
* connection.
*
* @since 2.0
*/
final class PooledConnectionAndInfo {
private final PooledConnection pooledConnection;
private final char[] userPassword;
private final String userName;
private final UserPassKey upKey;
/**
* @since 2.4.0
*/
PooledConnectionAndInfo(final PooledConnection pc, final String userName, final char[] userPassword) {
this.pooledConnection = pc;
this.userName = userName;
this.userPassword = userPassword;
this.upKey = new UserPassKey(userName, userPassword);
}
/**
* @deprecated Since 2.4.0
*/
@Deprecated
PooledConnectionAndInfo(final PooledConnection pc, final String userName, final String userPassword) {
this(pc, userName, Utils.toCharArray(userPassword));
}
PooledConnection getPooledConnection() {
return pooledConnection;
}
UserPassKey getUserPassKey() {
return upKey;
}
/**
* Gets the value of password.
*
* @return value of password.
*/
String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password.
*
* @return value of password.
* @since 2.4.0
*/
char[] getPasswordCharArray() {
return userPassword;
}
/**
* Gets the value of userName.
*
* @return value of userName.
*/
String getUsername() {
return userName;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.sql.SQLException;
import javax.sql.PooledConnection;
/**
* Methods to manage PoolableConnections and the connection pools that source them.
*
* @since 2.0
*/
interface PooledConnectionManager {
/**
* Closes the PooledConnection and remove it from the connection pool to which it belongs, adjusting pool counters.
*
* @param pc
* PooledConnection to be invalidated
* @throws SQLException
* if an SQL error occurs closing the connection
*/
void invalidate(PooledConnection pc) throws SQLException;
// /**
// * Sets the database password used when creating connections.
// *
// * @param password password used when authenticating to the database
// * @since 3.0.0
// */
// void setPassword(char[] password);
/**
* Sets the database password used when creating connections.
*
* @param password
* password used when authenticating to the database
*/
void setPassword(String password);
/**
* Closes the connection pool associated with the given user.
*
* @param userName
* user name
* @throws SQLException
* if an error occurs closing idle connections in the pool
*/
void closePool(String userName) throws SQLException;
}

View File

@@ -0,0 +1,245 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* <p>
* A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration
* options, most of which are defined in the parent class. All users (based on user name) share a single maximum number
* of Connections in this data source.
* </p>
*
* <p>
* User passwords can be changed without re-initializing the data source. When a
* <code>getConnection(user name, password)</code> request is processed with a password that is different from those
* used to create connections in the pool associated with <code>user name</code>, an attempt is made to create a new
* connection using the supplied password and if this succeeds, idle connections created using the old password are
* destroyed and new connections are created using the new password.
* </p>
*
* @since 2.0
*/
public class SharedPoolDataSource extends InstanceKeyDataSource {
private static final long serialVersionUID = -1458539734480586454L;
// Pool properties
private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
private transient KeyedCPDSConnectionFactory factory;
/**
* Default no-argument constructor for Serialization
*/
public SharedPoolDataSource() {
// empty.
}
/**
* Closes pool being maintained by this data source.
*/
@Override
public void close() throws Exception {
if (pool != null) {
pool.close();
}
InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
}
// -------------------------------------------------------------------
// Properties
/**
* Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*
* @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*/
public int getMaxTotal() {
return this.maxTotal;
}
/**
* Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*
* @param maxTotal
* {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*/
public void setMaxTotal(final int maxTotal) {
assertInitializationAllowed();
this.maxTotal = maxTotal;
}
// ----------------------------------------------------------------------
// Instrumentation Methods
/**
* Gets the number of active connections in the pool.
*
* @return The number of active connections in the pool.
*/
public int getNumActive() {
return pool == null ? 0 : pool.getNumActive();
}
/**
* Gets the number of idle connections in the pool.
*
* @return The number of idle connections in the pool.
*/
public int getNumIdle() {
return pool == null ? 0 : pool.getNumIdle();
}
// ----------------------------------------------------------------------
// Inherited abstract methods
@Override
protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword)
throws SQLException {
synchronized (this) {
if (pool == null) {
try {
registerPool(userName, userPassword);
} catch (final NamingException e) {
throw new SQLException("RegisterPool failed", e);
}
}
}
PooledConnectionAndInfo info = null;
final UserPassKey key = new UserPassKey(userName, userPassword);
try {
info = pool.borrowObject(key);
} catch (final Exception e) {
throw new SQLException("Could not retrieve connection info from pool", e);
}
return info;
}
@Override
protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) {
return factory;
}
/**
* Returns a <code>SharedPoolDataSource</code> {@link Reference}.
*/
@Override
public Reference getReference() throws NamingException {
final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null);
ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
return ref;
}
private void registerPool(final String userName, final String password) throws NamingException, SQLException {
final ConnectionPoolDataSource cpds = testCPDS(userName, password);
// Create an object pool to contain our PooledConnections
factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeout(),
isRollbackAfterValidation());
factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = new GenericKeyedObjectPoolConfig<>();
config.setBlockWhenExhausted(getDefaultBlockWhenExhausted());
config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName());
config.setLifo(getDefaultLifo());
config.setMaxIdlePerKey(getDefaultMaxIdle());
config.setMaxTotal(getMaxTotal());
config.setMaxTotalPerKey(getDefaultMaxTotal());
config.setMaxWaitMillis(getDefaultMaxWaitMillis());
config.setMinEvictableIdleTimeMillis(getDefaultMinEvictableIdleTimeMillis());
config.setMinIdlePerKey(getDefaultMinIdle());
config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun());
config.setSoftMinEvictableIdleTimeMillis(getDefaultSoftMinEvictableIdleTimeMillis());
config.setTestOnCreate(getDefaultTestOnCreate());
config.setTestOnBorrow(getDefaultTestOnBorrow());
config.setTestOnReturn(getDefaultTestOnReturn());
config.setTestWhileIdle(getDefaultTestWhileIdle());
config.setTimeBetweenEvictionRunsMillis(getDefaultTimeBetweenEvictionRunsMillis());
final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory,
config);
factory.setPool(tmpPool);
pool = tmpPool;
}
@Override
protected void setupDefaults(final Connection connection, final String userName) throws SQLException {
final Boolean defaultAutoCommit = isDefaultAutoCommit();
if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit.booleanValue()) {
connection.setAutoCommit(defaultAutoCommit.booleanValue());
}
final int defaultTransactionIsolation = getDefaultTransactionIsolation();
if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
connection.setTransactionIsolation(defaultTransactionIsolation);
}
final Boolean defaultReadOnly = isDefaultReadOnly();
if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly.booleanValue()) {
connection.setReadOnly(defaultReadOnly.booleanValue());
}
}
/**
* Supports Serialization interface.
*
* @param in
* a <code>java.io.ObjectInputStream</code> value
* @throws IOException
* if an error occurs
* @throws ClassNotFoundException
* if an error occurs
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
in.defaultReadObject();
final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory()
.getObjectInstance(getReference(), null, null, null);
this.pool = oldDS.pool;
} catch (final NamingException e) {
throw new IOException("NamingException: " + e);
}
}
@Override
protected void toStringFields(final StringBuilder builder) {
super.toStringFields(builder);
builder.append(", maxTotal=");
builder.append(maxTotal);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import javax.naming.RefAddr;
import javax.naming.Reference;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
*
* @since 2.0
*/
public class SharedPoolDataSourceFactory extends InstanceKeyDataSourceFactory {
private static final String SHARED_POOL_CLASSNAME = SharedPoolDataSource.class.getName();
@Override
protected boolean isCorrectClass(final String className) {
return SHARED_POOL_CLASSNAME.equals(className);
}
@Override
protected InstanceKeyDataSource getNewInstance(final Reference ref) {
final SharedPoolDataSource spds = new SharedPoolDataSource();
final RefAddr ra = ref.get("maxTotal");
if (ra != null && ra.getContent() != null) {
spds.setMaxTotal(Integer.parseInt(ra.getContent().toString()));
}
return spds;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;
import java.io.Serializable;
import org.apache.tomcat.dbcp.dbcp2.Utils;
/**
* <p>
* Holds a user name and password pair. Serves as a poolable object key for the KeyedObjectPool backing a
* SharedPoolDataSource. Two instances with the same user name are considered equal. This ensures that there will be
* only one keyed pool for each user in the pool. The password is used (along with the user name) by the
* KeyedCPDSConnectionFactory when creating new connections.
* </p>
*
* <p>
* {@link InstanceKeyDataSource#getConnection(String, String)} validates that the password used to create a connection
* matches the password provided by the client.
* </p>
*
* @since 2.0
*/
class UserPassKey implements Serializable {
private static final long serialVersionUID = 5142970911626584817L;
private final String userName;
private final char[] userPassword;
/**
* @since 2.4.0
*/
UserPassKey(final String userName) {
this(userName, (char[]) null);
}
/**
* @since 2.4.0
*/
UserPassKey(final String userName, final char[] password) {
this.userName = userName;
this.userPassword = password;
}
UserPassKey(final String userName, final String userPassword) {
this(userName, Utils.toCharArray(userPassword));
}
/**
* Only takes the user name into account.
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UserPassKey other = (UserPassKey) obj;
if (userName == null) {
if (other.userName != null) {
return false;
}
} else if (!userName.equals(other.userName)) {
return false;
}
return true;
}
/**
* Gets the value of password.
*
* @return value of password.
*/
public String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password.
*
* @return value of password.
*/
public char[] getPasswordCharArray() {
return userPassword;
}
/**
* Gets the value of user name.
*
* @return value of user name.
*/
public String getUsername() {
return userName;
}
/**
* Only takes the user name into account.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer(super.toString());
sb.append("[");
sb.append(userName);
sb.append(']');
return sb.toString();
}
}

View File

@@ -0,0 +1,183 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* This package contains two DataSources: <code>PerUserPoolDataSource</code> and
* <code>SharedPoolDataSource</code> which provide a database connection pool.
* Below are a couple of usage examples. One shows deployment into a JNDI system.
* The other is a simple example initializing the pool using standard java code.
* </p>
*
* <h2>JNDI</h2>
*
* <p>
* Most
* J2EE containers will provide some way of deploying resources into JNDI. The
* method will vary among containers, but once the resource is available via
* JNDI, the application can access the resource in a container independent
* manner. The following example shows deployment into tomcat (catalina).
* </p>
* <p>In server.xml, the following would be added to the &lt;Context&gt; for your
* webapp:
* </p>
*
* <code>
* &lt;Resource name="jdbc/bookstore" auth="Container"
* type="org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolPoolDataSource"/&gt;
* &lt;ResourceParams name="jdbc/bookstore"&gt;
* &lt;parameter&gt;
* &lt;name&gt;factory&lt;/name&gt;
* &lt;value&gt;org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSourceFactory&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;dataSourceName&lt;/name&gt;&lt;value&gt;java:comp/env/jdbc/bookstoreCPDS&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;defaultMaxTotal&lt;/name&gt;&lt;value&gt;30&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;/ResourceParams&gt;
* </code>
*
* <p>
* In web.xml. Note that elements must be given in the order of the dtd
* described in the servlet specification:
* </p>
*
* <code>
* &lt;resource-ref&gt;
* &lt;description&gt;
* Resource reference to a factory for java.sql.Connection
* instances that may be used for talking to a particular
* database that is configured in the server.xml file.
* &lt;/description&gt;
* &lt;res-ref-name&gt;
* jdbc/bookstore
* &lt;/res-ref-name&gt;
* &lt;res-type&gt;
* org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSource
* &lt;/res-type&gt;
* &lt;res-auth&gt;
* Container
* &lt;/res-auth&gt;
* &lt;/resource-ref&gt;
* </code>
*
* <p>
* Apache Tomcat deploys all objects configured similarly to above within the
* <strong>java:comp/env</strong> namespace. So the JNDI path given for
* the dataSourceName parameter is valid for a
* <code>ConnectionPoolDataSource</code> that is deployed as given in the
* <a href="../cpdsadapter/package.html">cpdsadapter example</a>
* </p>
*
* <p>
* The <code>DataSource</code> is now available to the application as shown
* below:
* </p>
*
* <code>
*
* Context ctx = new InitialContext();
* DataSource ds = (DataSource)
* ctx.lookup("java:comp/env/jdbc/bookstore");
* Connection con = null;
* try
* {
* con = ds.getConnection();
* ...
* use the connection
* ...
* }
* finally
* {
* if (con != null)
* con.close();
* }
*
* </code>
*
* <p>
* The reference to the <code>DataSource</code> could be maintained, for
* multiple getConnection() requests. Or the <code>DataSource</code> can be
* looked up in different parts of the application code.
* <code>PerUserPoolDataSourceFactory</code> and
* <code>SharedPoolDataSourceFactory</code> will maintain the state of the pool
* between different lookups. This behavior may be different in other
* implementations.
* </p>
*
* <h2>Without JNDI</h2>
*
* <p>
* Connection pooling is useful in applications regardless of whether they run
* in a J2EE environment and a <code>DataSource</code> can be used within a
* simpler environment. The example below shows SharedPoolDataSource using
* DriverAdapterCPDS as the backend source, though any CPDS is applicable.
* </p>
*
* <code>
*
* public class Pool
* {
* private static DataSource ds;
*
* static
* {
* DriverAdapterCPDS cpds = new DriverAdapterCPDS();
* cpds.setDriver("org.gjt.mm.mysql.Driver");
* cpds.setUrl("jdbc:mysql://localhost:3306/bookstore");
* cpds.setUser("foo");
* cpds.setPassword(null);
*
* SharedPoolDataSource tds = new SharedPoolDataSource();
* tds.setConnectionPoolDataSource(cpds);
* tds.setMaxTotal(10);
* tds.setMaxWaitMillis(50);
*
* ds = tds;
* }
*
* public static getConnection()
* {
* return ds.getConnection();
* }
* }
*
* </code>
*
* <p>
* This class can then be used wherever a connection is needed:
* </p>
*
* <code>
* Connection con = null;
* try
* {
* con = Pool.getConnection();
* ...
* use the connection
* ...
* }
* finally
* {
* if (con != null)
* con.close();
* }
* </code>
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;

View File

@@ -0,0 +1,131 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* Database Connection Pool API.
* </p>
*
* <b>Overview in Dialog Form</b>
* <p>
* Q: How do I use the DBCP package?
* </p>
* <p>
* A: There are two primary ways to access the DBCP pool, as a {@link java.sql.Driver Driver}, or as a
* {@link javax.sql.DataSource DataSource}. You'll want to create an instance of
* {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver} or {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource}. When using one
* of these interfaces, you can just use your JDBC objects the way you normally would. Closing a
* {@link java.sql.Connection} will simply return it to its pool.
* </p>
* <p>
* Q: But {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver PoolingDriver} and
* {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource PoolingDataSource} both expect an
* {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} as an input. Where do I get one of those?
* </p>
* <p>
* A: The {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} interface is defined in Commons Pool. You can use one
* of the provided implementations such as {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool} or
* {@link org.apache.tomcat.dbcp.pool2.impl.SoftReferenceObjectPool SoftReferenceObjectPool} or you can create your own.
* </p>
* <p>
* Q: Ok, I've found an {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} implementation that I think suits my
* connection pooling needs. But it wants a {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory PooledObjectFactory}.
* What should I use for that?
* </p>
* <p>
* A: The DBCP package provides a class for this purpose. It's called
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}. It implements the factory and lifecycle methods of
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} for {@link java.sql.Connection}s. But it doesn't create the
* actual database {@link java.sql.Connection}s itself, it uses a {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} for
* that. The {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory} will take {@link java.sql.Connection}s created
* by the {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} and wrap them with classes that implement the pooling
* behaviour.
* </p>
* <p>
* Several implementations of {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} are provided--one that uses
* {@link java.sql.DriverManager} to create connections
* ({@link org.apache.tomcat.dbcp.dbcp2.DriverManagerConnectionFactory}), one that uses a {@link java.sql.Driver} to create
* connections ({@link org.apache.tomcat.dbcp.dbcp2.DriverConnectionFactory}), one that uses a {@link javax.sql.DataSource}
* to create connections ({@link org.apache.tomcat.dbcp.dbcp2.DataSourceConnectionFactory}).
* </p>
* <p>
* Q: I think I'm starting to get it, but can you walk me though it again?
* </p>
* <p>
* A: Sure. Let's assume you want to create a {@link javax.sql.DataSource} that pools {@link java.sql.Connection}s.
* Let's also assume that those pooled {@link java.sql.Connection}s should be obtained from the
* {@link java.sql.DriverManager}. You'll want to create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource}.
* </p>
* <p>
* The {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource} uses an underlying {@link org.apache.tomcat.dbcp.pool2.ObjectPool}
* to create and store its {@link java.sql.Connection}.
* </p>
* <p>
* To create a {@link org.apache.tomcat.dbcp.pool2.ObjectPool}, you'll need a
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} that creates the actual {@link java.sql.Connection}s. That's
* what {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory} is for.
* </p>
* <p>
* To create the {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}, you'll need at least two things:
* </p>
* <ol>
* <li>A {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} from which the actual database {@link java.sql.Connection}s
* will be obtained.</li>
* <li>An empty and factory-less {@link org.apache.tomcat.dbcp.pool2.ObjectPool} in which the {@link java.sql.Connection}s
* will be stored. <br>
* When you pass an {@link org.apache.tomcat.dbcp.pool2.ObjectPool} into the
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}, it will automatically register itself as the
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} for that pool.</li>
* </ol>
* <p>
* In code, that might look like this:
* </p>
*
* <pre>
* GenericObjectPool connectionPool = new GenericObjectPool(null);
* ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "userName",
* "password");
* PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,
* connectionPool, null, null, false, true);
* PoolingDataSource dataSource = new PoolingDataSource(connectionPool);
* </pre>
* <p>
* To create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, we do the same thing, except that instead of creating a
* {@link javax.sql.DataSource} on the last line, we create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, and
* register the {@code connectionPool} with it. E.g.,:
* </p>
*
* <pre>
* GenericObjectPool connectionPool = new GenericObjectPool(null);
* ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "userName",
* "password");
* PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,
* connectionPool, null, null, false, true);
* PoolingDriver driver = new PoolingDriver();
* driver.registerPool("example", connectionPool);
* </pre>
* <p>
* Since the {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver} registers itself with the {@link java.sql.DriverManager}
* when it is created, now you can just go to the {@link java.sql.DriverManager} to create your
* {@link java.sql.Connection}s, like you normally would:
* </p>
*
* <pre>
* Connection conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:example");
* </pre>
*/
package org.apache.tomcat.dbcp.dbcp2;