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.lang.ref.Reference;
20 import java.lang.ref.ReferenceQueue;
21 import java.lang.ref.SoftReference;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.NoSuchElementException;
25 import java.util.Optional;
26
27 import org.apache.commons.pool2.BaseObjectPool;
28 import org.apache.commons.pool2.ObjectPool;
29 import org.apache.commons.pool2.PoolUtils;
30 import org.apache.commons.pool2.PooledObjectFactory;
31
32
33
34
35
36
37
38
39
40
41
42
43 public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> {
44
45
46 private final PooledObjectFactory<T> factory;
47
48
49
50
51
52
53 private final ReferenceQueue<T> refQueue = new ReferenceQueue<>();
54
55
56 private int numActive;
57
58
59 private long destroyCount;
60
61
62
63 private long createCount;
64
65
66 private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences =
67 new LinkedBlockingDeque<>();
68
69
70 private final ArrayList<PooledSoftReference<T>> allReferences =
71 new ArrayList<>();
72
73
74
75
76
77
78 public SoftReferenceObjectPool(final PooledObjectFactory<T> factory) {
79 this.factory = factory;
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 @Override
106 public synchronized void addObject() throws Exception {
107 assertOpen();
108 if (factory == null) {
109 throw new IllegalStateException(
110 "Cannot add objects without a factory.");
111 }
112 final T obj = factory.makeObject().getObject();
113 createCount++;
114
115 final PooledSoftReference<T> ref = new PooledSoftReference<>(
116 new SoftReference<>(obj, refQueue));
117 allReferences.add(ref);
118
119 boolean success = true;
120 if (!factory.validateObject(ref)) {
121 success = false;
122 } else {
123 factory.passivateObject(ref);
124 }
125
126 final boolean shouldDestroy = !success;
127 if (success) {
128 idleReferences.add(ref);
129 notifyAll();
130 }
131
132 if (shouldDestroy) {
133 try {
134 destroy(ref);
135 } catch (final Exception ignored) {
136
137 }
138 }
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 @SuppressWarnings("null")
177 @Override
178 public synchronized T borrowObject() throws Exception {
179 assertOpen();
180 T obj = null;
181 boolean newlyCreated = false;
182 PooledSoftReference<T> ref = null;
183 while (null == obj) {
184 if (idleReferences.isEmpty()) {
185 if (null == factory) {
186 throw new NoSuchElementException();
187 }
188 newlyCreated = true;
189 obj = factory.makeObject().getObject();
190 createCount++;
191
192 ref = new PooledSoftReference<>(new SoftReference<>(obj));
193 allReferences.add(ref);
194 } else {
195 ref = idleReferences.pollFirst();
196 obj = ref.getObject();
197
198
199
200 ref.getReference().clear();
201 ref.setReference(new SoftReference<>(obj));
202 }
203 if (null != factory && null != obj) {
204 try {
205 factory.activateObject(ref);
206 if (!factory.validateObject(ref)) {
207 throw new Exception("ValidateObject failed");
208 }
209 } catch (final Throwable t) {
210 PoolUtils.checkRethrow(t);
211 try {
212 destroy(ref);
213 } catch (final Throwable t2) {
214 PoolUtils.checkRethrow(t2);
215
216 } finally {
217 obj = null;
218 }
219 if (newlyCreated) {
220 throw new NoSuchElementException("Could not create a validated object, cause: " + t);
221 }
222 }
223 }
224 }
225 numActive++;
226 ref.allocate();
227 return obj;
228 }
229
230
231
232
233 @Override
234 public synchronized void clear() {
235 if (null != factory) {
236 idleReferences.forEach(ref -> {
237 try {
238 if (null != ref.getObject()) {
239 factory.destroyObject(ref);
240 }
241 } catch (final Exception ignored) {
242
243 }
244 });
245 }
246 idleReferences.clear();
247 pruneClearedReferences();
248 }
249
250
251
252
253
254
255
256
257
258
259 @Override
260 public void close() {
261 super.close();
262 clear();
263 }
264
265
266
267
268
269
270
271
272
273 private void destroy(final PooledSoftReference<T> toDestroy) throws Exception {
274 toDestroy.invalidate();
275 idleReferences.remove(toDestroy);
276 allReferences.remove(toDestroy);
277 try {
278 factory.destroyObject(toDestroy);
279 } finally {
280 destroyCount++;
281 toDestroy.getReference().clear();
282 }
283 }
284
285
286
287
288
289
290
291 private PooledSoftReference<T> findReference(final T obj) {
292 final Optional<PooledSoftReference<T>> first = allReferences.stream()
293 .filter(reference -> reference.getObject() != null && reference.getObject().equals(obj)).findFirst();
294 return first.orElse(null);
295 }
296
297
298
299
300
301
302
303 public synchronized PooledObjectFactory<T> getFactory() {
304 return factory;
305 }
306
307
308
309
310
311
312 @Override
313 public synchronized int getNumActive() {
314 return numActive;
315 }
316
317
318
319
320
321
322
323 @Override
324 public synchronized int getNumIdle() {
325 pruneClearedReferences();
326 return idleReferences.size();
327 }
328
329
330
331
332 @Override
333 public synchronized void invalidateObject(final T obj) throws Exception {
334 final PooledSoftReference<T> ref = findReference(obj);
335 if (ref == null) {
336 throw new IllegalStateException(
337 "Object to invalidate is not currently part of this pool");
338 }
339 if (factory != null) {
340 destroy(ref);
341 }
342 numActive--;
343 notifyAll();
344 }
345
346
347
348
349
350 private void pruneClearedReferences() {
351
352 removeClearedReferences(idleReferences.iterator());
353 removeClearedReferences(allReferences.iterator());
354 while (refQueue.poll() != null) {
355 }
356 }
357
358
359
360
361
362 private void removeClearedReferences(final Iterator<PooledSoftReference<T>> iterator) {
363 PooledSoftReference<T> ref;
364 while (iterator.hasNext()) {
365 ref = iterator.next();
366 if (ref.getReference() == null || ref.getReference().isEnqueued()) {
367 iterator.remove();
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 @Override
394 public synchronized void returnObject(final T obj) throws Exception {
395 boolean success = !isClosed();
396 final PooledSoftReference<T> ref = findReference(obj);
397 if (ref == null) {
398 throw new IllegalStateException(
399 "Returned object not currently part of this pool");
400 }
401 if (factory != null) {
402 if (!factory.validateObject(ref)) {
403 success = false;
404 } else {
405 try {
406 factory.passivateObject(ref);
407 } catch (final Exception e) {
408 success = false;
409 }
410 }
411 }
412
413 final boolean shouldDestroy = !success;
414 numActive--;
415 if (success) {
416
417
418 ref.deallocate();
419 idleReferences.add(ref);
420 }
421 notifyAll();
422
423 if (shouldDestroy && factory != null) {
424 try {
425 destroy(ref);
426 } catch (final Exception ignored) {
427
428 }
429 }
430 }
431
432 @Override
433 protected void toStringAppendFields(final StringBuilder builder) {
434 super.toStringAppendFields(builder);
435 builder.append(", factory=");
436 builder.append(factory);
437 builder.append(", refQueue=");
438 builder.append(refQueue);
439 builder.append(", numActive=");
440 builder.append(numActive);
441 builder.append(", destroyCount=");
442 builder.append(destroyCount);
443 builder.append(", createCount=");
444 builder.append(createCount);
445 builder.append(", idleReferences=");
446 builder.append(idleReferences);
447 builder.append(", allReferences=");
448 builder.append(allReferences);
449 }
450 }