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 @Override 080 protected PooledConnectionManager getConnectionManager(final UserPassKey userPassKey) { 081 return factory; 082 } 083 084 /** 085 * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 086 * 087 * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 088 */ 089 public int getMaxTotal() { 090 return this.maxTotal; 091 } 092 093 // ---------------------------------------------------------------------- 094 // Instrumentation Methods 095 096 /** 097 * Gets the number of active connections in the pool. 098 * 099 * @return The number of active connections in the pool. 100 */ 101 public int getNumActive() { 102 return pool == null ? 0 : pool.getNumActive(); 103 } 104 105 /** 106 * Gets the number of idle connections in the pool. 107 * 108 * @return The number of idle connections in the pool. 109 */ 110 public int getNumIdle() { 111 return pool == null ? 0 : pool.getNumIdle(); 112 } 113 114 @Override 115 protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword) 116 throws SQLException { 117 118 synchronized (this) { 119 if (pool == null) { 120 try { 121 registerPool(userName, userPassword); 122 } catch (final NamingException e) { 123 throw new SQLException("registerPool failed", e); 124 } 125 } 126 } 127 128 try { 129 return pool.borrowObject(new UserPassKey(userName, userPassword)); 130 } catch (final Exception e) { 131 throw new SQLException("Could not retrieve connection info from pool", e); 132 } 133 } 134 135 /** 136 * Creates a new {@link Reference} to a {@link SharedPoolDataSource}. 137 */ 138 @Override 139 public Reference getReference() throws NamingException { 140 final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null); 141 ref.add(new StringRefAddr("instanceKey", getInstanceKey())); 142 return ref; 143 } 144 145 /** 146 * Supports Serialization interface. 147 * 148 * @param in 149 * a <code>java.io.ObjectInputStream</code> value 150 * @throws IOException 151 * if an error occurs 152 * @throws ClassNotFoundException 153 * if an error occurs 154 */ 155 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 156 try { 157 in.defaultReadObject(); 158 final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory() 159 .getObjectInstance(getReference(), null, null, null); 160 this.pool = oldDS.pool; 161 } catch (final NamingException e) { 162 throw new IOException("NamingException: " + e); 163 } 164 } 165 166 private void registerPool(final String userName, final String password) throws NamingException, SQLException { 167 168 final ConnectionPoolDataSource cpds = testCPDS(userName, password); 169 170 // Create an object pool to contain our PooledConnections 171 factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeout(), 172 isRollbackAfterValidation()); 173 factory.setMaxConnLifetime(getMaxConnLifetime()); 174 175 final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = 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(getDefaultMaxWait().toMillis()); 183 config.setMinEvictableIdleTimeMillis(getDefaultMinEvictableIdleTimeMillis()); 184 config.setMinIdlePerKey(getDefaultMinIdle()); 185 config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun()); 186 config.setSoftMinEvictableIdleTimeMillis(getDefaultSoftMinEvictableIdleTimeMillis()); 187 config.setTestOnCreate(getDefaultTestOnCreate()); 188 config.setTestOnBorrow(getDefaultTestOnBorrow()); 189 config.setTestOnReturn(getDefaultTestOnReturn()); 190 config.setTestWhileIdle(getDefaultTestWhileIdle()); 191 config.setTimeBetweenEvictionRunsMillis(getDefaultTimeBetweenEvictionRunsMillis()); 192 193 final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, 194 config); 195 factory.setPool(tmpPool); 196 pool = tmpPool; 197 } 198 199 /** 200 * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 201 * 202 * @param maxTotal 203 * {@link GenericKeyedObjectPool#getMaxTotal()} for this pool. 204 */ 205 public void setMaxTotal(final int maxTotal) { 206 assertInitializationAllowed(); 207 this.maxTotal = maxTotal; 208 } 209 210 @Override 211 protected void setupDefaults(final Connection connection, final String userName) throws SQLException { 212 final Boolean defaultAutoCommit = isDefaultAutoCommit(); 213 if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit) { 214 connection.setAutoCommit(defaultAutoCommit); 215 } 216 217 final int defaultTransactionIsolation = getDefaultTransactionIsolation(); 218 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 219 connection.setTransactionIsolation(defaultTransactionIsolation); 220 } 221 222 final Boolean defaultReadOnly = isDefaultReadOnly(); 223 if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly) { 224 connection.setReadOnly(defaultReadOnly); 225 } 226 } 227 228 @Override 229 protected void toStringFields(final StringBuilder builder) { 230 super.toStringFields(builder); 231 builder.append(", maxTotal="); 232 builder.append(maxTotal); 233 } 234}