/*
* 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.datanucleus.store.rdbms.datasource.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.datanucleus.store.rdbms.datasource.dbcp2.pool2.ObjectPool;
/**
* A {@link Driver} implementation that obtains
* {@link Connection}s from a registered
* {@link ObjectPool}.
*
* @author Rodney Waldhoff
* @author Dirk Verbeeck
* @version $Id: PoolingDriver.java 1649430 2015-01-04 21:29:32Z tn $
* @since 2.0
*/
public class PoolingDriver implements Driver {
/** Register myself with the {@link DriverManager}. */
static {
try {
DriverManager.registerDriver(new PoolingDriver());
} catch(Exception e) {
}
}
/** 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;
public PoolingDriver() {
this(true);
}
/**
* For unit testing purposes.
*/
protected PoolingDriver(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;
}
public synchronized ObjectPool<? extends Connection> getConnectionPool(String name)
throws SQLException {
ObjectPool<? extends Connection> pool = pools.get(name);
if (null == pool) {
throw new SQLException("Pool not registered.");
}
return pool;
}
public synchronized void registerPool(String name,
ObjectPool<? extends Connection> pool) {
pools.put(name,pool);
}
public synchronized void closePool(String name) throws SQLException {
ObjectPool<? extends Connection> pool = pools.get(name);
if (pool != null) {
pools.remove(name);
try {
pool.close();
}
catch (Exception e) {
throw new SQLException("Error closing pool " + name, e);
}
}
}
public synchronized String[] getPoolNames(){
Set<String> names = pools.keySet();
return names.toArray(new String[names.size()]);
}
@Override
public boolean acceptsURL(String url) throws SQLException {
try {
return url.startsWith(URL_PREFIX);
} catch(NullPointerException e) {
return false;
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
if(acceptsURL(url)) {
ObjectPool<? extends Connection> pool =
getConnectionPool(url.substring(URL_PREFIX_LEN));
try {
Connection conn = pool.borrowObject();
if (conn == null) {
return null;
}
return new PoolGuardConnectionWrapper(pool, conn);
} catch(SQLException e) {
throw e;
} catch(NoSuchElementException e) {
throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
} catch(RuntimeException e) {
throw e;
} catch(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(Connection conn) throws SQLException {
if (conn instanceof PoolGuardConnectionWrapper) { // normal case
PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
@SuppressWarnings("unchecked")
ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
try {
pool.invalidateObject(pgconn.getDelegateInternal());
}
catch (Exception e) {
}
}
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(String url, Properties info) {
return new DriverPropertyInfo[0];
}
/** My URL prefix */
protected 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(ObjectPool<? extends Connection> pool,
Connection delegate) {
super(delegate);
this.pool = pool;
}
/**
* @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
*/
@Override
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getDelegate();
}
return null;
}
/**
* @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
@Override
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getInnermostDelegate();
}
return null;
}
}
}