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 module hunt.pool.PoolUtils; 18 19 // import java.util.Collection; 20 // import java.util.Collections; 21 // import java.util.HashMap; 22 // import java.util.Iterator; 23 // import java.util.Map; 24 // import java.util.NoSuchElementException; 25 // import java.util.Timer; 26 // import java.util.TimerTask; 27 // import java.util.concurrent.locks.ReentrantReadWriteLock; 28 // import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; 29 // import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; 30 31 import hunt.concurrency.Executors; 32 import hunt.concurrency.ScheduledExecutorService; 33 import hunt.concurrency.ScheduledThreadPoolExecutor; 34 35 import hunt.collection; 36 import hunt.Exceptions; 37 import hunt.util.DateTime; 38 39 import core.sync.rwmutex; 40 41 import std.algorithm; 42 import std.concurrency : initOnce; 43 import std.conv; 44 45 /** 46 * This class consists exclusively of static methods that operate on or return 47 * ObjectPool or KeyedObjectPool related interfaces. 48 * 49 */ 50 class PoolUtils { 51 52 private enum string MSG_FACTOR_NEGATIVE = "factor must be positive."; 53 private enum string MSG_MIN_IDLE = "minIdle must be non-negative."; 54 private enum string MSG_NULL_KEY = "key must not be null."; 55 private enum string MSG_NULL_KEYED_POOL = "keyedPool must not be null."; 56 private enum string MSG_NULL_KEYS = "keys must not be null."; 57 private enum string MSG_NULL_POOL = "pool must not be null."; 58 59 /** 60 * Timer used to periodically check pools idle object count. Because a 61 * {@link Timer} creates a {@link Thread}, an IODH is used. 62 */ 63 // static class TimerHolder { 64 // static Timer MIN_IDLE_TIMER = new Timer(true); 65 // } 66 67 /** 68 * PoolUtils instances should NOT be constructed in standard programming. 69 * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. 70 * This constructor is public to permit tools that require a JavaBean 71 * instance to operate. 72 */ 73 this() { 74 } 75 76 /** 77 * Should the supplied Throwable be re-thrown (eg if it is an instance of 78 * one of the Throwables that should never be swallowed). Used by the pool 79 * error handling for operations that throw exceptions that normally need to 80 * be ignored. 81 * 82 * @param t 83 * The Throwable to check 84 * @throws ThreadDeath 85 * if that is passed in 86 * @throws VirtualMachineError 87 * if that is passed in 88 */ 89 static void checkRethrow(Throwable t) { 90 ThreadDeath td = cast(ThreadDeath) t; 91 if (td !is null) { 92 throw td; 93 } 94 // if (t instanceof VirtualMachineError) { 95 // throw (VirtualMachineError) t; 96 // } 97 // All other instances of Throwable will be silently swallowed 98 } 99 100 /** 101 * Periodically check the idle object count for the pool. At most one idle 102 * object will be added per period. If there is an exception when calling 103 * {@link ObjectPool#addObject()} then no more checks will be performed. 104 * 105 * @param pool 106 * the pool to check periodically. 107 * @param minIdle 108 * if the {@link ObjectPool#getNumIdle()} is less than this then 109 * add an idle object. 110 * @param period 111 * the frequency to check the number of idle objects in a pool, 112 * see {@link Timer#schedule(TimerTask, long, long)}. 113 * @param <T> the type of objects in the pool 114 * @return the {@link TimerTask} that will periodically check the pools idle 115 * object count. 116 * @throws IllegalArgumentException 117 * when <code>pool</code> is <code>null</code> or when 118 * <code>minIdle</code> is negative or when <code>period</code> 119 * isn't valid for {@link Timer#schedule(TimerTask, long, long)} 120 */ 121 static TimerTask checkMinIdle(T)(ObjectPool!(T) pool, 122 int minIdle, long period) { 123 if (pool is null) { 124 throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); 125 } 126 if (minIdle < 0) { 127 throw new IllegalArgumentException(MSG_MIN_IDLE); 128 } 129 TimerTask task = new ObjectPoolMinIdleTimerTask!T(pool, minIdle); 130 getMinIdleTimer().schedule(task, 0L, period); 131 return task; 132 } 133 134 /** 135 * Periodically check the idle object count for the key in the keyedPool. At 136 * most one idle object will be added per period. If there is an exception 137 * when calling {@link KeyedObjectPool#addObject(Object)} then no more 138 * checks for that key will be performed. 139 * 140 * @param keyedPool 141 * the keyedPool to check periodically. 142 * @param key 143 * the key to check the idle count of. 144 * @param minIdle 145 * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than 146 * this then add an idle object. 147 * @param period 148 * the frequency to check the number of idle objects in a 149 * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. 150 * @param <K> the type of the pool key 151 * @param <V> the type of pool entries 152 * @return the {@link TimerTask} that will periodically check the pools idle 153 * object count. 154 * @throws IllegalArgumentException 155 * when <code>keyedPool</code>, <code>key</code> is 156 * <code>null</code> or when <code>minIdle</code> is negative or 157 * when <code>period</code> isn't valid for 158 * {@link Timer#schedule(TimerTask, long, long)}. 159 */ 160 static TimerTask checkMinIdle(K, V)( 161 KeyedObjectPool!(K, V) keyedPool, K key, 162 int minIdle, long period) { 163 if (keyedPool is null) { 164 throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); 165 } 166 if (key is null) { 167 throw new IllegalArgumentException(MSG_NULL_KEY); 168 } 169 if (minIdle < 0) { 170 throw new IllegalArgumentException(MSG_MIN_IDLE); 171 } 172 TimerTask task = new KeyedObjectPoolMinIdleTimerTask!(K, V)( 173 keyedPool, key, minIdle); 174 getMinIdleTimer().schedule(task, 0L, period); 175 return task; 176 } 177 178 /** 179 * Periodically check the idle object count for each key in the 180 * <code>Collection</code> <code>keys</code> in the keyedPool. At most one 181 * idle object will be added per period. 182 * 183 * @param keyedPool 184 * the keyedPool to check periodically. 185 * @param keys 186 * a collection of keys to check the idle object count. 187 * @param minIdle 188 * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than 189 * this then add an idle object. 190 * @param period 191 * the frequency to check the number of idle objects in a 192 * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. 193 * @param <K> the type of the pool key 194 * @param <V> the type of pool entries 195 * @return a {@link Map} of key and {@link TimerTask} pairs that will 196 * periodically check the pools idle object count. 197 * @throws IllegalArgumentException 198 * when <code>keyedPool</code>, <code>keys</code>, or any of the 199 * values in the collection is <code>null</code> or when 200 * <code>minIdle</code> is negative or when <code>period</code> 201 * isn't valid for {@link Timer#schedule(TimerTask, long, long)} 202 * . 203 * @see #checkMinIdle(KeyedObjectPool, Object, int, long) 204 */ 205 static Map!(K, TimerTask) checkMinIdle(K, V)( 206 KeyedObjectPool!(K, V) keyedPool, Collection!(K) keys, 207 int minIdle, long period) 208 { 209 if (keys is null) { 210 throw new IllegalArgumentException(MSG_NULL_KEYS); 211 } 212 Map!(K, TimerTask) tasks = new HashMap!(K, V)(keys.size()); 213 Iterator!(K) iter = keys.iterator(); 214 while (iter.hasNext()) { 215 K key = iter.next(); 216 TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); 217 tasks.put(key, task); 218 } 219 return tasks; 220 } 221 222 /** 223 * Calls {@link ObjectPool#addObject()} on <code>pool</code> <code>count</code> 224 * number of times. 225 * 226 * @param pool 227 * the pool to prefill. 228 * @param count 229 * the number of idle objects to add. 230 * @param <T> the type of objects in the pool 231 * @throws Exception 232 * when {@link ObjectPool#addObject()} fails. 233 * @throws IllegalArgumentException 234 * when <code>pool</code> is <code>null</code>. 235 */ 236 static void prefill(T)(ObjectPool!(T) pool, int count) 237 { 238 if (pool is null) { 239 throw new IllegalArgumentException(MSG_NULL_POOL); 240 } 241 for (int i = 0; i < count; i++) { 242 pool.addObject(); 243 } 244 } 245 246 /** 247 * Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with 248 * <code>key</code> <code>count</code> number of times. 249 * 250 * @param keyedPool 251 * the keyedPool to prefill. 252 * @param key 253 * the key to add objects for. 254 * @param count 255 * the number of idle objects to add for <code>key</code>. 256 * @param <K> the type of the pool key 257 * @param <V> the type of pool entries 258 * @throws Exception 259 * when {@link KeyedObjectPool#addObject(Object)} fails. 260 * @throws IllegalArgumentException 261 * when <code>keyedPool</code> or <code>key</code> is 262 * <code>null</code>. 263 */ 264 static void prefill(K, V)(KeyedObjectPool!(K, V) keyedPool, 265 K key, int count) { 266 if (keyedPool is null) { 267 throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); 268 } 269 if (key is null) { 270 throw new IllegalArgumentException(MSG_NULL_KEY); 271 } 272 for (int i = 0; i < count; i++) { 273 keyedPool.addObject(key); 274 } 275 } 276 277 /** 278 * Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with each 279 * key in <code>keys</code> for <code>count</code> number of times. This has 280 * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} 281 * for each key in the <code>keys</code> collection. 282 * 283 * @param keyedPool 284 * the keyedPool to prefill. 285 * @param keys 286 * {@link Collection} of keys to add objects for. 287 * @param count 288 * the number of idle objects to add for each <code>key</code>. 289 * @param <K> the type of the pool key 290 * @param <V> the type of pool entries 291 * @throws Exception 292 * when {@link KeyedObjectPool#addObject(Object)} fails. 293 * @throws IllegalArgumentException 294 * when <code>keyedPool</code>, <code>keys</code>, or any value 295 * in <code>keys</code> is <code>null</code>. 296 * @see #prefill(KeyedObjectPool, Object, int) 297 */ 298 static void prefill(K, V)(KeyedObjectPool!(K, V) keyedPool, 299 Collection!(K) keys, int count) { 300 if (keys is null) { 301 throw new IllegalArgumentException(MSG_NULL_KEYS); 302 } 303 Iterator!(K) iter = keys.iterator(); 304 while (iter.hasNext()) { 305 prefill(keyedPool, iter.next(), count); 306 } 307 } 308 309 /** 310 * Returns a synchronized (thread-safe) ObjectPool backed by the specified 311 * ObjectPool. 312 * <p> 313 * <b>Note:</b> This should not be used on pool implementations that already 314 * provide proper synchronization such as the pools provided in the Commons 315 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 316 * objects to be returned before allowing another one to be borrowed with 317 * another layer of synchronization will cause liveliness issues or a 318 * deadlock. 319 * </p> 320 * 321 * @param pool 322 * the ObjectPool to be "wrapped" in a synchronized ObjectPool. 323 * @param <T> the type of objects in the pool 324 * @return a synchronized view of the specified ObjectPool. 325 */ 326 static ObjectPool!(T) synchronizedPool(T)(ObjectPool!(T) pool) { 327 if (pool is null) { 328 throw new IllegalArgumentException(MSG_NULL_POOL); 329 } 330 /* 331 * assert !(pool instanceof GenericObjectPool) : 332 * "GenericObjectPool is already thread-safe"; assert !(pool instanceof 333 * SoftReferenceObjectPool) : 334 * "SoftReferenceObjectPool is already thread-safe"; assert !(pool 335 * instanceof StackObjectPool) : 336 * "StackObjectPool is already thread-safe"; assert 337 * !"org.apache.commons.pool.composite.CompositeObjectPool" 338 * == typeid(pool).name : 339 * "CompositeObjectPools are already thread-safe"; 340 */ 341 return new SynchronizedObjectPool!T(pool); 342 } 343 344 /** 345 * Returns a synchronized (thread-safe) KeyedObjectPool backed by the 346 * specified KeyedObjectPool. 347 * <p> 348 * <b>Note:</b> This should not be used on pool implementations that already 349 * provide proper synchronization such as the pools provided in the Commons 350 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 351 * objects to be returned before allowing another one to be borrowed with 352 * another layer of synchronization will cause liveliness issues or a 353 * deadlock. 354 * </p> 355 * 356 * @param keyedPool 357 * the KeyedObjectPool to be "wrapped" in a synchronized 358 * KeyedObjectPool. 359 * @param <K> the type of the pool key 360 * @param <V> the type of pool entries 361 * @return a synchronized view of the specified KeyedObjectPool. 362 */ 363 static KeyedObjectPool!(K, V) synchronizedPool(K, V)( 364 KeyedObjectPool!(K, V) keyedPool) { 365 /* 366 * assert !(keyedPool instanceof GenericKeyedObjectPool) : 367 * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool 368 * instanceof StackKeyedObjectPool) : 369 * "StackKeyedObjectPool is already thread-safe"; assert 370 * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" 371 * == typeid(keyedPool).name : 372 * "CompositeKeyedObjectPools are already thread-safe"; 373 */ 374 return new SynchronizedKeyedObjectPool!(K, V)(keyedPool); 375 } 376 377 /** 378 * Returns a synchronized (thread-safe) PooledObjectFactory backed by the 379 * specified PooledObjectFactory. 380 * 381 * @param factory 382 * the PooledObjectFactory to be "wrapped" in a synchronized 383 * PooledObjectFactory. 384 * @param <T> the type of objects in the pool 385 * @return a synchronized view of the specified PooledObjectFactory. 386 */ 387 static PooledObjectFactory!(T) synchronizedPooledFactory(T)( 388 PooledObjectFactory!(T) factory) { 389 return new SynchronizedPooledObjectFactory!T(factory); 390 } 391 392 /** 393 * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by 394 * the specified KeyedPoolableObjectFactory. 395 * 396 * @param keyedFactory 397 * the KeyedPooledObjectFactory to be "wrapped" in a 398 * synchronized KeyedPooledObjectFactory. 399 * @param <K> the type of the pool key 400 * @param <V> the type of pool entries 401 * @return a synchronized view of the specified KeyedPooledObjectFactory. 402 */ 403 static KeyedPooledObjectFactory!(K, V) synchronizedKeyedPooledFactory(K, V)( 404 KeyedPooledObjectFactory!(K, V) keyedFactory) { 405 return new SynchronizedKeyedPooledObjectFactory!(K, V)(keyedFactory); 406 } 407 408 /** 409 * Returns a pool that adaptively decreases its size when idle objects are 410 * no longer needed. This is intended as an always thread-safe alternative 411 * to using an idle object evictor provided by many pool implementations. 412 * This is also an effective way to shrink FIFO ordered pools that 413 * experience load spikes. 414 * 415 * @param pool 416 * the ObjectPool to be decorated so it shrinks its idle count 417 * when possible. 418 * @param <T> the type of objects in the pool 419 * @return a pool that adaptively decreases its size when idle objects are 420 * no longer needed. 421 * @see #erodingPool(ObjectPool, float) 422 */ 423 static ObjectPool!(T) erodingPool(T)(ObjectPool!(T) pool) { 424 return erodingPool(pool, 1f); 425 } 426 427 /** 428 * Returns a pool that adaptively decreases its size when idle objects are 429 * no longer needed. This is intended as an always thread-safe alternative 430 * to using an idle object evictor provided by many pool implementations. 431 * This is also an effective way to shrink FIFO ordered pools that 432 * experience load spikes. 433 * <p> 434 * The factor parameter provides a mechanism to tweak the rate at which the 435 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 436 * try to shrink its size more often. Values greater than 1 cause the pool 437 * to less frequently try to shrink its size. 438 * </p> 439 * 440 * @param pool 441 * the ObjectPool to be decorated so it shrinks its idle count 442 * when possible. 443 * @param factor 444 * a positive value to scale the rate at which the pool tries to 445 * reduce its size. If 0 < factor < 1 then the pool 446 * shrinks more aggressively. If 1 < factor then the pool 447 * shrinks less aggressively. 448 * @param <T> the type of objects in the pool 449 * @return a pool that adaptively decreases its size when idle objects are 450 * no longer needed. 451 * @see #erodingPool(ObjectPool) 452 */ 453 static ObjectPool!(T) erodingPool(T)(ObjectPool!(T) pool, 454 float factor) { 455 if (pool is null) { 456 throw new IllegalArgumentException(MSG_NULL_POOL); 457 } 458 if (factor <= 0f) { 459 throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); 460 } 461 return new ErodingObjectPool!T(pool, factor); 462 } 463 464 /** 465 * Returns a pool that adaptively decreases its size when idle objects are 466 * no longer needed. This is intended as an always thread-safe alternative 467 * to using an idle object evictor provided by many pool implementations. 468 * This is also an effective way to shrink FIFO ordered pools that 469 * experience load spikes. 470 * 471 * @param keyedPool 472 * the KeyedObjectPool to be decorated so it shrinks its idle 473 * count when possible. 474 * @param <K> the type of the pool key 475 * @param <V> the type of pool entries 476 * @return a pool that adaptively decreases its size when idle objects are 477 * no longer needed. 478 * @see #erodingPool(KeyedObjectPool, float) 479 * @see #erodingPool(KeyedObjectPool, float, bool) 480 */ 481 static KeyedObjectPool!(K, V) erodingPool(K, V)( 482 KeyedObjectPool!(K, V) keyedPool) { 483 return erodingPool(keyedPool, 1f); 484 } 485 486 /** 487 * Returns a pool that adaptively decreases its size when idle objects are 488 * no longer needed. This is intended as an always thread-safe alternative 489 * to using an idle object evictor provided by many pool implementations. 490 * This is also an effective way to shrink FIFO ordered pools that 491 * experience load spikes. 492 * <p> 493 * The factor parameter provides a mechanism to tweak the rate at which the 494 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 495 * try to shrink its size more often. Values greater than 1 cause the pool 496 * to less frequently try to shrink its size. 497 * </p> 498 * 499 * @param keyedPool 500 * the KeyedObjectPool to be decorated so it shrinks its idle 501 * count when possible. 502 * @param factor 503 * a positive value to scale the rate at which the pool tries to 504 * reduce its size. If 0 < factor < 1 then the pool 505 * shrinks more aggressively. If 1 < factor then the pool 506 * shrinks less aggressively. 507 * @param <K> the type of the pool key 508 * @param <V> the type of pool entries 509 * @return a pool that adaptively decreases its size when idle objects are 510 * no longer needed. 511 * @see #erodingPool(KeyedObjectPool, float, bool) 512 */ 513 static KeyedObjectPool!(K, V) erodingPool(K, V)( 514 KeyedObjectPool!(K, V) keyedPool, float factor) { 515 return erodingPool(keyedPool, factor, false); 516 } 517 518 /** 519 * Returns a pool that adaptively decreases its size when idle objects are 520 * no longer needed. This is intended as an always thread-safe alternative 521 * to using an idle object evictor provided by many pool implementations. 522 * This is also an effective way to shrink FIFO ordered pools that 523 * experience load spikes. 524 * <p> 525 * The factor parameter provides a mechanism to tweak the rate at which the 526 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 527 * try to shrink its size more often. Values greater than 1 cause the pool 528 * to less frequently try to shrink its size. 529 * </p> 530 * <p> 531 * The perKey parameter determines if the pool shrinks on a whole pool basis 532 * or a per key basis. When perKey is false, the keys do not have an effect 533 * on the rate at which the pool tries to shrink its size. When perKey is 534 * true, each key is shrunk independently. 535 * </p> 536 * 537 * @param keyedPool 538 * the KeyedObjectPool to be decorated so it shrinks its idle 539 * count when possible. 540 * @param factor 541 * a positive value to scale the rate at which the pool tries to 542 * reduce its size. If 0 < factor < 1 then the pool 543 * shrinks more aggressively. If 1 < factor then the pool 544 * shrinks less aggressively. 545 * @param perKey 546 * when true, each key is treated independently. 547 * @param <K> the type of the pool key 548 * @param <V> the type of pool entries 549 * @return a pool that adaptively decreases its size when idle objects are 550 * no longer needed. 551 * @see #erodingPool(KeyedObjectPool) 552 * @see #erodingPool(KeyedObjectPool, float) 553 */ 554 static KeyedObjectPool!(K, V) erodingPool(K, V)( 555 KeyedObjectPool!(K, V) keyedPool, float factor, bool perKey) { 556 if (keyedPool is null) { 557 throw new IllegalArgumentException(MSG_NULL_KEYED_POOL); 558 } 559 if (factor <= 0f) { 560 throw new IllegalArgumentException(MSG_FACTOR_NEGATIVE); 561 } 562 if (perKey) { 563 return new ErodingPerKeyKeyedObjectPool!(K, V)(keyedPool, factor); 564 } 565 return new ErodingKeyedObjectPool!(K, V)(keyedPool, factor); 566 } 567 568 /** 569 * Gets the <code>Timer</code> for checking keyedPool's idle count. 570 * 571 * @return the {@link Timer} for checking keyedPool's idle count. 572 */ 573 private static ScheduledExecutorService getMinIdleTimer() { 574 // return TimerHolder.MIN_IDLE_TIMER; 575 __gshared ScheduledExecutorService inst; 576 return initOnce!inst(Executors.newScheduledThreadPool(1)); 577 } 578 579 580 /** 581 * Timer task that adds objects to the pool until the number of idle 582 * instances reaches the configured minIdle. Note that this is not the same 583 * as the pool's minIdle setting. 584 * 585 * @param <T> type of objects in the pool 586 */ 587 private static class ObjectPoolMinIdleTimerTask(T) : TimerTask { 588 589 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ 590 private int minIdle; 591 592 /** Object pool */ 593 private ObjectPool!(T) pool; 594 595 /** 596 * Create a new ObjectPoolMinIdleTimerTask for the given pool with the 597 * given minIdle setting. 598 * 599 * @param pool 600 * object pool 601 * @param minIdle 602 * number of idle instances to maintain 603 * @throws IllegalArgumentException 604 * if the pool is null 605 */ 606 this(ObjectPool!(T) pool, int minIdle) { 607 if (pool is null) { 608 throw new IllegalArgumentException(MSG_NULL_POOL); 609 } 610 this.pool = pool; 611 this.minIdle = minIdle; 612 } 613 614 /** 615 * {@inheritDoc} 616 */ 617 override 618 void run() { 619 bool success = false; 620 try { 621 if (pool.getNumIdle() < minIdle) { 622 pool.addObject(); 623 } 624 success = true; 625 626 } catch (Exception e) { 627 cancel(); 628 } finally { 629 // detect other types of Throwable and cancel this Timer 630 if (!success) { 631 cancel(); 632 } 633 } 634 } 635 636 /** 637 * {@inheritDoc} 638 */ 639 override 640 string toString() { 641 StringBuilder sb = new StringBuilder(); 642 sb.append("ObjectPoolMinIdleTimerTask"); 643 sb.append("{minIdle=").append(minIdle); 644 sb.append(", pool=").append(pool); 645 sb.append('}'); 646 return sb.toString(); 647 } 648 } 649 650 /** 651 * Timer task that adds objects to the pool until the number of idle 652 * instances for the given key reaches the configured minIdle. Note that 653 * this is not the same as the pool's minIdle setting. 654 * 655 * @param <K> object pool key type 656 * @param <V> object pool value type 657 */ 658 private static class KeyedObjectPoolMinIdleTimerTask(K, V) : TimerTask { 659 660 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ 661 private int minIdle; 662 663 /** Key to ensure minIdle for */ 664 private K key; 665 666 /** Keyed object pool */ 667 private KeyedObjectPool!(K, V) keyedPool; 668 669 /** 670 * Creates a new KeyedObjecPoolMinIdleTimerTask. 671 * 672 * @param keyedPool 673 * keyed object pool 674 * @param key 675 * key to ensure minimum number of idle instances 676 * @param minIdle 677 * minimum number of idle instances 678 * @throws IllegalArgumentException 679 * if the key is null 680 */ 681 this(KeyedObjectPool!(K, V) keyedPool, 682 K key, int minIdle){ 683 if (keyedPool is null) { 684 throw new IllegalArgumentException( 685 MSG_NULL_KEYED_POOL); 686 } 687 this.keyedPool = keyedPool; 688 this.key = key; 689 this.minIdle = minIdle; 690 } 691 692 /** 693 * {@inheritDoc} 694 */ 695 override 696 void run() { 697 bool success = false; 698 try { 699 if (keyedPool.getNumIdle(key) < minIdle) { 700 keyedPool.addObject(key); 701 } 702 success = true; 703 704 } catch (Exception e) { 705 cancel(); 706 707 } finally { 708 // detect other types of Throwable and cancel this Timer 709 if (!success) { 710 cancel(); 711 } 712 } 713 } 714 715 /** 716 * {@inheritDoc} 717 */ 718 override 719 string toString() { 720 StringBuilder sb = new StringBuilder(); 721 sb.append("KeyedObjectPoolMinIdleTimerTask"); 722 sb.append("{minIdle=").append(minIdle); 723 sb.append(", key=").append(key); 724 sb.append(", keyedPool=").append(keyedPool); 725 sb.append('}'); 726 return sb.toString(); 727 } 728 } 729 730 /** 731 * A synchronized (thread-safe) ObjectPool backed by the specified 732 * ObjectPool. 733 * <p> 734 * <b>Note:</b> This should not be used on pool implementations that already 735 * provide proper synchronization such as the pools provided in the Commons 736 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 737 * objects to be returned before allowing another one to be borrowed with 738 * another layer of synchronization will cause liveliness issues or a 739 * deadlock. 740 * </p> 741 * 742 * @param <T> type of objects in the pool 743 */ 744 private static class SynchronizedObjectPool(T) : ObjectPool!(T) { 745 746 /** 747 * Object whose monitor is used to synchronize methods on the wrapped 748 * pool. 749 */ 750 private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 751 752 /** the underlying object pool */ 753 private ObjectPool!(T) pool; 754 755 /** 756 * Creates a new SynchronizedObjectPool wrapping the given pool. 757 * 758 * @param pool 759 * the ObjectPool to be "wrapped" in a synchronized 760 * ObjectPool. 761 * @throws IllegalArgumentException 762 * if the pool is null 763 */ 764 this(ObjectPool!(T) pool) { 765 if (pool is null) { 766 throw new IllegalArgumentException(MSG_NULL_POOL); 767 } 768 this.pool = pool; 769 } 770 771 /** 772 * {@inheritDoc} 773 */ 774 override 775 T borrowObject() { 776 WriteLock writeLock = readWriteLock.writeLock(); 777 writeLock.lock(); 778 try { 779 return pool.borrowObject(); 780 } finally { 781 writeLock.unlock(); 782 } 783 } 784 785 /** 786 * {@inheritDoc} 787 */ 788 override 789 void returnObject(T obj) { 790 WriteLock writeLock = readWriteLock.writeLock(); 791 writeLock.lock(); 792 try { 793 pool.returnObject(obj); 794 } catch (Exception e) { 795 // swallowed as of Pool 2 796 } finally { 797 writeLock.unlock(); 798 } 799 } 800 801 /** 802 * {@inheritDoc} 803 */ 804 override 805 void invalidateObject(T obj) { 806 WriteLock writeLock = readWriteLock.writeLock(); 807 writeLock.lock(); 808 try { 809 pool.invalidateObject(obj); 810 } catch (Exception e) { 811 // swallowed as of Pool 2 812 } finally { 813 writeLock.unlock(); 814 } 815 } 816 817 /** 818 * {@inheritDoc} 819 */ 820 override 821 void addObject() { 822 WriteLock writeLock = readWriteLock.writeLock(); 823 writeLock.lock(); 824 try { 825 pool.addObject(); 826 } finally { 827 writeLock.unlock(); 828 } 829 } 830 831 /** 832 * {@inheritDoc} 833 */ 834 override 835 int getNumIdle() { 836 ReadLock readLock = readWriteLock.readLock(); 837 readLock.lock(); 838 try { 839 return pool.getNumIdle(); 840 } finally { 841 readLock.unlock(); 842 } 843 } 844 845 /** 846 * {@inheritDoc} 847 */ 848 override 849 int getNumActive() { 850 ReadLock readLock = readWriteLock.readLock(); 851 readLock.lock(); 852 try { 853 return pool.getNumActive(); 854 } finally { 855 readLock.unlock(); 856 } 857 } 858 859 /** 860 * {@inheritDoc} 861 */ 862 override 863 void clear(){ 864 WriteLock writeLock = readWriteLock.writeLock(); 865 writeLock.lock(); 866 try { 867 pool.clear(); 868 } finally { 869 writeLock.unlock(); 870 } 871 } 872 873 /** 874 * {@inheritDoc} 875 */ 876 override 877 void close() { 878 WriteLock writeLock = readWriteLock.writeLock(); 879 writeLock.lock(); 880 try { 881 pool.close(); 882 } catch (Exception e) { 883 // swallowed as of Pool 2 884 } finally { 885 writeLock.unlock(); 886 } 887 } 888 889 /** 890 * {@inheritDoc} 891 */ 892 override 893 string toString() { 894 StringBuilder sb = new StringBuilder(); 895 sb.append("SynchronizedObjectPool"); 896 sb.append("{pool=").append(pool); 897 sb.append('}'); 898 return sb.toString(); 899 } 900 } 901 902 /** 903 * A synchronized (thread-safe) KeyedObjectPool backed by the specified 904 * KeyedObjectPool. 905 * <p> 906 * <b>Note:</b> This should not be used on pool implementations that already 907 * provide proper synchronization such as the pools provided in the Commons 908 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 909 * objects to be returned before allowing another one to be borrowed with 910 * another layer of synchronization will cause liveliness issues or a 911 * deadlock. 912 * </p> 913 * 914 * @param <K> object pool key type 915 * @param <V> object pool value type 916 */ 917 private static class SynchronizedKeyedObjectPool(K, V) : 918 KeyedObjectPool!(K, V) { 919 920 /** 921 * Object whose monitor is used to synchronize methods on the wrapped 922 * pool. 923 */ 924 private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 925 926 /** Underlying object pool */ 927 private KeyedObjectPool!(K, V) keyedPool; 928 929 /** 930 * Creates a new SynchronizedKeyedObjectPool wrapping the given pool 931 * 932 * @param keyedPool 933 * KeyedObjectPool to wrap 934 * @throws IllegalArgumentException 935 * if keyedPool is null 936 */ 937 this(KeyedObjectPool!(K, V) keyedPool) { 938 if (keyedPool is null) { 939 throw new IllegalArgumentException( 940 MSG_NULL_KEYED_POOL); 941 } 942 this.keyedPool = keyedPool; 943 } 944 945 /** 946 * {@inheritDoc} 947 */ 948 override 949 V borrowObject(K key) { 950 WriteLock writeLock = readWriteLock.writeLock(); 951 writeLock.lock(); 952 try { 953 return keyedPool.borrowObject(key); 954 } finally { 955 writeLock.unlock(); 956 } 957 } 958 959 /** 960 * {@inheritDoc} 961 */ 962 override 963 void returnObject(K key, V obj) { 964 WriteLock writeLock = readWriteLock.writeLock(); 965 writeLock.lock(); 966 try { 967 keyedPool.returnObject(key, obj); 968 } catch (Exception e) { 969 // swallowed 970 } finally { 971 writeLock.unlock(); 972 } 973 } 974 975 /** 976 * {@inheritDoc} 977 */ 978 override 979 void invalidateObject(K key, V obj) { 980 WriteLock writeLock = readWriteLock.writeLock(); 981 writeLock.lock(); 982 try { 983 keyedPool.invalidateObject(key, obj); 984 } catch (Exception e) { 985 // swallowed as of Pool 2 986 } finally { 987 writeLock.unlock(); 988 } 989 } 990 991 /** 992 * {@inheritDoc} 993 */ 994 override 995 void addObject(K key) { 996 WriteLock writeLock = readWriteLock.writeLock(); 997 writeLock.lock(); 998 try { 999 keyedPool.addObject(key); 1000 } finally { 1001 writeLock.unlock(); 1002 } 1003 } 1004 1005 /** 1006 * {@inheritDoc} 1007 */ 1008 override 1009 int getNumIdle(K key) { 1010 ReadLock readLock = readWriteLock.readLock(); 1011 readLock.lock(); 1012 try { 1013 return keyedPool.getNumIdle(key); 1014 } finally { 1015 readLock.unlock(); 1016 } 1017 } 1018 1019 /** 1020 * {@inheritDoc} 1021 */ 1022 override 1023 int getNumActive(K key) { 1024 ReadLock readLock = readWriteLock.readLock(); 1025 readLock.lock(); 1026 try { 1027 return keyedPool.getNumActive(key); 1028 } finally { 1029 readLock.unlock(); 1030 } 1031 } 1032 1033 /** 1034 * {@inheritDoc} 1035 */ 1036 override 1037 int getNumIdle() { 1038 ReadLock readLock = readWriteLock.readLock(); 1039 readLock.lock(); 1040 try { 1041 return keyedPool.getNumIdle(); 1042 } finally { 1043 readLock.unlock(); 1044 } 1045 } 1046 1047 /** 1048 * {@inheritDoc} 1049 */ 1050 override 1051 int getNumActive() { 1052 ReadLock readLock = readWriteLock.readLock(); 1053 readLock.lock(); 1054 try { 1055 return keyedPool.getNumActive(); 1056 } finally { 1057 readLock.unlock(); 1058 } 1059 } 1060 1061 /** 1062 * {@inheritDoc} 1063 */ 1064 override 1065 void clear(){ 1066 WriteLock writeLock = readWriteLock.writeLock(); 1067 writeLock.lock(); 1068 try { 1069 keyedPool.clear(); 1070 } finally { 1071 writeLock.unlock(); 1072 } 1073 } 1074 1075 /** 1076 * {@inheritDoc} 1077 */ 1078 override 1079 void clear(K key) { 1080 WriteLock writeLock = readWriteLock.writeLock(); 1081 writeLock.lock(); 1082 try { 1083 keyedPool.clear(key); 1084 } finally { 1085 writeLock.unlock(); 1086 } 1087 } 1088 1089 /** 1090 * {@inheritDoc} 1091 */ 1092 override 1093 void close() { 1094 WriteLock writeLock = readWriteLock.writeLock(); 1095 writeLock.lock(); 1096 try { 1097 keyedPool.close(); 1098 } catch (Exception e) { 1099 // swallowed as of Pool 2 1100 } finally { 1101 writeLock.unlock(); 1102 } 1103 } 1104 1105 /** 1106 * {@inheritDoc} 1107 */ 1108 override 1109 string toString() { 1110 StringBuilder sb = new StringBuilder(); 1111 sb.append("SynchronizedKeyedObjectPool"); 1112 sb.append("{keyedPool=").append(keyedPool); 1113 sb.append('}'); 1114 return sb.toString(); 1115 } 1116 } 1117 1118 /** 1119 * A fully synchronized PooledObjectFactory that wraps a 1120 * PooledObjectFactory and synchronizes access to the wrapped factory 1121 * methods. 1122 * <p> 1123 * <b>Note:</b> This should not be used on pool implementations that already 1124 * provide proper synchronization such as the pools provided in the Commons 1125 * Pool library. 1126 * </p> 1127 * 1128 * @param <T> pooled object factory type 1129 */ 1130 private static class SynchronizedPooledObjectFactory(T) : PooledObjectFactory!(T) { 1131 1132 /** Synchronization lock */ 1133 private WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); 1134 1135 /** Wrapped factory */ 1136 private PooledObjectFactory!(T) factory; 1137 1138 /** 1139 * Creates a SynchronizedPoolableObjectFactory wrapping the given 1140 * factory. 1141 * 1142 * @param factory 1143 * underlying factory to wrap 1144 * @throws IllegalArgumentException 1145 * if the factory is null 1146 */ 1147 this(PooledObjectFactory!(T) factory) { 1148 if (factory is null) { 1149 throw new IllegalArgumentException("factory must not be null."); 1150 } 1151 this.factory = factory; 1152 } 1153 1154 /** 1155 * {@inheritDoc} 1156 */ 1157 override 1158 PooledObject!(T) makeObject(){ 1159 writeLock.lock(); 1160 try { 1161 return factory.makeObject(); 1162 } finally { 1163 writeLock.unlock(); 1164 } 1165 } 1166 1167 /** 1168 * {@inheritDoc} 1169 */ 1170 override 1171 void destroyObject(PooledObject!(T) p){ 1172 writeLock.lock(); 1173 try { 1174 factory.destroyObject(p); 1175 } finally { 1176 writeLock.unlock(); 1177 } 1178 } 1179 1180 /** 1181 * {@inheritDoc} 1182 */ 1183 override 1184 bool validateObject(PooledObject!(T) p) { 1185 writeLock.lock(); 1186 try { 1187 return factory.validateObject(p); 1188 } finally { 1189 writeLock.unlock(); 1190 } 1191 } 1192 1193 /** 1194 * {@inheritDoc} 1195 */ 1196 override 1197 void activateObject(PooledObject!(T) p){ 1198 writeLock.lock(); 1199 try { 1200 factory.activateObject(p); 1201 } finally { 1202 writeLock.unlock(); 1203 } 1204 } 1205 1206 /** 1207 * {@inheritDoc} 1208 */ 1209 override 1210 void passivateObject(PooledObject!(T) p){ 1211 writeLock.lock(); 1212 try { 1213 factory.passivateObject(p); 1214 } finally { 1215 writeLock.unlock(); 1216 } 1217 } 1218 1219 /** 1220 * {@inheritDoc} 1221 */ 1222 override 1223 string toString() { 1224 StringBuilder sb = new StringBuilder(); 1225 sb.append("SynchronizedPoolableObjectFactory"); 1226 sb.append("{factory=").append(factory); 1227 sb.append('}'); 1228 return sb.toString(); 1229 } 1230 } 1231 1232 /** 1233 * A fully synchronized KeyedPooledObjectFactory that wraps a 1234 * KeyedPooledObjectFactory and synchronizes access to the wrapped factory 1235 * methods. 1236 * <p> 1237 * <b>Note:</b> This should not be used on pool implementations that already 1238 * provide proper synchronization such as the pools provided in the Commons 1239 * Pool library. 1240 * </p> 1241 * 1242 * @param <K> pooled object factory key type 1243 * @param <V> pooled object factory key value 1244 */ 1245 private static class SynchronizedKeyedPooledObjectFactory(K, V) : KeyedPooledObjectFactory!(K, V) { 1246 1247 /** Synchronization lock */ 1248 private WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); 1249 1250 /** Wrapped factory */ 1251 private KeyedPooledObjectFactory!(K, V) keyedFactory; 1252 1253 /** 1254 * Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given 1255 * factory. 1256 * 1257 * @param keyedFactory 1258 * underlying factory to wrap 1259 * @throws IllegalArgumentException 1260 * if the factory is null 1261 */ 1262 this(KeyedPooledObjectFactory!(K, V) keyedFactory) { 1263 if (keyedFactory is null) { 1264 throw new IllegalArgumentException( 1265 "keyedFactory must not be null."); 1266 } 1267 this.keyedFactory = keyedFactory; 1268 } 1269 1270 /** 1271 * {@inheritDoc} 1272 */ 1273 override 1274 PooledObject!(V) makeObject(K key){ 1275 writeLock.lock(); 1276 try { 1277 return keyedFactory.makeObject(key); 1278 } finally { 1279 writeLock.unlock(); 1280 } 1281 } 1282 1283 /** 1284 * {@inheritDoc} 1285 */ 1286 override 1287 void destroyObject(K key, PooledObject!(V) p){ 1288 writeLock.lock(); 1289 try { 1290 keyedFactory.destroyObject(key, p); 1291 } finally { 1292 writeLock.unlock(); 1293 } 1294 } 1295 1296 /** 1297 * {@inheritDoc} 1298 */ 1299 override 1300 bool validateObject(K key, PooledObject!(V) p) { 1301 writeLock.lock(); 1302 try { 1303 return keyedFactory.validateObject(key, p); 1304 } finally { 1305 writeLock.unlock(); 1306 } 1307 } 1308 1309 /** 1310 * {@inheritDoc} 1311 */ 1312 override 1313 void activateObject(K key, PooledObject!(V) p){ 1314 writeLock.lock(); 1315 try { 1316 keyedFactory.activateObject(key, p); 1317 } finally { 1318 writeLock.unlock(); 1319 } 1320 } 1321 1322 /** 1323 * {@inheritDoc} 1324 */ 1325 override 1326 void passivateObject(K key, PooledObject!(V) p){ 1327 writeLock.lock(); 1328 try { 1329 keyedFactory.passivateObject(key, p); 1330 } finally { 1331 writeLock.unlock(); 1332 } 1333 } 1334 1335 /** 1336 * {@inheritDoc} 1337 */ 1338 override 1339 string toString() { 1340 StringBuilder sb = new StringBuilder(); 1341 sb.append("SynchronizedKeyedPoolableObjectFactory"); 1342 sb.append("{keyedFactory=").append(keyedFactory); 1343 sb.append('}'); 1344 return sb.toString(); 1345 } 1346 } 1347 1348 /** 1349 * Encapsulate the logic for when the next poolable object should be 1350 * discarded. Each time update is called, the next time to shrink is 1351 * recomputed, based on the float factor, number of idle instances in the 1352 * pool and high water mark. Float factor is assumed to be between 0 and 1. 1353 * Values closer to 1 cause less frequent erosion events. Erosion event 1354 * timing also depends on numIdle. When this value is relatively high (close 1355 * to previously established high water mark), erosion occurs more 1356 * frequently. 1357 */ 1358 private static class ErodingFactor { 1359 /** Determines frequency of "erosion" events */ 1360 private float factor; 1361 1362 /** Time of next shrink event */ 1363 private shared long nextShrink; 1364 1365 /** High water mark - largest numIdle encountered */ 1366 private shared int idleHighWaterMark; 1367 1368 /** 1369 * Creates a new ErodingFactor with the given erosion factor. 1370 * 1371 * @param factor 1372 * erosion factor 1373 */ 1374 this(float factor) { 1375 this.factor = factor; 1376 nextShrink = DateTime.currentTimeMillis() + cast(long) (900000 * factor); // now 1377 // + 1378 // 15 1379 // min 1380 // * 1381 // factor 1382 idleHighWaterMark = 1; 1383 } 1384 1385 /** 1386 * Updates internal state using the supplied time and numIdle. 1387 * 1388 * @param now 1389 * current time 1390 * @param numIdle 1391 * number of idle elements in the pool 1392 */ 1393 void update(long now, int numIdle) { 1394 int idle = max(0, numIdle); 1395 idleHighWaterMark = max(idle, idleHighWaterMark); 1396 float maxInterval = 15f; 1397 float minutes = maxInterval + 1398 ((1f - maxInterval) / idleHighWaterMark) * idle; 1399 nextShrink = now + cast(long) (minutes * 60000f * factor); 1400 } 1401 1402 /** 1403 * Returns the time of the next erosion event. 1404 * 1405 * @return next shrink time 1406 */ 1407 long getNextShrink() { 1408 return nextShrink; 1409 } 1410 1411 /** 1412 * {@inheritDoc} 1413 */ 1414 override 1415 string toString() { 1416 return "ErodingFactor{" ~ "factor=" ~ factor.to!string() ~ 1417 ", idleHighWaterMark=" ~ idleHighWaterMark.to!string() ~ "}"; 1418 } 1419 } 1420 1421 /** 1422 * Decorates an object pool, adding "eroding" behavior. Based on the 1423 * configured {@link #factor erosion factor}, objects returning to the pool 1424 * may be invalidated instead of being added to idle capacity. 1425 * 1426 * @param <T> type of objects in the pool 1427 */ 1428 private static class ErodingObjectPool(T) : ObjectPool!(T) { 1429 1430 /** Underlying object pool */ 1431 private ObjectPool!(T) pool; 1432 1433 /** Erosion factor */ 1434 private ErodingFactor factor; 1435 1436 /** 1437 * Creates an ErodingObjectPool wrapping the given pool using the 1438 * specified erosion factor. 1439 * 1440 * @param pool 1441 * underlying pool 1442 * @param factor 1443 * erosion factor - determines the frequency of erosion 1444 * events 1445 * @see #factor 1446 */ 1447 this(ObjectPool!(T) pool, float factor) { 1448 this.pool = pool; 1449 this.factor = new ErodingFactor(factor); 1450 } 1451 1452 /** 1453 * {@inheritDoc} 1454 */ 1455 override 1456 T borrowObject() { 1457 return pool.borrowObject(); 1458 } 1459 1460 /** 1461 * Returns obj to the pool, unless erosion is triggered, in which case 1462 * obj is invalidated. Erosion is triggered when there are idle 1463 * instances in the pool and more than the {@link #factor erosion 1464 * factor}-determined time has elapsed since the last returnObject 1465 * activation. 1466 * 1467 * @param obj 1468 * object to return or invalidate 1469 * @see #factor 1470 */ 1471 override 1472 void returnObject(T obj) { 1473 bool discard = false; 1474 long now = DateTime.currentTimeMillis(); 1475 synchronized (pool) { 1476 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test 1477 // out of sync block 1478 int numIdle = pool.getNumIdle(); 1479 if (numIdle > 0) { 1480 discard = true; 1481 } 1482 1483 factor.update(now, numIdle); 1484 } 1485 } 1486 try { 1487 if (discard) { 1488 pool.invalidateObject(obj); 1489 } else { 1490 pool.returnObject(obj); 1491 } 1492 } catch (Exception e) { 1493 // swallowed 1494 } 1495 } 1496 1497 /** 1498 * {@inheritDoc} 1499 */ 1500 override 1501 void invalidateObject(T obj) { 1502 try { 1503 pool.invalidateObject(obj); 1504 } catch (Exception e) { 1505 // swallowed 1506 } 1507 } 1508 1509 /** 1510 * {@inheritDoc} 1511 */ 1512 override 1513 void addObject() { 1514 pool.addObject(); 1515 } 1516 1517 /** 1518 * {@inheritDoc} 1519 */ 1520 override 1521 int getNumIdle() { 1522 return pool.getNumIdle(); 1523 } 1524 1525 /** 1526 * {@inheritDoc} 1527 */ 1528 override 1529 int getNumActive() { 1530 return pool.getNumActive(); 1531 } 1532 1533 /** 1534 * {@inheritDoc} 1535 */ 1536 override 1537 void clear(){ 1538 pool.clear(); 1539 } 1540 1541 /** 1542 * {@inheritDoc} 1543 */ 1544 override 1545 void close() { 1546 try { 1547 pool.close(); 1548 } catch (Exception e) { 1549 // swallowed 1550 } 1551 } 1552 1553 /** 1554 * {@inheritDoc} 1555 */ 1556 override 1557 string toString() { 1558 return "ErodingObjectPool{" ~ "factor=" ~ factor ~ ", pool=" ~ 1559 pool + '}'; 1560 } 1561 } 1562 1563 /** 1564 * Decorates a keyed object pool, adding "eroding" behavior. Based on the 1565 * configured erosion factor, objects returning to the pool 1566 * may be invalidated instead of being added to idle capacity. 1567 * 1568 * @param <K> object pool key type 1569 * @param <V> object pool value type 1570 */ 1571 private static class ErodingKeyedObjectPool(K, V) : KeyedObjectPool!(K, V) { 1572 1573 /** Underlying pool */ 1574 private KeyedObjectPool!(K, V) keyedPool; 1575 1576 /** Erosion factor */ 1577 private ErodingFactor erodingFactor; 1578 1579 /** 1580 * Creates an ErodingObjectPool wrapping the given pool using the 1581 * specified erosion factor. 1582 * 1583 * @param keyedPool 1584 * underlying pool 1585 * @param factor 1586 * erosion factor - determines the frequency of erosion 1587 * events 1588 * @see #erodingFactor 1589 */ 1590 this(KeyedObjectPool!(K, V) keyedPool, float factor) { 1591 this(keyedPool, new ErodingFactor(factor)); 1592 } 1593 1594 /** 1595 * Creates an ErodingObjectPool wrapping the given pool using the 1596 * specified erosion factor. 1597 * 1598 * @param keyedPool 1599 * underlying pool - must not be null 1600 * @param erodingFactor 1601 * erosion factor - determines the frequency of erosion 1602 * events 1603 * @see #erodingFactor 1604 */ 1605 protected this(KeyedObjectPool!(K, V) keyedPool, 1606 ErodingFactor erodingFactor) { 1607 if (keyedPool is null) { 1608 throw new IllegalArgumentException( 1609 MSG_NULL_KEYED_POOL); 1610 } 1611 this.keyedPool = keyedPool; 1612 this.erodingFactor = erodingFactor; 1613 } 1614 1615 /** 1616 * {@inheritDoc} 1617 */ 1618 override 1619 V borrowObject(K key) { 1620 return keyedPool.borrowObject(key); 1621 } 1622 1623 /** 1624 * Returns obj to the pool, unless erosion is triggered, in which case 1625 * obj is invalidated. Erosion is triggered when there are idle 1626 * instances in the pool associated with the given key and more than the 1627 * configured {@link #erodingFactor erosion factor} time has elapsed 1628 * since the last returnObject activation. 1629 * 1630 * @param obj 1631 * object to return or invalidate 1632 * @param key 1633 * key 1634 * @see #erodingFactor 1635 */ 1636 override 1637 void returnObject(K key, V obj){ 1638 bool discard = false; 1639 long now = DateTime.currentTimeMillis(); 1640 ErodingFactor factor = getErodingFactor(key); 1641 synchronized (keyedPool) { 1642 if (factor.getNextShrink() < now) { 1643 int numIdle = getNumIdle(key); 1644 if (numIdle > 0) { 1645 discard = true; 1646 } 1647 1648 factor.update(now, numIdle); 1649 } 1650 } 1651 try { 1652 if (discard) { 1653 keyedPool.invalidateObject(key, obj); 1654 } else { 1655 keyedPool.returnObject(key, obj); 1656 } 1657 } catch (Exception e) { 1658 // swallowed 1659 } 1660 } 1661 1662 /** 1663 * Returns the eroding factor for the given key 1664 * 1665 * @param key 1666 * key 1667 * @return eroding factor for the given keyed pool 1668 */ 1669 protected ErodingFactor getErodingFactor(K key) { 1670 return erodingFactor; 1671 } 1672 1673 /** 1674 * {@inheritDoc} 1675 */ 1676 override 1677 void invalidateObject(K key, V obj) { 1678 try { 1679 keyedPool.invalidateObject(key, obj); 1680 } catch (Exception e) { 1681 // swallowed 1682 } 1683 } 1684 1685 /** 1686 * {@inheritDoc} 1687 */ 1688 override 1689 void addObject(K key) { 1690 keyedPool.addObject(key); 1691 } 1692 1693 /** 1694 * {@inheritDoc} 1695 */ 1696 override 1697 int getNumIdle() { 1698 return keyedPool.getNumIdle(); 1699 } 1700 1701 /** 1702 * {@inheritDoc} 1703 */ 1704 override 1705 int getNumIdle(K key) { 1706 return keyedPool.getNumIdle(key); 1707 } 1708 1709 /** 1710 * {@inheritDoc} 1711 */ 1712 override 1713 int getNumActive() { 1714 return keyedPool.getNumActive(); 1715 } 1716 1717 /** 1718 * {@inheritDoc} 1719 */ 1720 override 1721 int getNumActive(K key) { 1722 return keyedPool.getNumActive(key); 1723 } 1724 1725 /** 1726 * {@inheritDoc} 1727 */ 1728 override 1729 void clear(){ 1730 keyedPool.clear(); 1731 } 1732 1733 /** 1734 * {@inheritDoc} 1735 */ 1736 override 1737 void clear(K key) { 1738 keyedPool.clear(key); 1739 } 1740 1741 /** 1742 * {@inheritDoc} 1743 */ 1744 override 1745 void close() { 1746 try { 1747 keyedPool.close(); 1748 } catch (Exception e) { 1749 // swallowed 1750 } 1751 } 1752 1753 /** 1754 * Returns the underlying pool 1755 * 1756 * @return the keyed pool that this ErodingKeyedObjectPool wraps 1757 */ 1758 protected KeyedObjectPool!(K, V) getKeyedPool() { 1759 return keyedPool; 1760 } 1761 1762 /** 1763 * {@inheritDoc} 1764 */ 1765 override 1766 string toString() { 1767 return "ErodingKeyedObjectPool{" ~ "factor=" ~ 1768 erodingFactor ~ ", keyedPool=" ~ keyedPool + '}'; 1769 } 1770 } 1771 1772 /** 1773 * Extends ErodingKeyedObjectPool to allow erosion to take place on a 1774 * per-key basis. Timing of erosion events is tracked separately for 1775 * separate keyed pools. 1776 * 1777 * @param <K> object pool key type 1778 * @param <V> object pool value type 1779 */ 1780 private static class ErodingPerKeyKeyedObjectPool(K, V) : ErodingKeyedObjectPool!(K, V) { 1781 1782 /** Erosion factor - same for all pools */ 1783 private float factor; 1784 1785 /** Map of ErodingFactor instances keyed on pool keys */ 1786 private Map!(K, ErodingFactor) factors = Collections.synchronizedMap(new HashMap!(K, ErodingFactor)()); 1787 1788 /** 1789 * Creates a new ErordingPerKeyKeyedObjectPool decorating the given keyed 1790 * pool with the specified erosion factor. 1791 * 1792 * @param keyedPool 1793 * underlying keyed pool 1794 * @param factor 1795 * erosion factor 1796 */ 1797 this(KeyedObjectPool!(K, V) keyedPool, float factor) { 1798 super(keyedPool, null); 1799 this.factor = factor; 1800 } 1801 1802 /** 1803 * {@inheritDoc} 1804 */ 1805 override 1806 protected ErodingFactor getErodingFactor(K key) { 1807 ErodingFactor eFactor = factors.get(key); 1808 // this may result in two ErodingFactors being created for a key 1809 // since they are small and cheap this is okay. 1810 if (eFactor is null) { 1811 eFactor = new ErodingFactor(this.factor); 1812 factors.put(key, eFactor); 1813 } 1814 return eFactor; 1815 } 1816 1817 /** 1818 * {@inheritDoc} 1819 */ 1820 override 1821 string toString() { 1822 return "ErodingPerKeyKeyedObjectPool{" ~ "factor=" ~ factor ~ 1823 ", keyedPool=" ~ getKeyedPool() + "}"; 1824 } 1825 } 1826 }