1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.pool2.impl;
18
19 import java.time.Duration;
20 import java.time.Instant;
21 import java.util.ArrayList;
22 import java.util.Deque;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.NoSuchElementException;
29 import java.util.Objects;
30 import java.util.TreeMap;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.atomic.AtomicBoolean;
34 import java.util.concurrent.atomic.AtomicInteger;
35 import java.util.concurrent.atomic.AtomicLong;
36 import java.util.concurrent.locks.Lock;
37 import java.util.concurrent.locks.ReadWriteLock;
38 import java.util.concurrent.locks.ReentrantReadWriteLock;
39 import java.util.stream.Collectors;
40
41 import org.apache.commons.pool2.DestroyMode;
42 import org.apache.commons.pool2.KeyedObjectPool;
43 import org.apache.commons.pool2.KeyedPooledObjectFactory;
44 import org.apache.commons.pool2.PoolUtils;
45 import org.apache.commons.pool2.PooledObject;
46 import org.apache.commons.pool2.PooledObjectState;
47 import org.apache.commons.pool2.SwallowedExceptionListener;
48 import org.apache.commons.pool2.UsageTracking;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T>
93 implements KeyedObjectPool<K, T>, GenericKeyedObjectPoolMXBean<K>, UsageTracking<T> {
94
95
96
97
98
99
100 private static class ObjectDeque<S> {
101
102 private final LinkedBlockingDeque<PooledObject<S>> idleObjects;
103
104
105
106
107
108 private final AtomicInteger createCount = new AtomicInteger(0);
109
110 private long makeObjectCount;
111 private final Object makeObjectCountLock = new Object();
112
113
114
115
116
117 private final Map<IdentityWrapper<S>, PooledObject<S>> allObjects =
118 new ConcurrentHashMap<>();
119
120
121
122
123
124
125
126 private final AtomicLong numInterested = new AtomicLong();
127
128
129
130
131
132
133 public ObjectDeque(final boolean fairness) {
134 idleObjects = new LinkedBlockingDeque<>(fairness);
135 }
136
137
138
139
140
141
142 public Map<IdentityWrapper<S>, PooledObject<S>> getAllObjects() {
143 return allObjects;
144 }
145
146
147
148
149
150
151
152 public AtomicInteger getCreateCount() {
153 return createCount;
154 }
155
156
157
158
159
160
161 public LinkedBlockingDeque<PooledObject<S>> getIdleObjects() {
162 return idleObjects;
163 }
164
165
166
167
168
169
170 public AtomicLong getNumInterested() {
171 return numInterested;
172 }
173
174 @Override
175 public String toString() {
176 final StringBuilder builder = new StringBuilder();
177 builder.append("ObjectDeque [idleObjects=");
178 builder.append(idleObjects);
179 builder.append(", createCount=");
180 builder.append(createCount);
181 builder.append(", allObjects=");
182 builder.append(allObjects);
183 builder.append(", numInterested=");
184 builder.append(numInterested);
185 builder.append("]");
186 return builder.toString();
187 }
188
189 }
190
191 private static final Integer ZERO = Integer.valueOf(0);
192
193
194 private static final String ONAME_BASE =
195 "org.apache.commons.pool2:type=GenericKeyedObjectPool,name=";
196
197 private volatile int maxIdlePerKey =
198 GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
199
200 private volatile int minIdlePerKey =
201 GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
202
203
204 private volatile int maxTotalPerKey =
205 GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
206
207 private final KeyedPooledObjectFactory<K, T> factory;
208
209 private final boolean fairness;
210
211
212
213
214
215
216 private final Map<K, ObjectDeque<T>> poolMap =
217 new ConcurrentHashMap<>();
218
219
220
221
222
223
224
225 private final ArrayList<K> poolKeyList = new ArrayList<>();
226
227 private final ReadWriteLock keyLock = new ReentrantReadWriteLock(true);
228
229
230
231
232
233
234
235 private final AtomicInteger numTotal = new AtomicInteger(0);
236
237 private Iterator<K> evictionKeyIterator;
238
239
240 private K evictionKey;
241
242
243
244
245
246
247 public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory) {
248 this(factory, new GenericKeyedObjectPoolConfig<>());
249 }
250
251
252
253
254
255
256
257
258
259
260
261 public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory,
262 final GenericKeyedObjectPoolConfig<T> config) {
263
264 super(config, ONAME_BASE, config.getJmxNamePrefix());
265
266 if (factory == null) {
267 jmxUnregister();
268 throw new IllegalArgumentException("Factory may not be null");
269 }
270 this.factory = factory;
271 this.fairness = config.getFairness();
272
273 setConfig(config);
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory,
291 final GenericKeyedObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
292 this(factory, config);
293 setAbandonedConfig(abandonedConfig);
294 }
295
296
297
298
299
300
301
302
303
304
305 private void addIdleObject(final K key, final PooledObject<T> p) throws Exception {
306 if (!PooledObject.isNull(p)) {
307 factory.passivateObject(key, p);
308 final LinkedBlockingDeque<PooledObject<T>> idleObjects = poolMap.get(key).getIdleObjects();
309 if (getLifo()) {
310 idleObjects.addFirst(p);
311 } else {
312 idleObjects.addLast(p);
313 }
314 }
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 @Override
337 public void addObject(final K key) throws Exception {
338 assertOpen();
339 register(key);
340 try {
341 addIdleObject(key, create(key));
342 } finally {
343 deregister(key);
344 }
345 }
346
347
348
349
350
351
352
353 @Override
354 public T borrowObject(final K key) throws Exception {
355 return borrowObject(key, getMaxWaitDuration().toMillis());
356 }
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception {
421 assertOpen();
422
423 final AbandonedConfig ac = this.abandonedConfig;
424 if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 &&
425 getNumActive() > getMaxTotal() - 3) {
426 removeAbandoned(ac);
427 }
428
429 PooledObject<T> p = null;
430
431
432
433 final boolean blockWhenExhausted = getBlockWhenExhausted();
434
435 boolean create;
436 final Instant waitTime = Instant.now();
437 final ObjectDeque<T> objectDeque = register(key);
438
439 try {
440 while (p == null) {
441 create = false;
442 p = objectDeque.getIdleObjects().pollFirst();
443 if (p == null) {
444 p = create(key);
445 if (!PooledObject.isNull(p)) {
446 create = true;
447 }
448 }
449 if (blockWhenExhausted) {
450 if (PooledObject.isNull(p)) {
451 p = borrowMaxWaitMillis < 0 ? objectDeque.getIdleObjects().takeFirst():
452 objectDeque.getIdleObjects().pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
453 }
454 if (PooledObject.isNull(p)) {
455 throw new NoSuchElementException(appendStats(
456 "Timeout waiting for idle object, borrowMaxWaitMillis=" + borrowMaxWaitMillis));
457 }
458 } else if (PooledObject.isNull(p)) {
459 throw new NoSuchElementException(appendStats("Pool exhausted"));
460 }
461 if (!p.allocate()) {
462 p = null;
463 }
464
465 if (!PooledObject.isNull(p)) {
466 try {
467 factory.activateObject(key, p);
468 } catch (final Exception e) {
469 try {
470 destroy(key, p, true, DestroyMode.NORMAL);
471 } catch (final Exception ignored) {
472
473 }
474 p = null;
475 if (create) {
476 final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to activate object"));
477 nsee.initCause(e);
478 throw nsee;
479 }
480 }
481 if (!PooledObject.isNull(p) && getTestOnBorrow()) {
482 boolean validate = false;
483 Throwable validationThrowable = null;
484 try {
485 validate = factory.validateObject(key, p);
486 } catch (final Throwable t) {
487 PoolUtils.checkRethrow(t);
488 validationThrowable = t;
489 }
490 if (!validate) {
491 try {
492 destroy(key, p, true, DestroyMode.NORMAL);
493 destroyedByBorrowValidationCount.incrementAndGet();
494 } catch (final Exception ignored) {
495
496 }
497 p = null;
498 if (create) {
499 final NoSuchElementException nsee = new NoSuchElementException(
500 appendStats("Unable to validate object"));
501 nsee.initCause(validationThrowable);
502 throw nsee;
503 }
504 }
505 }
506 }
507 }
508 } finally {
509 deregister(key);
510 }
511
512 updateStatsBorrow(p, Duration.between(waitTime, Instant.now()));
513
514 return p.getObject();
515 }
516
517
518
519
520
521
522
523
524
525
526 private int calculateDeficit(final ObjectDeque<T> objectDeque) {
527
528 if (objectDeque == null) {
529 return getMinIdlePerKey();
530 }
531
532
533 final int maxTotal = getMaxTotal();
534 final int maxTotalPerKeySave = getMaxTotalPerKey();
535
536
537
538 int objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size();
539 if (maxTotalPerKeySave > 0) {
540 final int growLimit = Math.max(0,
541 maxTotalPerKeySave - objectDeque.getIdleObjects().size());
542 objectDefecit = Math.min(objectDefecit, growLimit);
543 }
544
545
546 if (maxTotal > 0) {
547 final int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle());
548 objectDefecit = Math.min(objectDefecit, growLimit);
549 }
550
551 return objectDefecit;
552 }
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572 @Override
573 public void clear() {
574 poolMap.keySet().forEach(key -> clear(key, false));
575 }
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590 @Override
591 public void clear(final K key) {
592 clear(key, true);
593 }
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610 public void clear(final K key, final boolean reuseCapacity) {
611
612 if (!poolMap.containsKey(key)) {
613 return;
614 }
615 final ObjectDeque<T> objectDeque = register(key);
616 int freedCapacity = 0;
617 try {
618 final LinkedBlockingDeque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
619 PooledObject<T> p = idleObjects.poll();
620 while (p != null) {
621 try {
622 if (destroy(key, p, true, DestroyMode.NORMAL)) {
623 freedCapacity++;
624 }
625 } catch (final Exception e) {
626 swallowException(e);
627 }
628 p = idleObjects.poll();
629 }
630 } finally {
631 deregister(key);
632 }
633 if (reuseCapacity) {
634 reuseCapacity(freedCapacity);
635 }
636 }
637
638
639
640
641
642 public void clearOldest() {
643
644
645 final TreeMap<PooledObject<T>, K> map = new TreeMap<>();
646
647 poolMap.forEach((key, value) -> {
648
649
650 value.getIdleObjects().forEach(p -> map.put(p, key));
651 });
652
653
654
655 int itemsToRemove = (int) (map.size() * 0.15) + 1;
656 final Iterator<Entry<PooledObject<T>, K>> iter = map.entrySet().iterator();
657
658 while (iter.hasNext() && itemsToRemove > 0) {
659 final Entry<PooledObject<T>, K> entry = iter.next();
660
661
662
663
664 final K key = entry.getValue();
665 final PooledObject<T> p = entry.getKey();
666
667 boolean destroyed = true;
668 try {
669 destroyed = destroy(key, p, false, DestroyMode.NORMAL);
670 } catch (final Exception e) {
671 swallowException(e);
672 }
673 if (destroyed) {
674 itemsToRemove--;
675 }
676 }
677 }
678
679
680
681
682
683
684
685
686
687
688
689
690 @Override
691 public void close() {
692 if (isClosed()) {
693 return;
694 }
695
696 synchronized (closeLock) {
697 if (isClosed()) {
698 return;
699 }
700
701
702
703 stopEvictor();
704
705 closed = true;
706
707 clear();
708
709 jmxUnregister();
710
711
712 poolMap.values().forEach(e -> e.getIdleObjects().interuptTakeWaiters());
713
714
715 clear();
716 }
717 }
718
719
720
721
722
723
724
725
726
727
728
729 private PooledObject<T> create(final K key) throws Exception {
730 int maxTotalPerKeySave = getMaxTotalPerKey();
731 if (maxTotalPerKeySave < 0) {
732 maxTotalPerKeySave = Integer.MAX_VALUE;
733 }
734 final int maxTotal = getMaxTotal();
735
736 final ObjectDeque<T> objectDeque = poolMap.get(key);
737
738
739 boolean loop = true;
740
741 while (loop) {
742 final int newNumTotal = numTotal.incrementAndGet();
743 if (maxTotal > -1 && newNumTotal > maxTotal) {
744 numTotal.decrementAndGet();
745 if (getNumIdle() == 0) {
746 return null;
747 }
748 clearOldest();
749 } else {
750 loop = false;
751 }
752 }
753
754
755
756
757
758
759 Boolean create = null;
760 while (create == null) {
761 synchronized (objectDeque.makeObjectCountLock) {
762 final long newCreateCount = objectDeque.getCreateCount().incrementAndGet();
763
764 if (newCreateCount > maxTotalPerKeySave) {
765
766
767 objectDeque.getCreateCount().decrementAndGet();
768 if (objectDeque.makeObjectCount == 0) {
769
770
771
772
773 create = Boolean.FALSE;
774 } else {
775
776
777
778
779 objectDeque.makeObjectCountLock.wait();
780 }
781 } else {
782
783 objectDeque.makeObjectCount++;
784 create = Boolean.TRUE;
785 }
786 }
787 }
788
789 if (!create.booleanValue()) {
790 numTotal.decrementAndGet();
791 return null;
792 }
793
794 PooledObject<T> p = null;
795 try {
796 p = factory.makeObject(key);
797 if (PooledObject.isNull(p)) {
798 numTotal.decrementAndGet();
799 objectDeque.getCreateCount().decrementAndGet();
800 throw new NullPointerException(String.format("%s.makeObject() = null", factory.getClass().getSimpleName()));
801 }
802 if (getTestOnCreate() && !factory.validateObject(key, p)) {
803 numTotal.decrementAndGet();
804 objectDeque.getCreateCount().decrementAndGet();
805 return null;
806 }
807 } catch (final Exception e) {
808 numTotal.decrementAndGet();
809 objectDeque.getCreateCount().decrementAndGet();
810 throw e;
811 } finally {
812 synchronized (objectDeque.makeObjectCountLock) {
813 objectDeque.makeObjectCount--;
814 objectDeque.makeObjectCountLock.notifyAll();
815 }
816 }
817
818 final AbandonedConfig ac = this.abandonedConfig;
819 if (ac != null && ac.getLogAbandoned()) {
820 p.setLogAbandoned(true);
821 p.setRequireFullStackTrace(ac.getRequireFullStackTrace());
822 }
823
824 createdCount.incrementAndGet();
825 objectDeque.getAllObjects().put(new IdentityWrapper<>(p.getObject()), p);
826 return p;
827 }
828
829
830
831
832
833
834
835
836
837 private void deregister(final K k) {
838 Lock lock = keyLock.readLock();
839 try {
840 lock.lock();
841 ObjectDeque<T> objectDeque = poolMap.get(k);
842 if (objectDeque == null) {
843 throw new IllegalStateException("Attempt to de-register a key for a non-existent pool");
844 }
845 final long numInterested = objectDeque.getNumInterested().decrementAndGet();
846 if (numInterested < 0) {
847 throw new IllegalStateException("numInterested count for key " + k + " is less than zero");
848 }
849 if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) {
850
851
852 lock.unlock();
853 lock = keyLock.writeLock();
854 lock.lock();
855
856
857 objectDeque = poolMap.get(k);
858 if (null != objectDeque && objectDeque.getNumInterested().get() == 0 && objectDeque.getCreateCount().get() == 0) {
859
860
861
862 poolMap.remove(k);
863 poolKeyList.remove(k);
864 }
865 }
866 } finally {
867 lock.unlock();
868 }
869 }
870
871
872
873
874
875
876
877
878
879
880
881
882
883 private boolean destroy(final K key, final PooledObject<T> toDestroy, final boolean always, final DestroyMode destroyMode) throws Exception {
884
885 final ObjectDeque<T> objectDeque = register(key);
886
887 try {
888 boolean isIdle;
889 synchronized (toDestroy) {
890
891 isIdle = toDestroy.getState().equals(PooledObjectState.IDLE);
892
893
894 if (isIdle || always) {
895 isIdle = objectDeque.getIdleObjects().remove(toDestroy);
896 }
897 }
898 if (isIdle || always) {
899 objectDeque.getAllObjects().remove(new IdentityWrapper<>(toDestroy.getObject()));
900 toDestroy.invalidate();
901
902 try {
903 factory.destroyObject(key, toDestroy, destroyMode);
904 } finally {
905 objectDeque.getCreateCount().decrementAndGet();
906 destroyedCount.incrementAndGet();
907 numTotal.decrementAndGet();
908 }
909 return true;
910 }
911 return false;
912 } finally {
913 deregister(key);
914 }
915 }
916
917
918 @Override
919 void ensureMinIdle() throws Exception {
920 final int minIdlePerKeySave = getMinIdlePerKey();
921 if (minIdlePerKeySave < 1) {
922 return;
923 }
924
925 for (final K k : poolMap.keySet()) {
926 ensureMinIdle(k);
927 }
928 }
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946 private void ensureMinIdle(final K key) throws Exception {
947
948 ObjectDeque<T> objectDeque = poolMap.get(key);
949
950
951
952
953
954
955
956
957
958 final int deficit = calculateDeficit(objectDeque);
959
960 for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) {
961 addObject(key);
962
963
964
965 if (objectDeque == null) {
966 objectDeque = poolMap.get(key);
967 }
968 }
969 }
970
971
972
973
974
975
976
977
978
979 @Override
980 public void evict() throws Exception {
981 assertOpen();
982
983 if (getNumIdle() > 0) {
984
985 PooledObject<T> underTest = null;
986 final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
987
988 synchronized (evictionLock) {
989 final EvictionConfig evictionConfig = new EvictionConfig(
990 getMinEvictableIdleDuration(),
991 getSoftMinEvictableIdleDuration(),
992 getMinIdlePerKey());
993
994 final boolean testWhileIdle = getTestWhileIdle();
995
996 for (int i = 0, m = getNumTests(); i < m; i++) {
997 if (evictionIterator == null || !evictionIterator.hasNext()) {
998 if (evictionKeyIterator == null ||
999 !evictionKeyIterator.hasNext()) {
1000 final List<K> keyCopy = new ArrayList<>();
1001 final Lock readLock = keyLock.readLock();
1002 readLock.lock();
1003 try {
1004 keyCopy.addAll(poolKeyList);
1005 } finally {
1006 readLock.unlock();
1007 }
1008 evictionKeyIterator = keyCopy.iterator();
1009 }
1010 while (evictionKeyIterator.hasNext()) {
1011 evictionKey = evictionKeyIterator.next();
1012 final ObjectDeque<T> objectDeque = poolMap.get(evictionKey);
1013 if (objectDeque == null) {
1014 continue;
1015 }
1016
1017 final Deque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
1018 evictionIterator = new EvictionIterator(idleObjects);
1019 if (evictionIterator.hasNext()) {
1020 break;
1021 }
1022 evictionIterator = null;
1023 }
1024 }
1025 if (evictionIterator == null) {
1026
1027 return;
1028 }
1029 final Deque<PooledObject<T>> idleObjects;
1030 try {
1031 underTest = evictionIterator.next();
1032 idleObjects = evictionIterator.getIdleObjects();
1033 } catch (final NoSuchElementException nsee) {
1034
1035
1036 i--;
1037 evictionIterator = null;
1038 continue;
1039 }
1040
1041 if (!underTest.startEvictionTest()) {
1042
1043
1044 i--;
1045 continue;
1046 }
1047
1048
1049
1050
1051 boolean evict;
1052 try {
1053 evict = evictionPolicy.evict(evictionConfig, underTest,
1054 poolMap.get(evictionKey).getIdleObjects().size());
1055 } catch (final Throwable t) {
1056
1057
1058 PoolUtils.checkRethrow(t);
1059 swallowException(new Exception(t));
1060
1061 evict = false;
1062 }
1063
1064 if (evict) {
1065 destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
1066 destroyedByEvictorCount.incrementAndGet();
1067 } else {
1068 if (testWhileIdle) {
1069 boolean active = false;
1070 try {
1071 factory.activateObject(evictionKey, underTest);
1072 active = true;
1073 } catch (final Exception e) {
1074 destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
1075 destroyedByEvictorCount.incrementAndGet();
1076 }
1077 if (active) {
1078 boolean validate = false;
1079 Throwable validationThrowable = null;
1080 try {
1081 validate = factory.validateObject(evictionKey, underTest);
1082 } catch (final Throwable t) {
1083 PoolUtils.checkRethrow(t);
1084 validationThrowable = t;
1085 }
1086 if (!validate) {
1087 destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
1088 destroyedByEvictorCount.incrementAndGet();
1089 if (validationThrowable != null) {
1090 if (validationThrowable instanceof RuntimeException) {
1091 throw (RuntimeException) validationThrowable;
1092 }
1093 throw (Error) validationThrowable;
1094 }
1095 } else {
1096 try {
1097 factory.passivateObject(evictionKey, underTest);
1098 } catch (final Exception e) {
1099 destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
1100 destroyedByEvictorCount.incrementAndGet();
1101 }
1102 }
1103 }
1104 }
1105 underTest.endEvictionTest(idleObjects);
1106
1107
1108 }
1109 }
1110 }
1111 }
1112 final AbandonedConfig ac = this.abandonedConfig;
1113 if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
1114 removeAbandoned(ac);
1115 }
1116 }
1117
1118
1119
1120
1121
1122
1123
1124
1125 public KeyedPooledObjectFactory<K, T> getFactory() {
1126 return factory;
1127 }
1128
1129
1130
1131
1132
1133
1134
1135 @Override
1136 @SuppressWarnings("unchecked")
1137 public List<K> getKeys() {
1138 return (List<K>) poolKeyList.clone();
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156 @Override
1157 public int getMaxIdlePerKey() {
1158 return maxIdlePerKey;
1159 }
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170 @Override
1171 public int getMaxTotalPerKey() {
1172 return maxTotalPerKey;
1173 }
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192 @Override
1193 public int getMinIdlePerKey() {
1194 final int maxIdlePerKeySave = getMaxIdlePerKey();
1195 return Math.min(this.minIdlePerKey, maxIdlePerKeySave);
1196 }
1197
1198 @Override
1199 public int getNumActive() {
1200 return numTotal.get() - getNumIdle();
1201 }
1202
1203 @Override
1204 public int getNumActive(final K key) {
1205 final ObjectDeque<T> objectDeque = poolMap.get(key);
1206 if (objectDeque != null) {
1207 return objectDeque.getAllObjects().size() -
1208 objectDeque.getIdleObjects().size();
1209 }
1210 return 0;
1211 }
1212
1213 @Override
1214 public Map<String, Integer> getNumActivePerKey() {
1215 return poolMap.entrySet().stream().collect(Collectors.toMap(
1216 e -> e.getKey().toString(),
1217 e -> Integer.valueOf(e.getValue().getAllObjects().size() - e.getValue().getIdleObjects().size()),
1218 (t, u) -> u));
1219 }
1220
1221 @Override
1222 public int getNumIdle() {
1223 return poolMap.values().stream().mapToInt(e -> e.getIdleObjects().size()).sum();
1224 }
1225
1226 @Override
1227 public int getNumIdle(final K key) {
1228 final ObjectDeque<T> objectDeque = poolMap.get(key);
1229 return objectDeque != null ? objectDeque.getIdleObjects().size() : 0;
1230 }
1231
1232
1233
1234
1235
1236
1237
1238 private int getNumTests() {
1239 final int totalIdle = getNumIdle();
1240 final int numTests = getNumTestsPerEvictionRun();
1241 if (numTests >= 0) {
1242 return Math.min(numTests, totalIdle);
1243 }
1244 return (int) Math.ceil(totalIdle / Math.abs((double) numTests));
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255 @Override
1256 public int getNumWaiters() {
1257 if (getBlockWhenExhausted()) {
1258
1259 return poolMap.values().stream().mapToInt(e -> e.getIdleObjects().getTakeQueueLength()).sum();
1260 }
1261 return 0;
1262 }
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272 @Override
1273 public Map<String, Integer> getNumWaitersByKey() {
1274 final Map<String, Integer> result = new HashMap<>();
1275 poolMap.forEach((k, deque) -> result.put(k.toString(), getBlockWhenExhausted() ?
1276 Integer.valueOf(deque.getIdleObjects().getTakeQueueLength()) :
1277 ZERO));
1278 return result;
1279 }
1280
1281 @Override
1282 String getStatsString() {
1283
1284 return super.getStatsString() +
1285 String.format(", fairness=%s, maxIdlePerKey%,d, maxTotalPerKey=%,d, minIdlePerKey=%,d, numTotal=%,d",
1286 fairness, maxIdlePerKey, maxTotalPerKey, minIdlePerKey, numTotal.get());
1287 }
1288
1289
1290
1291
1292
1293
1294
1295
1296 private boolean hasBorrowWaiters() {
1297 return getBlockWhenExhausted() && poolMap.values().stream().anyMatch(deque -> deque.getIdleObjects().hasTakeWaiters());
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315 @Override
1316 public void invalidateObject(final K key, final T obj) throws Exception {
1317 invalidateObject(key, obj, DestroyMode.NORMAL);
1318 }
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337 @Override
1338 public void invalidateObject(final K key, final T obj, final DestroyMode destroyMode) throws Exception {
1339 final ObjectDeque<T> objectDeque = poolMap.get(key);
1340 final PooledObject<T> p = objectDeque != null ? objectDeque.getAllObjects().get(new IdentityWrapper<>(obj)) : null;
1341 if (p == null) {
1342 throw new IllegalStateException(appendStats("Object not currently part of this pool"));
1343 }
1344 synchronized (p) {
1345 if (p.getState() != PooledObjectState.INVALID) {
1346 destroy(key, p, true, destroyMode);
1347 reuseCapacity();
1348 }
1349 }
1350 }
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364 @Override
1365 public Map<String, List<DefaultPooledObjectInfo>> listAllObjects() {
1366 return poolMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toString(),
1367 e -> e.getValue().getAllObjects().values().stream().map(DefaultPooledObjectInfo::new).collect(Collectors.toList())));
1368 }
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378 public void preparePool(final K key) throws Exception {
1379 final int minIdlePerKeySave = getMinIdlePerKey();
1380 if (minIdlePerKeySave < 1) {
1381 return;
1382 }
1383 ensureMinIdle(key);
1384 }
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398 private ObjectDeque<T> register(final K k) {
1399 Lock lock = keyLock.readLock();
1400 ObjectDeque<T> objectDeque = null;
1401 try {
1402 lock.lock();
1403 objectDeque = poolMap.get(k);
1404 if (objectDeque == null) {
1405
1406 lock.unlock();
1407 lock = keyLock.writeLock();
1408 lock.lock();
1409 final AtomicBoolean allocated = new AtomicBoolean();
1410 objectDeque = poolMap.computeIfAbsent(k, key -> {
1411 allocated.set(true);
1412 final ObjectDeque<T> deque = new ObjectDeque<>(fairness);
1413 deque.getNumInterested().incrementAndGet();
1414
1415
1416
1417 poolKeyList.add(k);
1418 return deque;
1419 });
1420 if (!allocated.get()) {
1421
1422
1423 objectDeque = poolMap.get(k);
1424 objectDeque.getNumInterested().incrementAndGet();
1425 }
1426 } else {
1427 objectDeque.getNumInterested().incrementAndGet();
1428 }
1429 } finally {
1430 lock.unlock();
1431 }
1432 return objectDeque;
1433 }
1434
1435
1436
1437
1438
1439
1440
1441 @SuppressWarnings("resource")
1442 private void removeAbandoned(final AbandonedConfig abandonedConfig) {
1443 poolMap.forEach((key, value) -> {
1444
1445 final ArrayList<PooledObject<T>> remove = createRemoveList(abandonedConfig, value.getAllObjects());
1446
1447 remove.forEach(pooledObject -> {
1448 if (abandonedConfig.getLogAbandoned()) {
1449 pooledObject.printStackTrace(abandonedConfig.getLogWriter());
1450 }
1451 try {
1452 invalidateObject(key, pooledObject.getObject(), DestroyMode.ABANDONED);
1453 } catch (final Exception e) {
1454 swallowException(e);
1455 }
1456 });
1457 });
1458 }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485 @Override
1486 public void returnObject(final K key, final T obj) {
1487
1488 final ObjectDeque<T> objectDeque = poolMap.get(key);
1489
1490 if (objectDeque == null) {
1491 throw new IllegalStateException("No keyed pool found under the given key.");
1492 }
1493
1494 final PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj));
1495
1496 if (PooledObject.isNull(p)) {
1497 throw new IllegalStateException("Returned object not currently part of this pool");
1498 }
1499
1500 markReturningState(p);
1501
1502 final Duration activeTime = p.getActiveDuration();
1503
1504 try {
1505 if (getTestOnReturn() && !factory.validateObject(key, p)) {
1506 try {
1507 destroy(key, p, true, DestroyMode.NORMAL);
1508 } catch (final Exception e) {
1509 swallowException(e);
1510 }
1511 whenWaitersAddObject(key, objectDeque.idleObjects);
1512 return;
1513 }
1514
1515 try {
1516 factory.passivateObject(key, p);
1517 } catch (final Exception e1) {
1518 swallowException(e1);
1519 try {
1520 destroy(key, p, true, DestroyMode.NORMAL);
1521 } catch (final Exception e) {
1522 swallowException(e);
1523 }
1524 whenWaitersAddObject(key, objectDeque.idleObjects);
1525 return;
1526 }
1527
1528 if (!p.deallocate()) {
1529 throw new IllegalStateException("Object has already been returned to this pool");
1530 }
1531
1532 final int maxIdle = getMaxIdlePerKey();
1533 final LinkedBlockingDeque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
1534
1535 if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
1536 try {
1537 destroy(key, p, true, DestroyMode.NORMAL);
1538 } catch (final Exception e) {
1539 swallowException(e);
1540 }
1541 } else {
1542 if (getLifo()) {
1543 idleObjects.addFirst(p);
1544 } else {
1545 idleObjects.addLast(p);
1546 }
1547 if (isClosed()) {
1548
1549
1550
1551 clear(key);
1552 }
1553 }
1554 } finally {
1555 if (hasBorrowWaiters()) {
1556 reuseCapacity();
1557 }
1558 updateStatsReturn(activeTime);
1559 }
1560 }
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575 private void reuseCapacity() {
1576 final int maxTotalPerKeySave = getMaxTotalPerKey();
1577 int maxQueueLength = 0;
1578 LinkedBlockingDeque<PooledObject<T>> mostLoadedPool = null;
1579 K mostLoadedKey = null;
1580
1581
1582 for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) {
1583 final K k = entry.getKey();
1584 final LinkedBlockingDeque<PooledObject<T>> pool = entry.getValue().getIdleObjects();
1585 final int queueLength = pool.getTakeQueueLength();
1586 if (getNumActive(k) < maxTotalPerKeySave && queueLength > maxQueueLength) {
1587 maxQueueLength = queueLength;
1588 mostLoadedPool = pool;
1589 mostLoadedKey = k;
1590 }
1591 }
1592
1593
1594 if (mostLoadedPool != null) {
1595 register(mostLoadedKey);
1596 try {
1597
1598
1599 addIdleObject(mostLoadedKey, create(mostLoadedKey));
1600 } catch (final Exception e) {
1601 swallowException(e);
1602 } finally {
1603 deregister(mostLoadedKey);
1604 }
1605 }
1606 }
1607
1608
1609
1610
1611
1612
1613
1614
1615 private void reuseCapacity(final int newCapacity) {
1616 final int bound = newCapacity < 1 ? 1 : newCapacity;
1617 for (int i = 0; i < bound; i++) {
1618 reuseCapacity();
1619 }
1620 }
1621
1622
1623
1624
1625
1626
1627
1628
1629 public void setConfig(final GenericKeyedObjectPoolConfig<T> conf) {
1630 super.setConfig(conf);
1631 setMaxIdlePerKey(conf.getMaxIdlePerKey());
1632 setMaxTotalPerKey(conf.getMaxTotalPerKey());
1633 setMaxTotal(conf.getMaxTotal());
1634 setMinIdlePerKey(conf.getMinIdlePerKey());
1635 }
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653 public void setMaxIdlePerKey(final int maxIdlePerKey) {
1654 this.maxIdlePerKey = maxIdlePerKey;
1655 }
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666 public void setMaxTotalPerKey(final int maxTotalPerKey) {
1667 this.maxTotalPerKey = maxTotalPerKey;
1668 }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 public void setMinIdlePerKey(final int minIdlePerKey) {
1690 this.minIdlePerKey = minIdlePerKey;
1691 }
1692
1693 @Override
1694 protected void toStringAppendFields(final StringBuilder builder) {
1695 super.toStringAppendFields(builder);
1696 builder.append(", maxIdlePerKey=");
1697 builder.append(maxIdlePerKey);
1698 builder.append(", minIdlePerKey=");
1699 builder.append(minIdlePerKey);
1700 builder.append(", maxTotalPerKey=");
1701 builder.append(maxTotalPerKey);
1702 builder.append(", factory=");
1703 builder.append(factory);
1704 builder.append(", fairness=");
1705 builder.append(fairness);
1706 builder.append(", poolMap=");
1707 builder.append(poolMap);
1708 builder.append(", poolKeyList=");
1709 builder.append(poolKeyList);
1710 builder.append(", keyLock=");
1711 builder.append(keyLock);
1712 builder.append(", numTotal=");
1713 builder.append(numTotal);
1714 builder.append(", evictionKeyIterator=");
1715 builder.append(evictionKeyIterator);
1716 builder.append(", evictionKey=");
1717 builder.append(evictionKey);
1718 builder.append(", abandonedConfig=");
1719 builder.append(abandonedConfig);
1720 }
1721
1722
1723
1724
1725 @Override
1726 public void use(final T pooledObject) {
1727 final AbandonedConfig abandonedCfg = this.abandonedConfig;
1728 if (abandonedCfg != null && abandonedCfg.getUseUsageTracking()) {
1729 poolMap.values().stream()
1730 .map(pool -> pool.getAllObjects().get(new IdentityWrapper<>(pooledObject)))
1731 .filter(Objects::nonNull)
1732 .findFirst()
1733 .ifPresent(PooledObject::use);
1734 }
1735 }
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745 private void whenWaitersAddObject(final K key, final LinkedBlockingDeque<PooledObject<T>> idleObjects) {
1746 if (idleObjects.hasTakeWaiters()) {
1747 try {
1748 addObject(key);
1749 } catch (final Exception e) {
1750 swallowException(e);
1751 }
1752 }
1753 }
1754
1755 }