001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.dbcp2; 018 019import java.sql.Connection; 020import java.sql.Driver; 021import java.sql.DriverManager; 022import java.sql.DriverPropertyInfo; 023import java.sql.SQLException; 024import java.sql.SQLFeatureNotSupportedException; 025import java.util.HashMap; 026import java.util.NoSuchElementException; 027import java.util.Properties; 028import java.util.Set; 029import java.util.logging.Logger; 030 031import org.apache.commons.pool2.ObjectPool; 032 033 034/** 035 * A {@link Driver} implementation that obtains 036 * {@link Connection}s from a registered 037 * {@link ObjectPool}. 038 * 039 * @author Rodney Waldhoff 040 * @author Dirk Verbeeck 041 * @version $Id: PoolingDriver.java 1649430 2015-01-04 21:29:32Z tn $ 042 * @since 2.0 043 */ 044public class PoolingDriver implements Driver { 045 /** Register myself with the {@link DriverManager}. */ 046 static { 047 try { 048 DriverManager.registerDriver(new PoolingDriver()); 049 } catch(Exception e) { 050 } 051 } 052 053 /** The map of registered pools. */ 054 protected static final HashMap<String,ObjectPool<? extends Connection>> pools = 055 new HashMap<>(); 056 057 /** Controls access to the underlying connection */ 058 private final boolean accessToUnderlyingConnectionAllowed; 059 060 public PoolingDriver() { 061 this(true); 062 } 063 064 /** 065 * For unit testing purposes. 066 */ 067 protected PoolingDriver(boolean accessToUnderlyingConnectionAllowed) { 068 this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed; 069 } 070 071 072 /** 073 * Returns the value of the accessToUnderlyingConnectionAllowed property. 074 * 075 * @return true if access to the underlying is allowed, false otherwise. 076 */ 077 protected boolean isAccessToUnderlyingConnectionAllowed() { 078 return accessToUnderlyingConnectionAllowed; 079 } 080 081 public synchronized ObjectPool<? extends Connection> getConnectionPool(String name) 082 throws SQLException { 083 ObjectPool<? extends Connection> pool = pools.get(name); 084 if (null == pool) { 085 throw new SQLException("Pool not registered."); 086 } 087 return pool; 088 } 089 090 public synchronized void registerPool(String name, 091 ObjectPool<? extends Connection> pool) { 092 pools.put(name,pool); 093 } 094 095 public synchronized void closePool(String name) throws SQLException { 096 ObjectPool<? extends Connection> pool = pools.get(name); 097 if (pool != null) { 098 pools.remove(name); 099 try { 100 pool.close(); 101 } 102 catch (Exception e) { 103 throw new SQLException("Error closing pool " + name, e); 104 } 105 } 106 } 107 108 public synchronized String[] getPoolNames(){ 109 Set<String> names = pools.keySet(); 110 return names.toArray(new String[names.size()]); 111 } 112 113 @Override 114 public boolean acceptsURL(String url) throws SQLException { 115 try { 116 return url.startsWith(URL_PREFIX); 117 } catch(NullPointerException e) { 118 return false; 119 } 120 } 121 122 @Override 123 public Connection connect(String url, Properties info) throws SQLException { 124 if(acceptsURL(url)) { 125 ObjectPool<? extends Connection> pool = 126 getConnectionPool(url.substring(URL_PREFIX_LEN)); 127 128 try { 129 Connection conn = pool.borrowObject(); 130 if (conn == null) { 131 return null; 132 } 133 return new PoolGuardConnectionWrapper(pool, conn); 134 } catch(SQLException e) { 135 throw e; 136 } catch(NoSuchElementException e) { 137 throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e); 138 } catch(RuntimeException e) { 139 throw e; 140 } catch(Exception e) { 141 throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e); 142 } 143 } 144 return null; 145 } 146 147 @Override 148 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 149 throw new SQLFeatureNotSupportedException(); 150 } 151 152 /** 153 * Invalidates the given connection. 154 * 155 * @param conn connection to invalidate 156 * @throws SQLException if the connection is not a 157 * <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating 158 * the connection 159 */ 160 public void invalidateConnection(Connection conn) throws SQLException { 161 if (conn instanceof PoolGuardConnectionWrapper) { // normal case 162 PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn; 163 @SuppressWarnings("unchecked") 164 ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool; 165 try { 166 pool.invalidateObject(pgconn.getDelegateInternal()); 167 } 168 catch (Exception e) { 169 } 170 } 171 else { 172 throw new SQLException("Invalid connection class"); 173 } 174 } 175 176 @Override 177 public int getMajorVersion() { 178 return MAJOR_VERSION; 179 } 180 181 @Override 182 public int getMinorVersion() { 183 return MINOR_VERSION; 184 } 185 186 @Override 187 public boolean jdbcCompliant() { 188 return true; 189 } 190 191 @Override 192 public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { 193 return new DriverPropertyInfo[0]; 194 } 195 196 /** My URL prefix */ 197 protected static final String URL_PREFIX = "jdbc:apache:commons:dbcp:"; 198 protected static final int URL_PREFIX_LEN = URL_PREFIX.length(); 199 200 // version numbers 201 protected static final int MAJOR_VERSION = 1; 202 protected static final int MINOR_VERSION = 0; 203 204 /** 205 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a 206 * closed connection cannot be used anymore. 207 * @since 2.0 208 */ 209 private class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> { 210 211 private final ObjectPool<? extends Connection> pool; 212 213 PoolGuardConnectionWrapper(ObjectPool<? extends Connection> pool, 214 Connection delegate) { 215 super(delegate); 216 this.pool = pool; 217 } 218 219 /** 220 * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate() 221 */ 222 @Override 223 public Connection getDelegate() { 224 if (isAccessToUnderlyingConnectionAllowed()) { 225 return super.getDelegate(); 226 } 227 return null; 228 } 229 230 /** 231 * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate() 232 */ 233 @Override 234 public Connection getInnermostDelegate() { 235 if (isAccessToUnderlyingConnectionAllowed()) { 236 return super.getInnermostDelegate(); 237 } 238 return null; 239 } 240 } 241}