Coverage Report - org.apache.commons.dbcp.datasources.PerUserPoolDataSource
 
Classes in this File Line Coverage Branch Coverage Complexity
PerUserPoolDataSource
76%
142/185
68%
41/60
2.406
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  * 
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.dbcp.datasources;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.io.ObjectInputStream;
 22  
 import java.sql.Connection;
 23  
 import java.sql.SQLException;
 24  
 import java.util.HashMap;
 25  
 import java.util.Iterator;
 26  
 import java.util.Map;
 27  
 import java.util.NoSuchElementException;
 28  
 
 29  
 import javax.naming.NamingException;
 30  
 import javax.naming.Reference;
 31  
 import javax.naming.StringRefAddr;
 32  
 import javax.sql.ConnectionPoolDataSource;
 33  
 
 34  
 import org.apache.commons.dbcp.SQLNestedException;
 35  
 
 36  
 import org.apache.commons.pool.ObjectPool;
 37  
 import org.apache.commons.pool.impl.GenericObjectPool;
 38  
 
 39  
 /**
 40  
  * <p>A pooling <code>DataSource</code> appropriate for deployment within
 41  
  * J2EE environment.  There are many configuration options, most of which are
 42  
  * defined in the parent class.  This datasource uses individual pools per 
 43  
  * user, and some properties can be set specifically for a given user, if the 
 44  
  * deployment environment can support initialization of mapped properties.
 45  
  * So for example, a pool of admin or write-access Connections can be
 46  
  * guaranteed a certain number of connections, separate from a maximum
 47  
  * set for users with read-only connections.</p>
 48  
  * 
 49  
  * <p>User passwords can be changed without re-initializing the datasource.
 50  
  * When a <code>getConnection(username, password)</code> request is processed 
 51  
  * with a password that is different from those used to create connections in the
 52  
  * pool associated with <code>username</code>, an attempt is made to create a
 53  
  * new connection using the supplied password and if this succeeds, the existing
 54  
  * pool is cleared and a new pool is created for connections using the new password.</p>
 55  
  * 
 56  
  *
 57  
  * @author John D. McNally
 58  
  * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
 59  
  */
 60  
 public class PerUserPoolDataSource
 61  
     extends InstanceKeyDataSource {
 62  
 
 63  
     private static final long serialVersionUID = -3104731034410444060L;
 64  
 
 65  74
     private int defaultMaxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
 66  74
     private int defaultMaxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
 67  74
     private int defaultMaxWait = (int)Math.min(Integer.MAX_VALUE,
 68  
         GenericObjectPool.DEFAULT_MAX_WAIT);
 69  74
     Map perUserDefaultAutoCommit = null;    
 70  74
     Map perUserDefaultTransactionIsolation = null;
 71  74
     Map perUserMaxActive = null;    
 72  74
     Map perUserMaxIdle = null;    
 73  74
     Map perUserMaxWait = null;
 74  74
     Map perUserDefaultReadOnly = null;    
 75  
 
 76  
     /**
 77  
      * Map to keep track of Pools for a given user
 78  
      */
 79  74
     private transient Map /* <PoolKey, PooledConnectionManager> */ managers = new HashMap();
 80  
 
 81  
     /**
 82  
      * Default no-arg constructor for Serialization
 83  
      */
 84  74
     public PerUserPoolDataSource() {
 85  74
     }
 86  
 
 87  
     /**
 88  
      * Close pool(s) being maintained by this datasource.
 89  
      */
 90  
     public void close() {
 91  0
         for (Iterator poolIter = managers.values().iterator();
 92  0
              poolIter.hasNext();) {    
 93  
             try {
 94  0
               ((CPDSConnectionFactory) poolIter.next()).getPool().close();
 95  0
             } catch (Exception closePoolException) {
 96  
                     //ignore and try to close others.
 97  0
             }
 98  
         }
 99  0
         InstanceKeyObjectFactory.removeInstance(instanceKey);
 100  0
     }
 101  
 
 102  
     // -------------------------------------------------------------------
 103  
     // Properties
 104  
 
 105  
     /**
 106  
      * The maximum number of active connections that can be allocated from
 107  
      * this pool at the same time, or non-positive for no limit.
 108  
      * This value is used for any username which is not specified
 109  
      * in perUserMaxConnections.
 110  
      */
 111  
     public int getDefaultMaxActive() {
 112  32
         return (this.defaultMaxActive);
 113  
     }
 114  
 
 115  
     /**
 116  
      * The maximum number of active connections that can be allocated from
 117  
      * this pool at the same time, or non-positive for no limit.
 118  
      * This value is used for any username which is not specified
 119  
      * in perUserMaxConnections.  The default is 8.
 120  
      */
 121  
     public void setDefaultMaxActive(int maxActive) {
 122  70
         assertInitializationAllowed();
 123  70
         this.defaultMaxActive = maxActive;
 124  70
     }
 125  
 
 126  
     /**
 127  
      * The maximum number of active connections that can remain idle in the
 128  
      * pool, without extra ones being released, or negative for no limit.
 129  
      * This value is used for any username which is not specified
 130  
      * in perUserMaxIdle.
 131  
      */
 132  
     public int getDefaultMaxIdle() {
 133  84
         return (this.defaultMaxIdle);
 134  
     }
 135  
 
 136  
     /**
 137  
      * The maximum number of active connections that can remain idle in the
 138  
      * pool, without extra ones being released, or negative for no limit.
 139  
      * This value is used for any username which is not specified
 140  
      * in perUserMaxIdle.  The default is 8.
 141  
      */
 142  
     public void setDefaultMaxIdle(int defaultMaxIdle) {
 143  0
         assertInitializationAllowed();
 144  0
         this.defaultMaxIdle = defaultMaxIdle;
 145  0
     }
 146  
 
 147  
     /**
 148  
      * The maximum number of milliseconds that the pool will wait (when there
 149  
      * are no available connections) for a connection to be returned before
 150  
      * throwing an exception, or -1 to wait indefinitely.  Will fail 
 151  
      * immediately if value is 0.
 152  
      * This value is used for any username which is not specified
 153  
      * in perUserMaxWait.  The default is -1.
 154  
      */
 155  
     public int getDefaultMaxWait() {
 156  40
         return (this.defaultMaxWait);
 157  
     }
 158  
 
 159  
     /**
 160  
      * The maximum number of milliseconds that the pool will wait (when there
 161  
      * are no available connections) for a connection to be returned before
 162  
      * throwing an exception, or -1 to wait indefinitely.  Will fail 
 163  
      * immediately if value is 0.
 164  
      * This value is used for any username which is not specified
 165  
      * in perUserMaxWait.  The default is -1.
 166  
      */
 167  
     public void setDefaultMaxWait(int defaultMaxWait) {
 168  76
         assertInitializationAllowed();
 169  76
         this.defaultMaxWait = defaultMaxWait;
 170  76
     }
 171  
 
 172  
     /**
 173  
      * The keys are usernames and the value is the --.  Any 
 174  
      * username specified here will override the value of defaultAutoCommit.
 175  
      */
 176  
     public Boolean getPerUserDefaultAutoCommit(String key) {
 177  4265
         Boolean value = null;
 178  4265
         if (perUserDefaultAutoCommit != null) {
 179  0
             value = (Boolean) perUserDefaultAutoCommit.get(key);
 180  
         }
 181  4265
         return value;
 182  
     }
 183  
     
 184  
     /**
 185  
      * The keys are usernames and the value is the --.  Any 
 186  
      * username specified here will override the value of defaultAutoCommit.
 187  
      */
 188  
     public void setPerUserDefaultAutoCommit(String username, Boolean value) {
 189  0
         assertInitializationAllowed();
 190  0
         if (perUserDefaultAutoCommit == null) {
 191  0
             perUserDefaultAutoCommit = new HashMap();
 192  
         }
 193  0
         perUserDefaultAutoCommit.put(username, value);
 194  0
     }
 195  
 
 196  
     /**
 197  
      * The isolation level of connections when returned from getConnection.  
 198  
      * If null, the username will use the value of defaultTransactionIsolation.
 199  
      */
 200  
     public Integer getPerUserDefaultTransactionIsolation(String username) {
 201  4265
         Integer value = null;
 202  4265
         if (perUserDefaultTransactionIsolation != null) {
 203  0
             value = (Integer) perUserDefaultTransactionIsolation.get(username);
 204  
         }
 205  4265
         return value;
 206  
     }
 207  
 
 208  
     /**
 209  
      * The isolation level of connections when returned from getConnection.  
 210  
      * Valid values are the constants defined in Connection.
 211  
      */
 212  
     public void setPerUserDefaultTransactionIsolation(String username, 
 213  
                                                       Integer value) {
 214  0
         assertInitializationAllowed();
 215  0
         if (perUserDefaultTransactionIsolation == null) {
 216  0
             perUserDefaultTransactionIsolation = new HashMap();
 217  
         }
 218  0
         perUserDefaultTransactionIsolation.put(username, value);
 219  0
     }
 220  
 
 221  
     /**
 222  
      * The maximum number of active connections that can be allocated from
 223  
      * this pool at the same time, or non-positive for no limit.
 224  
      * The keys are usernames and the value is the maximum connections.  Any 
 225  
      * username specified here will override the value of defaultMaxActive.
 226  
      */
 227  
     public Integer getPerUserMaxActive(String username) {
 228  86
         Integer value = null;
 229  86
         if (perUserMaxActive != null) {
 230  86
             value = (Integer) perUserMaxActive.get(username);
 231  
         }
 232  86
         return value;
 233  
     }
 234  
     
 235  
     /**
 236  
      * The maximum number of active connections that can be allocated from
 237  
      * this pool at the same time, or non-positive for no limit.
 238  
      * The keys are usernames and the value is the maximum connections.  Any 
 239  
      * username specified here will override the value of defaultMaxActive.
 240  
      */
 241  
     public void setPerUserMaxActive(String username, Integer value) {
 242  82
         assertInitializationAllowed();
 243  82
         if (perUserMaxActive == null) {
 244  72
             perUserMaxActive = new HashMap();
 245  
         }
 246  82
         perUserMaxActive.put(username, value);
 247  82
     }
 248  
 
 249  
 
 250  
     /**
 251  
      * The maximum number of active connections that can remain idle in the
 252  
      * pool, without extra ones being released, or negative for no limit.
 253  
      * The keys are usernames and the value is the maximum connections.  Any 
 254  
      * username specified here will override the value of defaultMaxIdle.
 255  
      */
 256  
     public Integer getPerUserMaxIdle(String username) {
 257  86
         Integer value = null;
 258  86
         if (perUserMaxIdle != null) {
 259  2
             value = (Integer) perUserMaxIdle.get(username);
 260  
         }
 261  86
         return value;
 262  
     }
 263  
     
 264  
     /**
 265  
      * The maximum number of active connections that can remain idle in the
 266  
      * pool, without extra ones being released, or negative for no limit.
 267  
      * The keys are usernames and the value is the maximum connections.  Any 
 268  
      * username specified here will override the value of defaultMaxIdle.
 269  
      */
 270  
     public void setPerUserMaxIdle(String username, Integer value) {
 271  2
         assertInitializationAllowed();
 272  2
         if (perUserMaxIdle == null) {
 273  2
             perUserMaxIdle = new HashMap();
 274  
         }
 275  2
         perUserMaxIdle.put(username, value);
 276  2
     }
 277  
     
 278  
     /**
 279  
      * The maximum number of milliseconds that the pool will wait (when there
 280  
      * are no available connections) for a connection to be returned before
 281  
      * throwing an exception, or -1 to wait indefinitely.  Will fail 
 282  
      * immediately if value is 0.
 283  
      * The keys are usernames and the value is the maximum connections.  Any 
 284  
      * username specified here will override the value of defaultMaxWait.
 285  
      */
 286  
     public Integer getPerUserMaxWait(String username) {
 287  86
         Integer value = null;
 288  86
         if (perUserMaxWait != null) {
 289  86
             value = (Integer) perUserMaxWait.get(username);
 290  
         }
 291  86
         return value;
 292  
     }
 293  
     
 294  
     /**
 295  
      * The maximum number of milliseconds that the pool will wait (when there
 296  
      * are no available connections) for a connection to be returned before
 297  
      * throwing an exception, or -1 to wait indefinitely.  Will fail 
 298  
      * immediately if value is 0.
 299  
      * The keys are usernames and the value is the maximum connections.  Any 
 300  
      * username specified here will override the value of defaultMaxWait.
 301  
      */
 302  
     public void setPerUserMaxWait(String username, Integer value) {
 303  76
         assertInitializationAllowed();
 304  76
         if (perUserMaxWait == null) {
 305  72
             perUserMaxWait = new HashMap();
 306  
         }
 307  76
         perUserMaxWait.put(username, value);
 308  76
     }
 309  
 
 310  
     /**
 311  
      * The keys are usernames and the value is the --.  Any 
 312  
      * username specified here will override the value of defaultReadOnly.
 313  
      */
 314  
     public Boolean getPerUserDefaultReadOnly(String username) {
 315  4265
         Boolean value = null;
 316  4265
         if (perUserDefaultReadOnly != null) {
 317  0
             value = (Boolean) perUserDefaultReadOnly.get(username);
 318  
         }
 319  4265
         return value;
 320  
     }
 321  
     
 322  
     /**
 323  
      * The keys are usernames and the value is the --.  Any 
 324  
      * username specified here will override the value of defaultReadOnly.
 325  
      */
 326  
     public void setPerUserDefaultReadOnly(String username, Boolean value) {
 327  0
         assertInitializationAllowed();
 328  0
         if (perUserDefaultReadOnly == null) {
 329  0
             perUserDefaultReadOnly = new HashMap();
 330  
         }
 331  0
         perUserDefaultReadOnly.put(username, value);
 332  0
     }
 333  
 
 334  
     // ----------------------------------------------------------------------
 335  
     // Instrumentation Methods
 336  
 
 337  
     /**
 338  
      * Get the number of active connections in the default pool.
 339  
      */
 340  
     public int getNumActive() {
 341  20
         return getNumActive(null, null);
 342  
     }
 343  
 
 344  
     /**
 345  
      * Get the number of active connections in the pool for a given user.
 346  
      */
 347  
     public int getNumActive(String username, String password) {
 348  46
         ObjectPool pool = getPool(getPoolKey(username,password));
 349  46
         return (pool == null) ? 0 : pool.getNumActive();
 350  
     }
 351  
 
 352  
     /**
 353  
      * Get the number of idle connections in the default pool.
 354  
      */
 355  
     public int getNumIdle() {
 356  22
         return getNumIdle(null, null);
 357  
     }
 358  
 
 359  
     /**
 360  
      * Get the number of idle connections in the pool for a given user.
 361  
      */
 362  
     public int getNumIdle(String username, String password) {
 363  58
         ObjectPool pool = getPool(getPoolKey(username,password));
 364  58
         return (pool == null) ? 0 : pool.getNumIdle();
 365  
     }
 366  
 
 367  
 
 368  
     // ----------------------------------------------------------------------
 369  
     // Inherited abstract methods
 370  
 
 371  
     protected PooledConnectionAndInfo 
 372  
         getPooledConnectionAndInfo(String username, String password)
 373  
         throws SQLException {
 374  
 
 375  4377
         final PoolKey key = getPoolKey(username,password);
 376  
         ObjectPool pool;
 377  
         PooledConnectionManager manager;
 378  4377
         synchronized(this) {
 379  4377
             manager = (PooledConnectionManager) managers.get(key);
 380  4377
             if (manager == null) {
 381  
                 try {
 382  90
                     registerPool(username, password);
 383  84
                     manager = (PooledConnectionManager) managers.get(key);
 384  0
                 } catch (NamingException e) {
 385  0
                     throw new SQLNestedException("RegisterPool failed", e);
 386  84
                 }
 387  
             }
 388  4371
             pool = ((CPDSConnectionFactory) manager).getPool();
 389  4371
         }
 390  
 
 391  4371
         PooledConnectionAndInfo info = null;
 392  
         try {
 393  4371
             info = (PooledConnectionAndInfo) pool.borrowObject();
 394  
         }
 395  24
         catch (NoSuchElementException ex) {
 396  24
             throw new SQLNestedException(
 397  
                     "Could not retrieve connection info from pool", ex);
 398  
         }
 399  2
         catch (Exception e) {
 400  
             // See if failure is due to CPDSConnectionFactory authentication failure
 401  
             try {
 402  2
                 testCPDS(username, password);
 403  0
             } catch (Exception ex) {
 404  0
                 throw (SQLException) new SQLException(
 405  
                         "Could not retrieve connection info from pool").initCause(ex);
 406  2
             }
 407  
             // New password works, so kill the old pool, create a new one, and borrow
 408  2
             manager.closePool(username);
 409  2
             synchronized (this) {
 410  2
                 managers.remove(key);
 411  2
             }
 412  
             try {
 413  2
                 registerPool(username, password);
 414  2
                 pool = getPool(key);
 415  0
             } catch (NamingException ne) {
 416  0
                 throw new SQLNestedException("RegisterPool failed", ne);
 417  2
             }
 418  
             try {
 419  2
                 info = (PooledConnectionAndInfo)((ObjectPool) pool).borrowObject();
 420  0
             } catch (Exception ex) {
 421  0
                 throw (SQLException) new SQLException(
 422  
                 "Could not retrieve connection info from pool").initCause(ex);
 423  2
             }
 424  4345
         }
 425  4347
         return info;
 426  
     }
 427  
 
 428  
     protected void setupDefaults(Connection con, String username) 
 429  
         throws SQLException {
 430  4339
         boolean defaultAutoCommit = isDefaultAutoCommit();
 431  4339
         if (username != null) {
 432  4265
             Boolean userMax = getPerUserDefaultAutoCommit(username);
 433  4265
             if (userMax != null) {
 434  0
                 defaultAutoCommit = userMax.booleanValue();
 435  
             }
 436  
         }    
 437  
 
 438  4339
         boolean defaultReadOnly = isDefaultReadOnly();
 439  4339
         if (username != null) {
 440  4265
             Boolean userMax = getPerUserDefaultReadOnly(username);
 441  4265
             if (userMax != null) {
 442  0
                 defaultReadOnly = userMax.booleanValue();
 443  
             }
 444  
         }    
 445  
 
 446  4339
         int defaultTransactionIsolation = getDefaultTransactionIsolation();
 447  4339
         if (username != null) {
 448  4265
             Integer userMax = getPerUserDefaultTransactionIsolation(username);
 449  4265
             if (userMax != null) {
 450  0
                 defaultTransactionIsolation = userMax.intValue();
 451  
             }
 452  
         }
 453  
 
 454  4339
         if (con.getAutoCommit() != defaultAutoCommit) {
 455  2
             con.setAutoCommit(defaultAutoCommit);
 456  
         }
 457  
 
 458  4339
         if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
 459  4333
             con.setTransactionIsolation(defaultTransactionIsolation);
 460  
         }
 461  
 
 462  4339
         if (con.isReadOnly() != defaultReadOnly) {
 463  0
             con.setReadOnly(defaultReadOnly);
 464  
         }
 465  4339
     }
 466  
     
 467  
     protected PooledConnectionManager getConnectionManager(UserPassKey upkey) {
 468  2
         return (PooledConnectionManager) managers.get(getPoolKey(
 469  
                 upkey.getUsername(), upkey.getPassword()));
 470  
     }
 471  
 
 472  
     /**
 473  
      * Returns a <code>PerUserPoolDataSource</code> {@link Reference}.
 474  
      * 
 475  
      * @since 1.2.2
 476  
      */
 477  
     public Reference getReference() throws NamingException {
 478  4
         Reference ref = new Reference(getClass().getName(),
 479  
                 PerUserPoolDataSourceFactory.class.getName(), null);
 480  4
         ref.add(new StringRefAddr("instanceKey", instanceKey));
 481  4
         return ref;
 482  
     }
 483  
     
 484  
     private PoolKey getPoolKey(String username, String password) { 
 485  4569
         return new PoolKey(getDataSourceName(), username); 
 486  
     }
 487  
 
 488  
     private synchronized void registerPool(
 489  
         String username, String password) 
 490  
         throws javax.naming.NamingException, SQLException {
 491  
 
 492  92
         ConnectionPoolDataSource cpds = testCPDS(username, password);
 493  
 
 494  86
         Integer userMax = getPerUserMaxActive(username);
 495  86
         int maxActive = (userMax == null) ? 
 496  
             getDefaultMaxActive() : userMax.intValue();
 497  86
         userMax = getPerUserMaxIdle(username);
 498  86
         int maxIdle =  (userMax == null) ?
 499  
             getDefaultMaxIdle() : userMax.intValue();
 500  86
         userMax = getPerUserMaxWait(username);
 501  86
         int maxWait = (userMax == null) ?
 502  
             getDefaultMaxWait() : userMax.intValue();
 503  
 
 504  
         // Create an object pool to contain our PooledConnections
 505  86
         GenericObjectPool pool = new GenericObjectPool(null);
 506  86
         pool.setMaxActive(maxActive);
 507  86
         pool.setMaxIdle(maxIdle);
 508  86
         pool.setMaxWait(maxWait);
 509  86
         pool.setWhenExhaustedAction(whenExhaustedAction(maxActive, maxWait));
 510  86
         pool.setTestOnBorrow(getTestOnBorrow());
 511  86
         pool.setTestOnReturn(getTestOnReturn());
 512  86
         pool.setTimeBetweenEvictionRunsMillis(
 513  
             getTimeBetweenEvictionRunsMillis());
 514  86
         pool.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
 515  86
         pool.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
 516  86
         pool.setTestWhileIdle(getTestWhileIdle());
 517  
                 
 518  
         // Set up the factory we will use (passing the pool associates
 519  
         // the factory with the pool, so we do not have to do so
 520  
         // explicitly)
 521  86
         CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, pool, getValidationQuery(),
 522  
                 isRollbackAfterValidation(), username, password);
 523  
            
 524  86
         Object old = managers.put(getPoolKey(username,password), factory);
 525  86
         if (old != null) {
 526  0
             throw new IllegalStateException("Pool already contains an entry for this user/password: "+username);
 527  
         }
 528  86
     }
 529  
 
 530  
     /**
 531  
      * Supports Serialization interface.
 532  
      *
 533  
      * @param in a <code>java.io.ObjectInputStream</code> value
 534  
      * @exception IOException if an error occurs
 535  
      * @exception ClassNotFoundException if an error occurs
 536  
      */
 537  
     private void readObject(ObjectInputStream in)
 538  
         throws IOException, ClassNotFoundException {
 539  
         try 
 540  
         {
 541  2
             in.defaultReadObject();
 542  2
             PerUserPoolDataSource oldDS = (PerUserPoolDataSource)
 543  
                 new PerUserPoolDataSourceFactory()
 544  
                     .getObjectInstance(getReference(), null, null, null);
 545  2
             this.managers = oldDS.managers;
 546  
         }
 547  0
         catch (NamingException e)
 548  
         {
 549  0
             throw new IOException("NamingException: " + e);
 550  2
         }
 551  2
     }
 552  
     
 553  
     /**
 554  
      * Returns the object pool associated with the given PoolKey.
 555  
      * 
 556  
      * @param key PoolKey identifying the pool 
 557  
      * @return the GenericObjectPool pooling connections for the username and datasource
 558  
      * specified by the PoolKey
 559  
      */
 560  
     private GenericObjectPool getPool(PoolKey key) {
 561  106
         CPDSConnectionFactory mgr = (CPDSConnectionFactory) managers.get(key);
 562  106
         return mgr == null ? null : (GenericObjectPool) mgr.getPool();
 563  
     }
 564  
 }