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 * @since 2.0 050 */ 051public class SharedPoolDataSource extends InstanceKeyDataSource { 052 053 private static final long serialVersionUID = -1458539734480586454L; 054 055 // Pool properties 056 private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; 057 058 059 private transient KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> pool = null; 060 private transient KeyedCPDSConnectionFactory factory = null; 061 062 /** 063 * Default no-arg constructor for Serialization 064 */ 065 public SharedPoolDataSource() { 066 } 067 068 /** 069 * Close pool being maintained by this datasource. 070 */ 071 @Override 072 public void close() throws Exception { 073 if (pool != null) { 074 pool.close(); 075 } 076 InstanceKeyDataSourceFactory.removeInstance(getInstanceKey()); 077 } 078 079 080 // ------------------------------------------------------------------- 081 // Properties 082 083 /** 084 * Set {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 085 */ 086 public int getMaxTotal() { 087 return this.maxTotal; 088 } 089 090 /** 091 * Get {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 092 */ 093 public void setMaxTotal(final int maxTotal) { 094 assertInitializationAllowed(); 095 this.maxTotal = maxTotal; 096 } 097 098 099 // ---------------------------------------------------------------------- 100 // Instrumentation Methods 101 102 /** 103 * Get the number of active connections in the pool. 104 */ 105 public int getNumActive() { 106 return pool == null ? 0 : pool.getNumActive(); 107 } 108 109 /** 110 * Get the number of idle connections in the pool. 111 */ 112 public int getNumIdle() { 113 return pool == null ? 0 : pool.getNumIdle(); 114 } 115 116 // ---------------------------------------------------------------------- 117 // Inherited abstract methods 118 119 @Override 120 protected PooledConnectionAndInfo 121 getPooledConnectionAndInfo(final String username, final String password) 122 throws SQLException { 123 124 synchronized(this) { 125 if (pool == null) { 126 try { 127 registerPool(username, password); 128 } catch (final NamingException e) { 129 throw new SQLException("RegisterPool failed", e); 130 } 131 } 132 } 133 134 PooledConnectionAndInfo info = null; 135 136 final UserPassKey key = new UserPassKey(username, password); 137 138 try { 139 info = pool.borrowObject(key); 140 } 141 catch (final Exception e) { 142 throw new SQLException( 143 "Could not retrieve connection info from pool", e); 144 } 145 return info; 146 } 147 148 @Override 149 protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) { 150 return factory; 151 } 152 153 /** 154 * Returns a <code>SharedPoolDataSource</code> {@link Reference}. 155 */ 156 @Override 157 public Reference getReference() throws NamingException { 158 final Reference ref = new Reference(getClass().getName(), 159 SharedPoolDataSourceFactory.class.getName(), null); 160 ref.add(new StringRefAddr("instanceKey", getInstanceKey())); 161 return ref; 162 } 163 164 private void registerPool(final String username, final String password) 165 throws NamingException, SQLException { 166 167 final ConnectionPoolDataSource cpds = testCPDS(username, password); 168 169 // Create an object pool to contain our PooledConnections 170 factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), 171 getValidationQueryTimeout(), isRollbackAfterValidation()); 172 factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); 173 174 final GenericKeyedObjectPoolConfig config = 175 new GenericKeyedObjectPoolConfig(); 176 config.setBlockWhenExhausted(getDefaultBlockWhenExhausted()); 177 config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName()); 178 config.setLifo(getDefaultLifo()); 179 config.setMaxIdlePerKey(getDefaultMaxIdle()); 180 config.setMaxTotal(getMaxTotal()); 181 config.setMaxTotalPerKey(getDefaultMaxTotal()); 182 config.setMaxWaitMillis(getDefaultMaxWaitMillis()); 183 config.setMinEvictableIdleTimeMillis( 184 getDefaultMinEvictableIdleTimeMillis()); 185 config.setMinIdlePerKey(getDefaultMinIdle()); 186 config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun()); 187 config.setSoftMinEvictableIdleTimeMillis( 188 getDefaultSoftMinEvictableIdleTimeMillis()); 189 config.setTestOnCreate(getDefaultTestOnCreate()); 190 config.setTestOnBorrow(getDefaultTestOnBorrow()); 191 config.setTestOnReturn(getDefaultTestOnReturn()); 192 config.setTestWhileIdle(getDefaultTestWhileIdle()); 193 config.setTimeBetweenEvictionRunsMillis( 194 getDefaultTimeBetweenEvictionRunsMillis()); 195 196 final KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> tmpPool = 197 new GenericKeyedObjectPool<>(factory, config); 198 factory.setPool(tmpPool); 199 pool = tmpPool; 200 } 201 202 @Override 203 protected void setupDefaults(final Connection con, final String username) throws SQLException { 204 final Boolean defaultAutoCommit = isDefaultAutoCommit(); 205 if (defaultAutoCommit != null && 206 con.getAutoCommit() != defaultAutoCommit.booleanValue()) { 207 con.setAutoCommit(defaultAutoCommit.booleanValue()); 208 } 209 210 final int defaultTransactionIsolation = getDefaultTransactionIsolation(); 211 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 212 con.setTransactionIsolation(defaultTransactionIsolation); 213 } 214 215 final Boolean defaultReadOnly = isDefaultReadOnly(); 216 if (defaultReadOnly != null && 217 con.isReadOnly() != defaultReadOnly.booleanValue()) { 218 con.setReadOnly(defaultReadOnly.booleanValue()); 219 } 220 } 221 222 /** 223 * Supports Serialization interface. 224 * 225 * @param in a <code>java.io.ObjectInputStream</code> value 226 * @throws IOException if an error occurs 227 * @throws ClassNotFoundException if an error occurs 228 */ 229 private void readObject(final ObjectInputStream in) 230 throws IOException, ClassNotFoundException { 231 try 232 { 233 in.defaultReadObject(); 234 final SharedPoolDataSource oldDS = (SharedPoolDataSource) 235 new SharedPoolDataSourceFactory() 236 .getObjectInstance(getReference(), null, null, null); 237 this.pool = oldDS.pool; 238 } 239 catch (final NamingException e) 240 { 241 throw new IOException("NamingException: " + e); 242 } 243 } 244} 245