init
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
82
java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java
Normal file
82
java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
134
java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java
Normal file
134
java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java
Normal 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();
|
||||
}
|
||||
}
|
||||
183
java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java
Normal file
183
java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java
Normal 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 <Context> for your
|
||||
* webapp:
|
||||
* </p>
|
||||
*
|
||||
* <code>
|
||||
* <Resource name="jdbc/bookstore" auth="Container"
|
||||
* type="org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolPoolDataSource"/>
|
||||
* <ResourceParams name="jdbc/bookstore">
|
||||
* <parameter>
|
||||
* <name>factory</name>
|
||||
* <value>org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSourceFactory</value>
|
||||
* </parameter>
|
||||
* <parameter>
|
||||
* <name>dataSourceName</name><value>java:comp/env/jdbc/bookstoreCPDS</value>
|
||||
* </parameter>
|
||||
* <parameter>
|
||||
* <name>defaultMaxTotal</name><value>30</value>
|
||||
* </parameter>
|
||||
* </ResourceParams>
|
||||
* </code>
|
||||
*
|
||||
* <p>
|
||||
* In web.xml. Note that elements must be given in the order of the dtd
|
||||
* described in the servlet specification:
|
||||
* </p>
|
||||
*
|
||||
* <code>
|
||||
* <resource-ref>
|
||||
* <description>
|
||||
* 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.
|
||||
* </description>
|
||||
* <res-ref-name>
|
||||
* jdbc/bookstore
|
||||
* </res-ref-name>
|
||||
* <res-type>
|
||||
* org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSource
|
||||
* </res-type>
|
||||
* <res-auth>
|
||||
* Container
|
||||
* </res-auth>
|
||||
* </resource-ref>
|
||||
* </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;
|
||||
Reference in New Issue
Block a user