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 * @since 2.0 042 */ 043public class PoolingDriver implements Driver { 044 /** Register myself with the {@link DriverManager}. */ 045 static { 046 try { 047 DriverManager.registerDriver(new PoolingDriver()); 048 } catch(final Exception e) { 049 } 050 } 051 052 /** The map of registered pools. */ 053 protected static final HashMap<String,ObjectPool<? extends Connection>> pools = 054 new HashMap<>(); 055 056 /** Controls access to the underlying connection */ 057 private final boolean accessToUnderlyingConnectionAllowed; 058 059 public PoolingDriver() { 060 this(true); 061 } 062 063 /** 064 * For unit testing purposes. 065 */ 066 protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) { 067 this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed; 068 } 069 070 071 /** 072 * Returns the value of the accessToUnderlyingConnectionAllowed property. 073 * 074 * @return true if access to the underlying is allowed, false otherwise. 075 */ 076 protected boolean isAccessToUnderlyingConnectionAllowed() { 077 return accessToUnderlyingConnectionAllowed; 078 } 079 080 public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) 081 throws SQLException { 082 final ObjectPool<? extends Connection> pool = pools.get(name); 083 if (null == pool) { 084 throw new SQLException("Pool not registered."); 085 } 086 return pool; 087 } 088 089 public synchronized void registerPool(final String name, 090 final ObjectPool<? extends Connection> pool) { 091 pools.put(name,pool); 092 } 093 094 public synchronized void closePool(final String name) throws SQLException { 095 final ObjectPool<? extends Connection> pool = pools.get(name); 096 if (pool != null) { 097 pools.remove(name); 098 try { 099 pool.close(); 100 } 101 catch (final Exception e) { 102 throw new SQLException("Error closing pool " + name, e); 103 } 104 } 105 } 106 107 public synchronized String[] getPoolNames(){ 108 final Set<String> names = pools.keySet(); 109 return names.toArray(new String[names.size()]); 110 } 111 112 @Override 113 public boolean acceptsURL(final String url) throws SQLException { 114 try { 115 return url.startsWith(URL_PREFIX); 116 } catch(final NullPointerException e) { 117 return false; 118 } 119 } 120 121 @Override 122 public Connection connect(final String url, final Properties info) throws SQLException { 123 if(acceptsURL(url)) { 124 final ObjectPool<? extends Connection> pool = 125 getConnectionPool(url.substring(URL_PREFIX_LEN)); 126 127 try { 128 final Connection conn = pool.borrowObject(); 129 if (conn == null) { 130 return null; 131 } 132 return new PoolGuardConnectionWrapper(pool, conn); 133 } catch(final SQLException e) { 134 throw e; 135 } catch(final NoSuchElementException e) { 136 throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e); 137 } catch(final RuntimeException e) { 138 throw e; 139 } catch(final Exception e) { 140 throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e); 141 } 142 } 143 return null; 144 } 145 146 @Override 147 public Logger getParentLogger() throws SQLFeatureNotSupportedException { 148 throw new SQLFeatureNotSupportedException(); 149 } 150 151 /** 152 * Invalidates the given connection. 153 * 154 * @param conn connection to invalidate 155 * @throws SQLException if the connection is not a 156 * <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating 157 * the connection 158 */ 159 public void invalidateConnection(final Connection conn) throws SQLException { 160 if (conn instanceof PoolGuardConnectionWrapper) { // normal case 161 final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn; 162 @SuppressWarnings("unchecked") 163 final 164 ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool; 165 try { 166 pool.invalidateObject(pgconn.getDelegateInternal()); 167 } 168 catch (final 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(final String url, final 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(final ObjectPool<? extends Connection> pool, 214 final 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}