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> 036 * A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration 037 * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number 038 * of Connections in this data source. 039 * </p> 040 * 041 * <p> 042 * User passwords can be changed without re-initializing the data source. When a 043 * <code>getConnection(user name, password)</code> request is processed with a password that is different from those 044 * used to create connections in the pool associated with <code>user name</code>, an attempt is made to create a new 045 * connection using the supplied password and if this succeeds, idle connections created using the old password are 046 * destroyed and new connections are created using the new password. 047 * </p> 048 * 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 private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool; 059 private transient KeyedCPDSConnectionFactory factory; 060 061 /** 062 * Default no-argument constructor for Serialization 063 */ 064 public SharedPoolDataSource() { 065 // empty. 066 } 067 068 /** 069 * Closes pool being maintained by this data source. 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 // Properties 081 082 /** 083 * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 084 * 085 * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 086 */ 087 public int getMaxTotal() { 088 return this.maxTotal; 089 } 090 091 /** 092 * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 093 * 094 * @param maxTotal 095 * {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 096 */ 097 public void setMaxTotal(final int maxTotal) { 098 assertInitializationAllowed(); 099 this.maxTotal = maxTotal; 100 } 101 102 // ---------------------------------------------------------------------- 103 // Instrumentation Methods 104 105 /** 106 * Gets the number of active connections in the pool. 107 * 108 * @return The number of active connections in the pool. 109 */ 110 public int getNumActive() { 111 return pool == null ? 0 : pool.getNumActive(); 112 } 113 114 /** 115 * Gets the number of idle connections in the pool. 116 * 117 * @return The number of idle connections in the pool. 118 */ 119 public int getNumIdle() { 120 return pool == null ? 0 : pool.getNumIdle(); 121 } 122 123 // ---------------------------------------------------------------------- 124 // Inherited abstract methods 125 126 @Override 127 protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword) 128 throws SQLException { 129 130 synchronized (this) { 131 if (pool == null) { 132 try { 133 registerPool(userName, userPassword); 134 } catch (final NamingException e) { 135 throw new SQLException("RegisterPool failed", e); 136 } 137 } 138 } 139 140 PooledConnectionAndInfo info = null; 141 142 final UserPassKey key = new UserPassKey(userName, userPassword); 143 144 try { 145 info = pool.borrowObject(key); 146 } catch (final Exception e) { 147 throw new SQLException("Could not retrieve connection info from pool", e); 148 } 149 return info; 150 } 151 152 @Override 153 protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) { 154 return factory; 155 } 156 157 /** 158 * Returns a <code>SharedPoolDataSource</code> {@link Reference}. 159 */ 160 @Override 161 public Reference getReference() throws NamingException { 162 final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null); 163 ref.add(new StringRefAddr("instanceKey", getInstanceKey())); 164 return ref; 165 } 166 167 private void registerPool(final String userName, final String password) throws NamingException, SQLException { 168 169 final ConnectionPoolDataSource cpds = testCPDS(userName, password); 170 171 // Create an object pool to contain our PooledConnections 172 factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeout(), 173 isRollbackAfterValidation()); 174 factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); 175 176 final GenericKeyedObjectPoolConfig config = 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(getDefaultMinEvictableIdleTimeMillis()); 185 config.setMinIdlePerKey(getDefaultMinIdle()); 186 config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun()); 187 config.setSoftMinEvictableIdleTimeMillis(getDefaultSoftMinEvictableIdleTimeMillis()); 188 config.setTestOnCreate(getDefaultTestOnCreate()); 189 config.setTestOnBorrow(getDefaultTestOnBorrow()); 190 config.setTestOnReturn(getDefaultTestOnReturn()); 191 config.setTestWhileIdle(getDefaultTestWhileIdle()); 192 config.setTimeBetweenEvictionRunsMillis(getDefaultTimeBetweenEvictionRunsMillis()); 193 194 final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, 195 config); 196 factory.setPool(tmpPool); 197 pool = tmpPool; 198 } 199 200 @Override 201 protected void setupDefaults(final Connection connection, final String userName) throws SQLException { 202 final Boolean defaultAutoCommit = isDefaultAutoCommit(); 203 if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit.booleanValue()) { 204 connection.setAutoCommit(defaultAutoCommit.booleanValue()); 205 } 206 207 final int defaultTransactionIsolation = getDefaultTransactionIsolation(); 208 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 209 connection.setTransactionIsolation(defaultTransactionIsolation); 210 } 211 212 final Boolean defaultReadOnly = isDefaultReadOnly(); 213 if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly.booleanValue()) { 214 connection.setReadOnly(defaultReadOnly.booleanValue()); 215 } 216 } 217 218 /** 219 * Supports Serialization interface. 220 * 221 * @param in 222 * a <code>java.io.ObjectInputStream</code> value 223 * @throws IOException 224 * if an error occurs 225 * @throws ClassNotFoundException 226 * if an error occurs 227 */ 228 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 229 try { 230 in.defaultReadObject(); 231 final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory() 232 .getObjectInstance(getReference(), null, null, null); 233 this.pool = oldDS.pool; 234 } catch (final NamingException e) { 235 throw new IOException("NamingException: " + e); 236 } 237 } 238}