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.io.PrintWriter;
20 import java.time.Clock;
21 import java.time.Duration;
22 import java.time.Instant;
23 import java.util.Deque;
24
25 import org.apache.commons.pool2.PooledObject;
26 import org.apache.commons.pool2.PooledObjectState;
27 import org.apache.commons.pool2.TrackedUse;
28
29
30
31
32
33
34
35
36
37
38
39
40 public class DefaultPooledObject<T> implements PooledObject<T> {
41
42 private final T object;
43 private PooledObjectState state = PooledObjectState.IDLE;
44 private final Clock systemClock = Clock.systemUTC();
45 private final Instant createInstant = now();
46
47 private volatile Instant lastBorrowInstant = createInstant;
48 private volatile Instant lastUseInstant = createInstant;
49 private volatile Instant lastReturnInstant = createInstant;
50 private volatile boolean logAbandoned;
51 private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE;
52 private volatile CallStack usedBy = NoOpCallStack.INSTANCE;
53 private volatile long borrowedCount;
54
55
56
57
58
59
60
61 public DefaultPooledObject(final T object) {
62 this.object = object;
63 }
64
65
66
67
68
69
70 @Override
71 public synchronized boolean allocate() {
72 if (state == PooledObjectState.IDLE) {
73 state = PooledObjectState.ALLOCATED;
74 lastBorrowInstant = now();
75 lastUseInstant = lastBorrowInstant;
76 borrowedCount++;
77 if (logAbandoned) {
78 borrowedBy.fillInStackTrace();
79 }
80 return true;
81 }
82 if (state == PooledObjectState.EVICTION) {
83
84 state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
85 }
86
87
88 return false;
89 }
90
91 @Override
92 public int compareTo(final PooledObject<T> other) {
93 final int compareTo = getLastReturnInstant().compareTo(other.getLastReturnInstant());
94 if (compareTo == 0) {
95
96
97
98
99 return System.identityHashCode(this) - System.identityHashCode(other);
100 }
101 return compareTo;
102 }
103
104
105
106
107
108
109
110
111
112 @Override
113 public synchronized boolean deallocate() {
114 if (state == PooledObjectState.ALLOCATED || state == PooledObjectState.RETURNING) {
115 state = PooledObjectState.IDLE;
116 lastReturnInstant = now();
117 borrowedBy.clear();
118 return true;
119 }
120
121 return false;
122 }
123
124 @Override
125 public synchronized boolean endEvictionTest(
126 final Deque<PooledObject<T>> idleQueue) {
127 if (state == PooledObjectState.EVICTION) {
128 state = PooledObjectState.IDLE;
129 return true;
130 }
131 if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
132 state = PooledObjectState.IDLE;
133 idleQueue.offerFirst(this);
134 }
135
136 return false;
137 }
138
139 @Override
140 public long getActiveTimeMillis() {
141 return getActiveDuration().toMillis();
142 }
143
144
145
146
147
148
149 @Override
150 public long getBorrowedCount() {
151 return borrowedCount;
152 }
153
154 @Override
155 public Instant getCreateInstant() {
156 return createInstant;
157 }
158
159 @Override
160 public long getCreateTime() {
161 return createInstant.toEpochMilli();
162 }
163
164 @Override
165 public Duration getIdleDuration() {
166
167
168
169 final Duration elapsed = Duration.between(lastReturnInstant, now());
170 return elapsed.isNegative() ? Duration.ZERO : elapsed;
171 }
172
173 @Override
174 public Duration getIdleTime() {
175 return getIdleDuration();
176 }
177
178 @Override
179 public long getIdleTimeMillis() {
180 return getIdleDuration().toMillis();
181 }
182
183 @Override
184 public Instant getLastBorrowInstant() {
185 return lastBorrowInstant;
186 }
187
188 @Override
189 public long getLastBorrowTime() {
190 return lastBorrowInstant.toEpochMilli();
191 }
192
193 @Override
194 public Instant getLastReturnInstant() {
195 return lastReturnInstant;
196 }
197
198 @Override
199 public long getLastReturnTime() {
200 return lastReturnInstant.toEpochMilli();
201 }
202
203
204
205
206
207
208
209
210
211
212 @Override
213 public Instant getLastUsedInstant() {
214 if (object instanceof TrackedUse) {
215 return PoolImplUtils.max(((TrackedUse) object).getLastUsedInstant(), lastUseInstant);
216 }
217 return lastUseInstant;
218 }
219
220
221
222
223
224
225
226
227
228
229 @Override
230 public long getLastUsedTime() {
231 return getLastUsedInstant().toEpochMilli();
232 }
233
234 @Override
235 public T getObject() {
236 return object;
237 }
238
239
240
241
242
243 @Override
244 public synchronized PooledObjectState getState() {
245 return state;
246 }
247
248
249
250
251 @Override
252 public synchronized void invalidate() {
253 state = PooledObjectState.INVALID;
254 }
255
256
257
258
259 @Override
260 public synchronized void markAbandoned() {
261 state = PooledObjectState.ABANDONED;
262 }
263
264
265
266
267 @Override
268 public synchronized void markReturning() {
269 state = PooledObjectState.RETURNING;
270 }
271
272
273
274
275
276
277 private Instant now() {
278 return systemClock.instant();
279 }
280
281 @Override
282 public void printStackTrace(final PrintWriter writer) {
283 boolean written = borrowedBy.printStackTrace(writer);
284 written |= usedBy.printStackTrace(writer);
285 if (written) {
286 writer.flush();
287 }
288 }
289
290 @Override
291 public void setLogAbandoned(final boolean logAbandoned) {
292 this.logAbandoned = logAbandoned;
293 }
294
295
296
297
298
299
300
301
302
303
304
305 @Override
306 public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
307 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
308 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
309 true, requireFullStackTrace);
310 usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
311 false, requireFullStackTrace);
312 }
313
314 @Override
315 public synchronized boolean startEvictionTest() {
316 if (state == PooledObjectState.IDLE) {
317 state = PooledObjectState.EVICTION;
318 return true;
319 }
320 return false;
321 }
322
323 @Override
324 public String toString() {
325 final StringBuilder result = new StringBuilder();
326 result.append("Object: ");
327 result.append(object.toString());
328 result.append(", State: ");
329 synchronized (this) {
330 result.append(state.toString());
331 }
332 return result.toString();
333
334 }
335
336 @Override
337 public void use() {
338 lastUseInstant = now();
339 usedBy.fillInStackTrace();
340 }
341
342
343 }