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 */ 017package org.apache.commons.dbcp2.datasources; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.sql.Connection; 022import java.sql.SQLException; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.NoSuchElementException; 026 027import javax.naming.NamingException; 028import javax.naming.Reference; 029import javax.naming.StringRefAddr; 030import javax.sql.ConnectionPoolDataSource; 031 032import org.apache.commons.dbcp2.SwallowedExceptionLogger; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.apache.commons.pool2.ObjectPool; 036import org.apache.commons.pool2.impl.GenericObjectPool; 037 038/** 039 * <p>A pooling <code>DataSource</code> appropriate for deployment within 040 * J2EE environment. There are many configuration options, most of which are 041 * defined in the parent class. This datasource uses individual pools per 042 * user, and some properties can be set specifically for a given user, if the 043 * deployment environment can support initialization of mapped properties. 044 * So for example, a pool of admin or write-access Connections can be 045 * guaranteed a certain number of connections, separate from a maximum 046 * set for users with read-only connections.</p> 047 * 048 * <p>User passwords can be changed without re-initializing the datasource. 049 * When a <code>getConnection(username, password)</code> request is processed 050 * with a password that is different from those used to create connections in 051 * the pool associated with <code>username</code>, an attempt is made to create 052 * a new connection using the supplied password and if this succeeds, the 053 * existing pool is cleared and a new pool is created for connections using the 054 * new password.</p> 055 * 056 * @author John D. McNally 057 * @since 2.0 058 */ 059public class PerUserPoolDataSource extends InstanceKeyDataSource { 060 061 private static final long serialVersionUID = 7872747993848065028L; 062 063 private static final Log log = 064 LogFactory.getLog(PerUserPoolDataSource.class); 065 066 // Per user pool properties 067 private Map<String,Boolean> perUserBlockWhenExhausted = null; 068 private Map<String,String> perUserEvictionPolicyClassName = null; 069 private Map<String,Boolean> perUserLifo = null; 070 private Map<String,Integer> perUserMaxIdle = null; 071 private Map<String,Integer> perUserMaxTotal = null; 072 private Map<String,Long> perUserMaxWaitMillis = null; 073 private Map<String,Long> perUserMinEvictableIdleTimeMillis = null; 074 private Map<String,Integer> perUserMinIdle = null; 075 private Map<String,Integer> perUserNumTestsPerEvictionRun = null; 076 private Map<String,Long> perUserSoftMinEvictableIdleTimeMillis = null; 077 private Map<String,Boolean> perUserTestOnCreate = null; 078 private Map<String,Boolean> perUserTestOnBorrow = null; 079 private Map<String,Boolean> perUserTestOnReturn = null; 080 private Map<String,Boolean> perUserTestWhileIdle = null; 081 private Map<String,Long> perUserTimeBetweenEvictionRunsMillis = null; 082 083 // Per user connection properties 084 private Map<String,Boolean> perUserDefaultAutoCommit = null; 085 private Map<String,Integer> perUserDefaultTransactionIsolation = null; 086 private Map<String,Boolean> perUserDefaultReadOnly = null; 087 088 /** 089 * Map to keep track of Pools for a given user 090 */ 091 private transient Map<PoolKey, PooledConnectionManager> managers = 092 new HashMap<>(); 093 094 /** 095 * Default no-arg constructor for Serialization 096 */ 097 public PerUserPoolDataSource() { 098 } 099 100 /** 101 * Clears pool(s) maintained by this data source. 102 * 103 * @see org.apache.commons.pool2.ObjectPool#clear() 104 * @since 2.3.0 105 */ 106 public void clear() { 107 for (final PooledConnectionManager manager : managers.values()) { 108 try { 109 ((CPDSConnectionFactory) manager).getPool().clear(); 110 } catch (final Exception closePoolException) { 111 //ignore and try to close others. 112 } 113 } 114 InstanceKeyDataSourceFactory.removeInstance(getInstanceKey()); 115 } 116 117 /** 118 * Closes pool(s) maintained by this data source. 119 * 120 * @see org.apache.commons.pool2.ObjectPool#close() 121 */ 122 @Override 123 public void close() { 124 for (final PooledConnectionManager manager : managers.values()) { 125 try { 126 ((CPDSConnectionFactory) manager).getPool().close(); 127 } catch (final Exception closePoolException) { 128 //ignore and try to close others. 129 } 130 } 131 InstanceKeyDataSourceFactory.removeInstance(getInstanceKey()); 132 } 133 134 // ------------------------------------------------------------------- 135 // Properties 136 137 /** 138 * Gets the user specific value for 139 * {@link GenericObjectPool#getBlockWhenExhausted()} for the 140 * specified user's pool or the default if no user specific value is defined. 141 */ 142 public boolean getPerUserBlockWhenExhausted(final String key) { 143 Boolean value = null; 144 if (perUserBlockWhenExhausted != null) { 145 value = perUserBlockWhenExhausted.get(key); 146 } 147 if (value == null) { 148 return getDefaultBlockWhenExhausted(); 149 } 150 return value.booleanValue(); 151 } 152 153 /** 154 * Sets a user specific value for 155 * {@link GenericObjectPool#getBlockWhenExhausted()} for the specified 156 * user's pool. 157 */ 158 public void setPerUserBlockWhenExhausted(final String username, 159 final Boolean value) { 160 assertInitializationAllowed(); 161 if (perUserBlockWhenExhausted == null) { 162 perUserBlockWhenExhausted = new HashMap<>(); 163 } 164 perUserBlockWhenExhausted.put(username, value); 165 } 166 167 void setPerUserBlockWhenExhausted( 168 final Map<String,Boolean> userDefaultBlockWhenExhausted) { 169 assertInitializationAllowed(); 170 if (perUserBlockWhenExhausted == null) { 171 perUserBlockWhenExhausted = new HashMap<>(); 172 } else { 173 perUserBlockWhenExhausted.clear(); 174 } 175 perUserBlockWhenExhausted.putAll(userDefaultBlockWhenExhausted); 176 } 177 178 179 /** 180 * Gets the user specific value for 181 * {@link GenericObjectPool#getEvictionPolicyClassName()} for the 182 * specified user's pool or the default if no user specific value is defined. 183 */ 184 public String getPerUserEvictionPolicyClassName(final String key) { 185 String value = null; 186 if (perUserEvictionPolicyClassName != null) { 187 value = perUserEvictionPolicyClassName.get(key); 188 } 189 if (value == null) { 190 return getDefaultEvictionPolicyClassName(); 191 } 192 return value; 193 } 194 195 /** 196 * Sets a user specific value for 197 * {@link GenericObjectPool#getEvictionPolicyClassName()} for the specified 198 * user's pool. 199 */ 200 public void setPerUserEvictionPolicyClassName(final String username, 201 final String value) { 202 assertInitializationAllowed(); 203 if (perUserEvictionPolicyClassName == null) { 204 perUserEvictionPolicyClassName = new HashMap<>(); 205 } 206 perUserEvictionPolicyClassName.put(username, value); 207 } 208 209 void setPerUserEvictionPolicyClassName( 210 final Map<String,String> userDefaultEvictionPolicyClassName) { 211 assertInitializationAllowed(); 212 if (perUserEvictionPolicyClassName == null) { 213 perUserEvictionPolicyClassName = new HashMap<>(); 214 } else { 215 perUserEvictionPolicyClassName.clear(); 216 } 217 perUserEvictionPolicyClassName.putAll(userDefaultEvictionPolicyClassName); 218 } 219 220 221 /** 222 * Gets the user specific value for {@link GenericObjectPool#getLifo()} for 223 * the specified user's pool or the default if no user specific value is 224 * defined. 225 */ 226 public boolean getPerUserLifo(final String key) { 227 Boolean value = null; 228 if (perUserLifo != null) { 229 value = perUserLifo.get(key); 230 } 231 if (value == null) { 232 return getDefaultLifo(); 233 } 234 return value.booleanValue(); 235 } 236 237 /** 238 * Sets a user specific value for 239 * {@link GenericObjectPool#getLifo()} for the specified 240 * user's pool. 241 */ 242 public void setPerUserLifo(final String username, final Boolean value) { 243 assertInitializationAllowed(); 244 if (perUserLifo == null) { 245 perUserLifo = new HashMap<>(); 246 } 247 perUserLifo.put(username, value); 248 } 249 250 void setPerUserLifo(final Map<String,Boolean> userDefaultLifo) { 251 assertInitializationAllowed(); 252 if (perUserLifo == null) { 253 perUserLifo = new HashMap<>(); 254 } else { 255 perUserLifo.clear(); 256 } 257 perUserLifo.putAll(userDefaultLifo); 258 } 259 260 261 /** 262 * Gets the user specific value for 263 * {@link GenericObjectPool#getMaxIdle()} for the 264 * specified user's pool or the default if no user specific value is defined. 265 */ 266 public int getPerUserMaxIdle(final String key) { 267 Integer value = null; 268 if (perUserMaxIdle != null) { 269 value = perUserMaxIdle.get(key); 270 } 271 if (value == null) { 272 return getDefaultMaxIdle(); 273 } 274 return value.intValue(); 275 } 276 277 /** 278 * Sets a user specific value for 279 * {@link GenericObjectPool#getMaxIdle()} for the specified 280 * user's pool. 281 */ 282 public void setPerUserMaxIdle(final String username, final Integer value) { 283 assertInitializationAllowed(); 284 if (perUserMaxIdle == null) { 285 perUserMaxIdle = new HashMap<>(); 286 } 287 perUserMaxIdle.put(username, value); 288 } 289 290 void setPerUserMaxIdle(final Map<String,Integer> userDefaultMaxIdle) { 291 assertInitializationAllowed(); 292 if (perUserMaxIdle == null) { 293 perUserMaxIdle = new HashMap<>(); 294 } else { 295 perUserMaxIdle.clear(); 296 } 297 perUserMaxIdle.putAll(userDefaultMaxIdle); 298 } 299 300 301 /** 302 * Gets the user specific value for 303 * {@link GenericObjectPool#getMaxTotal()} for the 304 * specified user's pool or the default if no user specific value is defined. 305 */ 306 public int getPerUserMaxTotal(final String key) { 307 Integer value = null; 308 if (perUserMaxTotal != null) { 309 value = perUserMaxTotal.get(key); 310 } 311 if (value == null) { 312 return getDefaultMaxTotal(); 313 } 314 return value.intValue(); 315 } 316 317 /** 318 * Sets a user specific value for 319 * {@link GenericObjectPool#getMaxTotal()} for the specified 320 * user's pool. 321 */ 322 public void setPerUserMaxTotal(final String username, final Integer value) { 323 assertInitializationAllowed(); 324 if (perUserMaxTotal == null) { 325 perUserMaxTotal = new HashMap<>(); 326 } 327 perUserMaxTotal.put(username, value); 328 } 329 330 void setPerUserMaxTotal(final Map<String,Integer> userDefaultMaxTotal) { 331 assertInitializationAllowed(); 332 if (perUserMaxTotal == null) { 333 perUserMaxTotal = new HashMap<>(); 334 } else { 335 perUserMaxTotal.clear(); 336 } 337 perUserMaxTotal.putAll(userDefaultMaxTotal); 338 } 339 340 341 /** 342 * Gets the user specific value for 343 * {@link GenericObjectPool#getMaxWaitMillis()} for the 344 * specified user's pool or the default if no user specific value is defined. 345 */ 346 public long getPerUserMaxWaitMillis(final String key) { 347 Long value = null; 348 if (perUserMaxWaitMillis != null) { 349 value = perUserMaxWaitMillis.get(key); 350 } 351 if (value == null) { 352 return getDefaultMaxWaitMillis(); 353 } 354 return value.longValue(); 355 } 356 357 /** 358 * Sets a user specific value for 359 * {@link GenericObjectPool#getMaxWaitMillis()} for the specified 360 * user's pool. 361 */ 362 public void setPerUserMaxWaitMillis(final String username, final Long value) { 363 assertInitializationAllowed(); 364 if (perUserMaxWaitMillis == null) { 365 perUserMaxWaitMillis = new HashMap<>(); 366 } 367 perUserMaxWaitMillis.put(username, value); 368 } 369 370 void setPerUserMaxWaitMillis( 371 final Map<String,Long> userDefaultMaxWaitMillis) { 372 assertInitializationAllowed(); 373 if (perUserMaxWaitMillis == null) { 374 perUserMaxWaitMillis = new HashMap<>(); 375 } else { 376 perUserMaxWaitMillis.clear(); 377 } 378 perUserMaxWaitMillis.putAll(userDefaultMaxWaitMillis); 379 } 380 381 382 /** 383 * Gets the user specific value for 384 * {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the 385 * specified user's pool or the default if no user specific value is defined. 386 */ 387 public long getPerUserMinEvictableIdleTimeMillis(final String key) { 388 Long value = null; 389 if (perUserMinEvictableIdleTimeMillis != null) { 390 value = perUserMinEvictableIdleTimeMillis.get(key); 391 } 392 if (value == null) { 393 return getDefaultMinEvictableIdleTimeMillis(); 394 } 395 return value.longValue(); 396 } 397 398 /** 399 * Sets a user specific value for 400 * {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the 401 * specified user's pool. 402 */ 403 public void setPerUserMinEvictableIdleTimeMillis(final String username, 404 final Long value) { 405 assertInitializationAllowed(); 406 if (perUserMinEvictableIdleTimeMillis == null) { 407 perUserMinEvictableIdleTimeMillis = new HashMap<>(); 408 } 409 perUserMinEvictableIdleTimeMillis.put(username, value); 410 } 411 412 void setPerUserMinEvictableIdleTimeMillis( 413 final Map<String,Long> userDefaultMinEvictableIdleTimeMillis) { 414 assertInitializationAllowed(); 415 if (perUserMinEvictableIdleTimeMillis == null) { 416 perUserMinEvictableIdleTimeMillis = new HashMap<>(); 417 } else { 418 perUserMinEvictableIdleTimeMillis.clear(); 419 } 420 perUserMinEvictableIdleTimeMillis.putAll( 421 userDefaultMinEvictableIdleTimeMillis); 422 } 423 424 425 /** 426 * Gets the user specific value for 427 * {@link GenericObjectPool#getMinIdle()} for the 428 * specified user's pool or the default if no user specific value is defined. 429 */ 430 public int getPerUserMinIdle(final String key) { 431 Integer value = null; 432 if (perUserMinIdle != null) { 433 value = perUserMinIdle.get(key); 434 } 435 if (value == null) { 436 return getDefaultMinIdle(); 437 } 438 return value.intValue(); 439 } 440 441 /** 442 * Sets a user specific value for 443 * {@link GenericObjectPool#getMinIdle()} for the specified 444 * user's pool. 445 */ 446 public void setPerUserMinIdle(final String username, final Integer value) { 447 assertInitializationAllowed(); 448 if (perUserMinIdle == null) { 449 perUserMinIdle = new HashMap<>(); 450 } 451 perUserMinIdle.put(username, value); 452 } 453 454 void setPerUserMinIdle(final Map<String,Integer> userDefaultMinIdle) { 455 assertInitializationAllowed(); 456 if (perUserMinIdle == null) { 457 perUserMinIdle = new HashMap<>(); 458 } else { 459 perUserMinIdle.clear(); 460 } 461 perUserMinIdle.putAll(userDefaultMinIdle); 462 } 463 464 465 /** 466 * Gets the user specific value for 467 * {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the 468 * specified user's pool or the default if no user specific value is defined. 469 */ 470 public int getPerUserNumTestsPerEvictionRun(final String key) { 471 Integer value = null; 472 if (perUserNumTestsPerEvictionRun != null) { 473 value = perUserNumTestsPerEvictionRun.get(key); 474 } 475 if (value == null) { 476 return getDefaultNumTestsPerEvictionRun(); 477 } 478 return value.intValue(); 479 } 480 481 /** 482 * Sets a user specific value for 483 * {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the specified 484 * user's pool. 485 */ 486 public void setPerUserNumTestsPerEvictionRun(final String username, 487 final Integer value) { 488 assertInitializationAllowed(); 489 if (perUserNumTestsPerEvictionRun == null) { 490 perUserNumTestsPerEvictionRun = new HashMap<>(); 491 } 492 perUserNumTestsPerEvictionRun.put(username, value); 493 } 494 495 void setPerUserNumTestsPerEvictionRun( 496 final Map<String,Integer> userDefaultNumTestsPerEvictionRun) { 497 assertInitializationAllowed(); 498 if (perUserNumTestsPerEvictionRun == null) { 499 perUserNumTestsPerEvictionRun = new HashMap<>(); 500 } else { 501 perUserNumTestsPerEvictionRun.clear(); 502 } 503 perUserNumTestsPerEvictionRun.putAll(userDefaultNumTestsPerEvictionRun); 504 } 505 506 507 /** 508 * Gets the user specific value for 509 * {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the 510 * specified user's pool or the default if no user specific value is defined. 511 */ 512 public long getPerUserSoftMinEvictableIdleTimeMillis(final String key) { 513 Long value = null; 514 if (perUserSoftMinEvictableIdleTimeMillis != null) { 515 value = perUserSoftMinEvictableIdleTimeMillis.get(key); 516 } 517 if (value == null) { 518 return getDefaultSoftMinEvictableIdleTimeMillis(); 519 } 520 return value.longValue(); 521 } 522 523 /** 524 * Sets a user specific value for 525 * {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the 526 * specified user's pool. 527 */ 528 public void setPerUserSoftMinEvictableIdleTimeMillis(final String username, 529 final Long value) { 530 assertInitializationAllowed(); 531 if (perUserSoftMinEvictableIdleTimeMillis == null) { 532 perUserSoftMinEvictableIdleTimeMillis = new HashMap<>(); 533 } 534 perUserSoftMinEvictableIdleTimeMillis.put(username, value); 535 } 536 537 void setPerUserSoftMinEvictableIdleTimeMillis( 538 final Map<String,Long> userDefaultSoftMinEvictableIdleTimeMillis) { 539 assertInitializationAllowed(); 540 if (perUserSoftMinEvictableIdleTimeMillis == null) { 541 perUserSoftMinEvictableIdleTimeMillis = new HashMap<>(); 542 } else { 543 perUserSoftMinEvictableIdleTimeMillis.clear(); 544 } 545 perUserSoftMinEvictableIdleTimeMillis.putAll(userDefaultSoftMinEvictableIdleTimeMillis); 546 } 547 548 549 /** 550 * Gets the user specific value for 551 * {@link GenericObjectPool#getTestOnCreate()} for the 552 * specified user's pool or the default if no user specific value is defined. 553 */ 554 public boolean getPerUserTestOnCreate(final String key) { 555 Boolean value = null; 556 if (perUserTestOnCreate != null) { 557 value = perUserTestOnCreate.get(key); 558 } 559 if (value == null) { 560 return getDefaultTestOnCreate(); 561 } 562 return value.booleanValue(); 563 } 564 565 /** 566 * Sets a user specific value for 567 * {@link GenericObjectPool#getTestOnCreate()} for the specified 568 * user's pool. 569 */ 570 public void setPerUserTestOnCreate(final String username, final Boolean value) { 571 assertInitializationAllowed(); 572 if (perUserTestOnCreate == null) { 573 perUserTestOnCreate = new HashMap<>(); 574 } 575 perUserTestOnCreate.put(username, value); 576 } 577 578 void setPerUserTestOnCreate(final Map<String,Boolean> userDefaultTestOnCreate) { 579 assertInitializationAllowed(); 580 if (perUserTestOnCreate == null) { 581 perUserTestOnCreate = new HashMap<>(); 582 } else { 583 perUserTestOnCreate.clear(); 584 } 585 perUserTestOnCreate.putAll(userDefaultTestOnCreate); 586 } 587 588 589 /** 590 * Gets the user specific value for 591 * {@link GenericObjectPool#getTestOnBorrow()} for the 592 * specified user's pool or the default if no user specific value is defined. 593 */ 594 public boolean getPerUserTestOnBorrow(final String key) { 595 Boolean value = null; 596 if (perUserTestOnBorrow != null) { 597 value = perUserTestOnBorrow.get(key); 598 } 599 if (value == null) { 600 return getDefaultTestOnBorrow(); 601 } 602 return value.booleanValue(); 603 } 604 605 /** 606 * Sets a user specific value for 607 * {@link GenericObjectPool#getTestOnBorrow()} for the specified 608 * user's pool. 609 */ 610 public void setPerUserTestOnBorrow(final String username, final Boolean value) { 611 assertInitializationAllowed(); 612 if (perUserTestOnBorrow == null) { 613 perUserTestOnBorrow = new HashMap<>(); 614 } 615 perUserTestOnBorrow.put(username, value); 616 } 617 618 void setPerUserTestOnBorrow(final Map<String,Boolean> userDefaultTestOnBorrow) { 619 assertInitializationAllowed(); 620 if (perUserTestOnBorrow == null) { 621 perUserTestOnBorrow = new HashMap<>(); 622 } else { 623 perUserTestOnBorrow.clear(); 624 } 625 perUserTestOnBorrow.putAll(userDefaultTestOnBorrow); 626 } 627 628 629 /** 630 * Gets the user specific value for 631 * {@link GenericObjectPool#getTestOnReturn()} for the 632 * specified user's pool or the default if no user specific value is defined. 633 */ 634 public boolean getPerUserTestOnReturn(final String key) { 635 Boolean value = null; 636 if (perUserTestOnReturn != null) { 637 value = perUserTestOnReturn.get(key); 638 } 639 if (value == null) { 640 return getDefaultTestOnReturn(); 641 } 642 return value.booleanValue(); 643 } 644 645 /** 646 * Sets a user specific value for 647 * {@link GenericObjectPool#getTestOnReturn()} for the specified 648 * user's pool. 649 */ 650 public void setPerUserTestOnReturn(final String username, final Boolean value) { 651 assertInitializationAllowed(); 652 if (perUserTestOnReturn == null) { 653 perUserTestOnReturn = new HashMap<>(); 654 } 655 perUserTestOnReturn.put(username, value); 656 } 657 658 void setPerUserTestOnReturn( 659 final Map<String,Boolean> userDefaultTestOnReturn) { 660 assertInitializationAllowed(); 661 if (perUserTestOnReturn == null) { 662 perUserTestOnReturn = new HashMap<>(); 663 } else { 664 perUserTestOnReturn.clear(); 665 } 666 perUserTestOnReturn.putAll(userDefaultTestOnReturn); 667 } 668 669 670 /** 671 * Gets the user specific value for 672 * {@link GenericObjectPool#getTestWhileIdle()} for the 673 * specified user's pool or the default if no user specific value is defined. 674 */ 675 public boolean getPerUserTestWhileIdle(final String key) { 676 Boolean value = null; 677 if (perUserTestWhileIdle != null) { 678 value = perUserTestWhileIdle.get(key); 679 } 680 if (value == null) { 681 return getDefaultTestWhileIdle(); 682 } 683 return value.booleanValue(); 684 } 685 686 /** 687 * Sets a user specific value for 688 * {@link GenericObjectPool#getTestWhileIdle()} for the specified 689 * user's pool. 690 */ 691 public void setPerUserTestWhileIdle(final String username, final Boolean value) { 692 assertInitializationAllowed(); 693 if (perUserTestWhileIdle == null) { 694 perUserTestWhileIdle = new HashMap<>(); 695 } 696 perUserTestWhileIdle.put(username, value); 697 } 698 699 void setPerUserTestWhileIdle( 700 final Map<String,Boolean> userDefaultTestWhileIdle) { 701 assertInitializationAllowed(); 702 if (perUserTestWhileIdle == null) { 703 perUserTestWhileIdle = new HashMap<>(); 704 } else { 705 perUserTestWhileIdle.clear(); 706 } 707 perUserTestWhileIdle.putAll(userDefaultTestWhileIdle); 708 } 709 710 711 /** 712 * Gets the user specific value for 713 * {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} for the 714 * specified user's pool or the default if no user specific value is defined. 715 */ 716 public long getPerUserTimeBetweenEvictionRunsMillis(final String key) { 717 Long value = null; 718 if (perUserTimeBetweenEvictionRunsMillis != null) { 719 value = perUserTimeBetweenEvictionRunsMillis.get(key); 720 } 721 if (value == null) { 722 return getDefaultTimeBetweenEvictionRunsMillis(); 723 } 724 return value.longValue(); 725 } 726 727 /** 728 * Sets a user specific value for 729 * {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for the specified 730 * user's pool. 731 */ 732 public void setPerUserTimeBetweenEvictionRunsMillis(final String username, 733 final Long value) { 734 assertInitializationAllowed(); 735 if (perUserTimeBetweenEvictionRunsMillis == null) { 736 perUserTimeBetweenEvictionRunsMillis = new HashMap<>(); 737 } 738 perUserTimeBetweenEvictionRunsMillis.put(username, value); 739 } 740 741 void setPerUserTimeBetweenEvictionRunsMillis( 742 final Map<String,Long> userDefaultTimeBetweenEvictionRunsMillis ) { 743 assertInitializationAllowed(); 744 if (perUserTimeBetweenEvictionRunsMillis == null) { 745 perUserTimeBetweenEvictionRunsMillis = new HashMap<>(); 746 } else { 747 perUserTimeBetweenEvictionRunsMillis.clear(); 748 } 749 perUserTimeBetweenEvictionRunsMillis.putAll( 750 userDefaultTimeBetweenEvictionRunsMillis ); 751 } 752 753 754 /** 755 * Gets the user specific default value for 756 * {@link Connection#setAutoCommit(boolean)} for the specified user's pool. 757 */ 758 public Boolean getPerUserDefaultAutoCommit(final String key) { 759 Boolean value = null; 760 if (perUserDefaultAutoCommit != null) { 761 value = perUserDefaultAutoCommit.get(key); 762 } 763 return value; 764 } 765 766 /** 767 * Sets a user specific default value for 768 * {@link Connection#setAutoCommit(boolean)} for the specified user's pool. 769 */ 770 public void setPerUserDefaultAutoCommit(final String username, final Boolean value) { 771 assertInitializationAllowed(); 772 if (perUserDefaultAutoCommit == null) { 773 perUserDefaultAutoCommit = new HashMap<>(); 774 } 775 perUserDefaultAutoCommit.put(username, value); 776 } 777 778 void setPerUserDefaultAutoCommit(final Map<String,Boolean> userDefaultAutoCommit) { 779 assertInitializationAllowed(); 780 if (perUserDefaultAutoCommit == null) { 781 perUserDefaultAutoCommit = new HashMap<>(); 782 } else { 783 perUserDefaultAutoCommit.clear(); 784 } 785 perUserDefaultAutoCommit.putAll(userDefaultAutoCommit); 786 } 787 788 789 /** 790 * Gets the user specific default value for 791 * {@link Connection#setReadOnly(boolean)} for the specified user's pool. 792 */ 793 public Boolean getPerUserDefaultReadOnly(final String key) { 794 Boolean value = null; 795 if (perUserDefaultReadOnly != null) { 796 value = perUserDefaultReadOnly.get(key); 797 } 798 return value; 799 } 800 801 /** 802 * Sets a user specific default value for 803 * {@link Connection#setReadOnly(boolean)} for the specified user's pool. 804 */ 805 public void setPerUserDefaultReadOnly(final String username, final Boolean value) { 806 assertInitializationAllowed(); 807 if (perUserDefaultReadOnly == null) { 808 perUserDefaultReadOnly = new HashMap<>(); 809 } 810 perUserDefaultReadOnly.put(username, value); 811 } 812 813 void setPerUserDefaultReadOnly(final Map<String,Boolean> userDefaultReadOnly) { 814 assertInitializationAllowed(); 815 if (perUserDefaultReadOnly == null) { 816 perUserDefaultReadOnly = new HashMap<>(); 817 } else { 818 perUserDefaultReadOnly.clear(); 819 } 820 perUserDefaultReadOnly.putAll(userDefaultReadOnly); 821 } 822 823 824 /** 825 * Gets the user specific default value for 826 * {@link Connection#setTransactionIsolation(int)} for the specified user's pool. 827 */ 828 public Integer getPerUserDefaultTransactionIsolation(final String key) { 829 Integer value = null; 830 if (perUserDefaultTransactionIsolation != null) { 831 value = perUserDefaultTransactionIsolation.get(key); 832 } 833 return value; 834 } 835 836 /** 837 * Sets a user specific default value for 838 * {@link Connection#setTransactionIsolation(int)} for the specified user's pool. 839 */ 840 public void setPerUserDefaultTransactionIsolation(final String username, 841 final Integer value) { 842 assertInitializationAllowed(); 843 if (perUserDefaultTransactionIsolation == null) { 844 perUserDefaultTransactionIsolation = new HashMap<>(); 845 } 846 perUserDefaultTransactionIsolation.put(username, value); 847 } 848 849 void setPerUserDefaultTransactionIsolation( 850 final Map<String,Integer> userDefaultTransactionIsolation) { 851 assertInitializationAllowed(); 852 if (perUserDefaultTransactionIsolation == null) { 853 perUserDefaultTransactionIsolation = new HashMap<>(); 854 } else { 855 perUserDefaultTransactionIsolation.clear(); 856 } 857 perUserDefaultTransactionIsolation.putAll(userDefaultTransactionIsolation); 858 } 859 860 861 // ---------------------------------------------------------------------- 862 // Instrumentation Methods 863 864 /** 865 * Gets the number of active connections in the default pool. 866 */ 867 public int getNumActive() { 868 return getNumActive(null); 869 } 870 871 /** 872 * Gets the number of active connections in the pool for a given user. 873 */ 874 public int getNumActive(final String username) { 875 final ObjectPool<PooledConnectionAndInfo> pool = 876 getPool(getPoolKey(username)); 877 return pool == null ? 0 : pool.getNumActive(); 878 } 879 880 /** 881 * Gets the number of idle connections in the default pool. 882 */ 883 public int getNumIdle() { 884 return getNumIdle(null); 885 } 886 887 /** 888 * Gets the number of idle connections in the pool for a given user. 889 */ 890 public int getNumIdle(final String username) { 891 final ObjectPool<PooledConnectionAndInfo> pool = 892 getPool(getPoolKey(username)); 893 return pool == null ? 0 : pool.getNumIdle(); 894 } 895 896 897 // ---------------------------------------------------------------------- 898 // Inherited abstract methods 899 900 @Override 901 protected PooledConnectionAndInfo 902 getPooledConnectionAndInfo(final String username, final String password) 903 throws SQLException { 904 905 final PoolKey key = getPoolKey(username); 906 ObjectPool<PooledConnectionAndInfo> pool; 907 PooledConnectionManager manager; 908 synchronized(this) { 909 manager = managers.get(key); 910 if (manager == null) { 911 try { 912 registerPool(username, password); 913 manager = managers.get(key); 914 } catch (final NamingException e) { 915 throw new SQLException("RegisterPool failed", e); 916 } 917 } 918 pool = ((CPDSConnectionFactory) manager).getPool(); 919 } 920 921 PooledConnectionAndInfo info = null; 922 try { 923 info = pool.borrowObject(); 924 } 925 catch (final NoSuchElementException ex) { 926 throw new SQLException( 927 "Could not retrieve connection info from pool", ex); 928 } 929 catch (final Exception e) { 930 // See if failure is due to CPDSConnectionFactory authentication failure 931 try { 932 testCPDS(username, password); 933 } catch (final Exception ex) { 934 throw new SQLException( 935 "Could not retrieve connection info from pool", ex); 936 } 937 // New password works, so kill the old pool, create a new one, and borrow 938 manager.closePool(username); 939 synchronized (this) { 940 managers.remove(key); 941 } 942 try { 943 registerPool(username, password); 944 pool = getPool(key); 945 } catch (final NamingException ne) { 946 throw new SQLException("RegisterPool failed", ne); 947 } 948 try { 949 info = pool.borrowObject(); 950 } catch (final Exception ex) { 951 throw new SQLException( 952 "Could not retrieve connection info from pool", ex); 953 } 954 } 955 return info; 956 } 957 958 @Override 959 protected void setupDefaults(final Connection con, final String username) 960 throws SQLException { 961 Boolean defaultAutoCommit = isDefaultAutoCommit(); 962 if (username != null) { 963 final Boolean userMax = getPerUserDefaultAutoCommit(username); 964 if (userMax != null) { 965 defaultAutoCommit = userMax; 966 } 967 } 968 969 Boolean defaultReadOnly = isDefaultReadOnly(); 970 if (username != null) { 971 final Boolean userMax = getPerUserDefaultReadOnly(username); 972 if (userMax != null) { 973 defaultReadOnly = userMax; 974 } 975 } 976 977 int defaultTransactionIsolation = getDefaultTransactionIsolation(); 978 if (username != null) { 979 final Integer userMax = getPerUserDefaultTransactionIsolation(username); 980 if (userMax != null) { 981 defaultTransactionIsolation = userMax.intValue(); 982 } 983 } 984 985 if (defaultAutoCommit != null && 986 con.getAutoCommit() != defaultAutoCommit.booleanValue()) { 987 con.setAutoCommit(defaultAutoCommit.booleanValue()); 988 } 989 990 if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) { 991 con.setTransactionIsolation(defaultTransactionIsolation); 992 } 993 994 if (defaultReadOnly != null && 995 con.isReadOnly() != defaultReadOnly.booleanValue()) { 996 con.setReadOnly(defaultReadOnly.booleanValue()); 997 } 998 } 999 1000 @Override 1001 protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) { 1002 return managers.get(getPoolKey(upkey.getUsername())); 1003 } 1004 1005 /** 1006 * Returns a <code>PerUserPoolDataSource</code> {@link Reference}. 1007 */ 1008 @Override 1009 public Reference getReference() throws NamingException { 1010 final Reference ref = new Reference(getClass().getName(), 1011 PerUserPoolDataSourceFactory.class.getName(), null); 1012 ref.add(new StringRefAddr("instanceKey", getInstanceKey())); 1013 return ref; 1014 } 1015 1016 /** 1017 * Creates a pool key from the provided parameters. 1018 * 1019 * @param username User name 1020 * @return The pool key 1021 */ 1022 private PoolKey getPoolKey(final String username) { 1023 return new PoolKey(getDataSourceName(), username); 1024 } 1025 1026 private synchronized void registerPool(final String username, final String password) 1027 throws NamingException, SQLException { 1028 1029 final ConnectionPoolDataSource cpds = testCPDS(username, password); 1030 1031 // Set up the factory we will use (passing the pool associates 1032 // the factory with the pool, so we do not have to do so 1033 // explicitly) 1034 final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, 1035 getValidationQuery(), getValidationQueryTimeout(), 1036 isRollbackAfterValidation(), username, password); 1037 factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis()); 1038 1039 // Create an object pool to contain our PooledConnections 1040 final GenericObjectPool<PooledConnectionAndInfo> pool = 1041 new GenericObjectPool<>(factory); 1042 factory.setPool(pool); 1043 pool.setBlockWhenExhausted(getPerUserBlockWhenExhausted(username)); 1044 pool.setEvictionPolicyClassName( 1045 getPerUserEvictionPolicyClassName(username)); 1046 pool.setLifo(getPerUserLifo(username)); 1047 pool.setMaxIdle(getPerUserMaxIdle(username)); 1048 pool.setMaxTotal(getPerUserMaxTotal(username)); 1049 pool.setMaxWaitMillis(getPerUserMaxWaitMillis(username)); 1050 pool.setMinEvictableIdleTimeMillis( 1051 getPerUserMinEvictableIdleTimeMillis(username)); 1052 pool.setMinIdle(getPerUserMinIdle(username)); 1053 pool.setNumTestsPerEvictionRun( 1054 getPerUserNumTestsPerEvictionRun(username)); 1055 pool.setSoftMinEvictableIdleTimeMillis( 1056 getPerUserSoftMinEvictableIdleTimeMillis(username)); 1057 pool.setTestOnCreate(getPerUserTestOnCreate(username)); 1058 pool.setTestOnBorrow(getPerUserTestOnBorrow(username)); 1059 pool.setTestOnReturn(getPerUserTestOnReturn(username)); 1060 pool.setTestWhileIdle(getPerUserTestWhileIdle(username)); 1061 pool.setTimeBetweenEvictionRunsMillis( 1062 getPerUserTimeBetweenEvictionRunsMillis(username)); 1063 1064 pool.setSwallowedExceptionListener(new SwallowedExceptionLogger(log)); 1065 1066 final Object old = managers.put(getPoolKey(username), factory); 1067 if (old != null) { 1068 throw new IllegalStateException("Pool already contains an entry for this user/password: " + username); 1069 } 1070 } 1071 1072 /** 1073 * Supports Serialization interface. 1074 * 1075 * @param in a <code>java.io.ObjectInputStream</code> value 1076 * @throws IOException if an error occurs 1077 * @throws ClassNotFoundException if an error occurs 1078 */ 1079 private void readObject(final ObjectInputStream in) 1080 throws IOException, ClassNotFoundException { 1081 try 1082 { 1083 in.defaultReadObject(); 1084 final PerUserPoolDataSource oldDS = (PerUserPoolDataSource) 1085 new PerUserPoolDataSourceFactory() 1086 .getObjectInstance(getReference(), null, null, null); 1087 this.managers = oldDS.managers; 1088 } 1089 catch (final NamingException e) 1090 { 1091 throw new IOException("NamingException: " + e); 1092 } 1093 } 1094 1095 /** 1096 * Returns the object pool associated with the given PoolKey. 1097 * 1098 * @param key PoolKey identifying the pool 1099 * @return the GenericObjectPool pooling connections for the username and datasource 1100 * specified by the PoolKey 1101 */ 1102 private ObjectPool<PooledConnectionAndInfo> getPool(final PoolKey key) { 1103 final CPDSConnectionFactory mgr = (CPDSConnectionFactory) managers.get(key); 1104 return mgr == null ? null : mgr.getPool(); 1105 } 1106}