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 018package org.apache.commons.dbcp2.datasources; 019 020import java.io.IOException; 021import java.io.ObjectInputStream; 022import java.sql.Connection; 023import java.sql.SQLException; 024 025import javax.naming.NamingException; 026import javax.naming.Reference; 027import javax.naming.StringRefAddr; 028import javax.sql.ConnectionPoolDataSource; 029 030import org.apache.commons.pool2.KeyedObjectPool; 031import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 032import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 033 034/** 035 * <p>A pooling <code>DataSource</code> appropriate for deployment within 036 * J2EE environment. There are many configuration options, most of which are 037 * defined in the parent class. All users (based on username) share a single 038 * maximum number of Connections in this datasource.</p> 039 * 040 * <p>User passwords can be changed without re-initializing the datasource. 041 * When a <code>getConnection(username, password)</code> request is processed 042 * with a password that is different from those used to create connections in the 043 * pool associated with <code>username</code>, an attempt is made to create a 044 * new connection using the supplied password and if this succeeds, idle connections 045 * created using the old password are destroyed and new connections are created 046 * using the new password.</p> 047 * 048 * @author John D. McNally 049 * @version $Id: SharedPoolDataSource.java 1649430 2015-01-04 21:29:32Z tn $ 050 * @since 2.0 051 */ 052public class SharedPoolDataSource extends InstanceKeyDataSource { 053 054 private static final long serialVersionUID = -1458539734480586454L; 055 056 // Pool properties 057 private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; 058 059 060 private transient KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> pool = null; 061 private transient KeyedCPDSConnectionFactory factory = null; 062 063 /** 064 * Default no-arg constructor for Serialization 065 */ 066 public SharedPoolDataSource() { 067 } 068 069 /** 070 * Close pool being maintained by this datasource. 071 */ 072 @Override 073 public void close() throws Exception { 074 if (pool != null) { 075 pool.close(); 076 } 077 InstanceKeyDataSourceFactory.removeInstance(getInstanceKey()); 078 } 079 080 081 // ------------------------------------------------------------------- 082 // Properties 083 084 /** 085 * Set {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 086 */ 087 public int getMaxTotal() { 088 return this.maxTotal; 089 } 090 091 /** 092 * Get {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 093 */ 094 public void setMaxTotal(int maxTotal) { 095 assertInitializationAllowed(); 096 this.maxTotal = maxTotal; 097 } 098 099 100 // ---------------------------------------------------------------------- 101 // Instrumentation Methods 102 103 /** 104 * Get the number of active connections in the pool. 105 */ 106 public int getNumActive() { 107 return pool == null ? 0 : pool.getNumActive(); 108 } 109 110 /** 111 * Get the number of idle connections in the pool. 112 */ 113 public int getNumIdle() { 114 return pool == null ? 0 : pool.getNumIdle(); 115 } 116 117 // ---------------------------------------------------------------------- 118 // Inherited abstract methods 119 120 @Override 121 protected PooledConnectionAndInfo 122 getPooledConnectionAndInfo(String username, String password) 123 throws SQLException { 124 125 synchronized(this) { 126 if (pool == null) { 127 try { 128 registerPool(username, password); 129 } catch (NamingException e) { 130 throw new SQLException("RegisterPool failed", e); 131 } 132 } 133 } 134 135 PooledConnectionAndInfo info = null; 136 137 UserPassKey key = new UserPassKey(username, password); 138 139 try { 140 info = pool.borrowObject(key); 141 } 142 catch (Exception e) { 143 throw new SQLException( 144 "Could not retrieve connection info from pool", e); 145 } 146 return info; 147 } 148 149 @Override 150 protected PooledConnectionManager getConnectionManager(UserPassKey upkey) { 151 return factory; 152 } 153 154 /** 155 * Returns a <code>SharedPoolDataSource</code> {@link Reference}. 156 */ 157 @Override 158 public Reference getReference() throws NamingException { 159 Reference ref = new Reference(getClass().getName(), 160 SharedPoolDataSourceFactory.class.getName(), null); 161 ref.add(new StringRefAddr("instanceKey", getInstanceKey())); 162 return ref; 163 } 164 165 private void registerPool(String username, String password) 166 throws NamingException, SQLException { 167 168 ConnectionPoolDataSource cpds = testCPDS(username, password); 169 170 // Create an object pool to contain our PooledConnections 171 factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), 172 getValidationQueryTimeout(), isRollbackAfterValidation()); 173 factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); 174 175 GenericKeyedObjectPoolConfig config = 176 new GenericKeyedObjectPoolConfig(); 177 config.setBlockWhenExhausted(getDefaultBlockWhenExhausted()); 178 config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName()); 179 config.setLifo(getDefaultLifo()); 180 config.setMaxIdlePerKey(getDefaultMaxIdle()); 181 config.setMaxTotal(getMaxTotal()); 182 config.setMaxTotalPerKey(getDefaultMaxTotal()); 183 config.setMaxWaitMillis(getDefaultMaxWaitMillis()); 184 config.setMinEvictableIdleTimeMillis( 185 getDefaultMinEvictableIdleTimeMillis()); 186 config.setMinIdlePerKey(getDefaultMinIdle()); 187 config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun()); 188 config.setSoftMinEvictableIdleTimeMillis( 189 getDefaultSoftMinEvictableIdleTimeMillis()); 190 config.setTestOnCreate(getDefaultTestOnCreate()); 191 config.setTestOnBorrow(getDefaultTestOnBorrow()); 192 config.setTestOnReturn(getDefaultTestOnReturn()); 193 config.setTestWhileIdle(getDefaultTestWhileIdle()); 194 config.setTimeBetweenEvictionRunsMillis( 195 getDefaultTimeBetweenEvictionRunsMillis()); 196 197 KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> tmpPool = 198 new GenericKeyedObjectPool<>(factory, config); 199 factory.setPool(tmpPool); 200 pool = tmpPool; 201 } 202 203 @Override 204 protected void setupDefaults(Connection con, String username) throws SQLException { 205 Boolean defaultAutoCommit = isDefaultAutoCommit(); 206 if (defaultAutoCommit != null && 207 con.getAutoCommit() != defaultAutoCommit.booleanValue()) { 208 con.setAutoCommit(defaultAutoCommit.booleanValue()); 209 } 210 211 int defaultTransactionIsolation = getDefaultTransactionIsolation(); 212 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 213 con.setTransactionIsolation(defaultTransactionIsolation); 214 } 215 216 Boolean defaultReadOnly = isDefaultReadOnly(); 217 if (defaultReadOnly != null && 218 con.isReadOnly() != defaultReadOnly.booleanValue()) { 219 con.setReadOnly(defaultReadOnly.booleanValue()); 220 } 221 } 222 223 /** 224 * Supports Serialization interface. 225 * 226 * @param in a <code>java.io.ObjectInputStream</code> value 227 * @exception IOException if an error occurs 228 * @exception ClassNotFoundException if an error occurs 229 */ 230 private void readObject(ObjectInputStream in) 231 throws IOException, ClassNotFoundException { 232 try 233 { 234 in.defaultReadObject(); 235 SharedPoolDataSource oldDS = (SharedPoolDataSource) 236 new SharedPoolDataSourceFactory() 237 .getObjectInstance(getReference(), null, null, null); 238 this.pool = oldDS.pool; 239 } 240 catch (NamingException e) 241 { 242 throw new IOException("NamingException: " + e); 243 } 244 } 245} 246