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 */ 017 018 package org.apache.commons.dbcp.datasources; 019 020 import java.io.IOException; 021 import java.io.ObjectInputStream; 022 import java.sql.Connection; 023 import java.sql.SQLException; 024 025 import javax.naming.NamingException; 026 import javax.naming.Reference; 027 import javax.naming.StringRefAddr; 028 import javax.sql.ConnectionPoolDataSource; 029 030 import org.apache.commons.pool.KeyedObjectPool; 031 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 032 import org.apache.commons.pool.impl.GenericObjectPool; 033 import org.apache.commons.dbcp.SQLNestedException; 034 035 /** 036 * <p>A pooling <code>DataSource</code> appropriate for deployment within 037 * J2EE environment. There are many configuration options, most of which are 038 * defined in the parent class. All users (based on username) share a single 039 * maximum number of Connections in this datasource.</p> 040 * 041 * <p>User passwords can be changed without re-initializing the datasource. 042 * When a <code>getConnection(username, password)</code> request is processed 043 * with a password that is different from those used to create connections in the 044 * pool associated with <code>username</code>, an attempt is made to create a 045 * new connection using the supplied password and if this succeeds, idle connections 046 * created using the old password are destroyed and new connections are created 047 * using the new password.</p> 048 * 049 * @author John D. McNally 050 * @version $Revision: 907288 $ $Date: 2010-02-06 14:42:58 -0500 (Sat, 06 Feb 2010) $ 051 */ 052 public class SharedPoolDataSource 053 extends InstanceKeyDataSource { 054 055 private static final long serialVersionUID = -8132305535403690372L; 056 057 private int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE; 058 private int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE; 059 private int maxWait = (int)Math.min(Integer.MAX_VALUE, 060 GenericObjectPool.DEFAULT_MAX_WAIT); 061 private transient KeyedObjectPool pool = null; 062 private transient KeyedCPDSConnectionFactory factory = null; 063 064 /** 065 * Default no-arg constructor for Serialization 066 */ 067 public SharedPoolDataSource() { 068 } 069 070 /** 071 * Close pool being maintained by this datasource. 072 */ 073 public void close() throws Exception { 074 if (pool != null) { 075 pool.close(); 076 } 077 InstanceKeyObjectFactory.removeInstance(instanceKey); 078 } 079 080 // ------------------------------------------------------------------- 081 // Properties 082 083 /** 084 * The maximum number of active connections that can be allocated from 085 * this pool at the same time, or non-positive for no limit. 086 */ 087 public int getMaxActive() { 088 return (this.maxActive); 089 } 090 091 /** 092 * The maximum number of active connections that can be allocated from 093 * this pool at the same time, or non-positive for no limit. 094 * The default is 8. 095 */ 096 public void setMaxActive(int maxActive) { 097 assertInitializationAllowed(); 098 this.maxActive = maxActive; 099 } 100 101 /** 102 * The maximum number of active connections that can remain idle in the 103 * pool, without extra ones being released, or negative for no limit. 104 */ 105 public int getMaxIdle() { 106 return (this.maxIdle); 107 } 108 109 /** 110 * The maximum number of active connections that can remain idle in the 111 * pool, without extra ones being released, or negative for no limit. 112 * The default is 8. 113 */ 114 public void setMaxIdle(int maxIdle) { 115 assertInitializationAllowed(); 116 this.maxIdle = maxIdle; 117 } 118 119 /** 120 * The maximum number of milliseconds that the pool will wait (when there 121 * are no available connections) for a connection to be returned before 122 * throwing an exception, or -1 to wait indefinitely. Will fail 123 * immediately if value is 0. 124 * The default is -1. 125 */ 126 public int getMaxWait() { 127 return (this.maxWait); 128 } 129 130 /** 131 * The maximum number of milliseconds that the pool will wait (when there 132 * are no available connections) for a connection to be returned before 133 * throwing an exception, or -1 to wait indefinitely. Will fail 134 * immediately if value is 0. 135 * The default is -1. 136 */ 137 public void setMaxWait(int maxWait) { 138 assertInitializationAllowed(); 139 this.maxWait = maxWait; 140 } 141 142 // ---------------------------------------------------------------------- 143 // Instrumentation Methods 144 145 /** 146 * Get the number of active connections in the pool. 147 */ 148 public int getNumActive() { 149 return (pool == null) ? 0 : pool.getNumActive(); 150 } 151 152 /** 153 * Get the number of idle connections in the pool. 154 */ 155 public int getNumIdle() { 156 return (pool == null) ? 0 : pool.getNumIdle(); 157 } 158 159 // ---------------------------------------------------------------------- 160 // Inherited abstract methods 161 162 protected PooledConnectionAndInfo 163 getPooledConnectionAndInfo(String username, String password) 164 throws SQLException { 165 166 synchronized(this) { 167 if (pool == null) { 168 try { 169 registerPool(username, password); 170 } catch (NamingException e) { 171 throw new SQLNestedException("RegisterPool failed", e); 172 } 173 } 174 } 175 176 PooledConnectionAndInfo info = null; 177 178 UserPassKey key = new UserPassKey(username, password); 179 180 try { 181 info = (PooledConnectionAndInfo) pool.borrowObject(key); 182 } 183 catch (Exception e) { 184 throw new SQLNestedException( 185 "Could not retrieve connection info from pool", e); 186 } 187 return info; 188 } 189 190 protected PooledConnectionManager getConnectionManager(UserPassKey upkey) { 191 return factory; 192 } 193 194 /** 195 * Returns a <code>SharedPoolDataSource</code> {@link Reference}. 196 * 197 * @since 1.2.2 198 */ 199 public Reference getReference() throws NamingException { 200 Reference ref = new Reference(getClass().getName(), 201 SharedPoolDataSourceFactory.class.getName(), null); 202 ref.add(new StringRefAddr("instanceKey", instanceKey)); 203 return ref; 204 } 205 206 private void registerPool( 207 String username, String password) 208 throws javax.naming.NamingException, SQLException { 209 210 ConnectionPoolDataSource cpds = testCPDS(username, password); 211 212 // Create an object pool to contain our PooledConnections 213 GenericKeyedObjectPool tmpPool = new GenericKeyedObjectPool(null); 214 tmpPool.setMaxActive(getMaxActive()); 215 tmpPool.setMaxIdle(getMaxIdle()); 216 tmpPool.setMaxWait(getMaxWait()); 217 tmpPool.setWhenExhaustedAction(whenExhaustedAction(maxActive, maxWait)); 218 tmpPool.setTestOnBorrow(getTestOnBorrow()); 219 tmpPool.setTestOnReturn(getTestOnReturn()); 220 tmpPool.setTimeBetweenEvictionRunsMillis( 221 getTimeBetweenEvictionRunsMillis()); 222 tmpPool.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun()); 223 tmpPool.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis()); 224 tmpPool.setTestWhileIdle(getTestWhileIdle()); 225 pool = tmpPool; 226 // Set up the factory we will use (passing the pool associates 227 // the factory with the pool, so we do not have to do so 228 // explicitly) 229 factory = new KeyedCPDSConnectionFactory(cpds, pool, getValidationQuery(), 230 isRollbackAfterValidation()); 231 } 232 233 protected void setupDefaults(Connection con, String username) throws SQLException { 234 boolean defaultAutoCommit = isDefaultAutoCommit(); 235 if (con.getAutoCommit() != defaultAutoCommit) { 236 con.setAutoCommit(defaultAutoCommit); 237 } 238 239 int defaultTransactionIsolation = getDefaultTransactionIsolation(); 240 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 241 con.setTransactionIsolation(defaultTransactionIsolation); 242 } 243 244 boolean defaultReadOnly = isDefaultReadOnly(); 245 if (con.isReadOnly() != defaultReadOnly) { 246 con.setReadOnly(defaultReadOnly); 247 } 248 } 249 250 /** 251 * Supports Serialization interface. 252 * 253 * @param in a <code>java.io.ObjectInputStream</code> value 254 * @exception IOException if an error occurs 255 * @exception ClassNotFoundException if an error occurs 256 */ 257 private void readObject(ObjectInputStream in) 258 throws IOException, ClassNotFoundException { 259 try 260 { 261 in.defaultReadObject(); 262 SharedPoolDataSource oldDS = (SharedPoolDataSource) 263 new SharedPoolDataSourceFactory() 264 .getObjectInstance(getReference(), null, null, null); 265 this.pool = oldDS.pool; 266 } 267 catch (NamingException e) 268 { 269 throw new IOException("NamingException: " + e); 270 } 271 } 272 } 273