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 package org.apache.tomcat.dbcp.pool2.impl;
18
19 import java.io.PrintWriter;
20 import java.util.Deque;
21
22 import org.apache.tomcat.dbcp.pool2.PooledObject;
23 import org.apache.tomcat.dbcp.pool2.PooledObjectState;
24 import org.apache.tomcat.dbcp.pool2.TrackedUse;
25
26 /**
27 * This wrapper is used to track the additional information, such as state, for
28 * the pooled objects.
29 * <p>
30 * This class is intended to be thread-safe.
31 * </p>
32 *
33 * @param <T> the type of object in the pool
34 *
35 * @since 2.0
36 */
37 public class DefaultPooledObject<T> implements PooledObject<T> {
38
39 private final T object;
40 private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid
41 private final long createTime = System.currentTimeMillis();
42 private volatile long lastBorrowTime = createTime;
43 private volatile long lastUseTime = createTime;
44 private volatile long lastReturnTime = createTime;
45 private volatile boolean logAbandoned = false;
46 private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE;
47 private volatile CallStack usedBy = NoOpCallStack.INSTANCE;
48 private volatile long borrowedCount = 0;
49
50 /**
51 * Creates a new instance that wraps the provided object so that the pool can
52 * track the state of the pooled object.
53 *
54 * @param object The object to wrap
55 */
56 public DefaultPooledObject(final T object) {
57 this.object = object;
58 }
59
60 @Override
61 public T getObject() {
62 return object;
63 }
64
65 @Override
66 public long getCreateTime() {
67 return createTime;
68 }
69
70 @Override
71 public long getActiveTimeMillis() {
72 // Take copies to avoid threading issues
73 final long rTime = lastReturnTime;
74 final long bTime = lastBorrowTime;
75
76 if (rTime > bTime) {
77 return rTime - bTime;
78 }
79 return System.currentTimeMillis() - bTime;
80 }
81
82 @Override
83 public long getIdleTimeMillis() {
84 final long elapsed = System.currentTimeMillis() - lastReturnTime;
85 // elapsed may be negative if:
86 // - another thread updates lastReturnTime during the calculation window
87 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back)
88 return elapsed >= 0 ? elapsed : 0;
89 }
90
91 @Override
92 public long getLastBorrowTime() {
93 return lastBorrowTime;
94 }
95
96 @Override
97 public long getLastReturnTime() {
98 return lastReturnTime;
99 }
100
101 /**
102 * Gets the number of times this object has been borrowed.
103 * @return The number of times this object has been borrowed.
104 * @since 2.1
105 */
106 @Override
107 public long getBorrowedCount() {
108 return borrowedCount;
109 }
110
111 /**
112 * Returns an estimate of the last time this object was used. If the class
113 * of the pooled object implements {@link TrackedUse}, what is returned is
114 * the maximum of {@link TrackedUse#getLastUsed()} and
115 * {@link #getLastBorrowTime()}; otherwise this method gives the same
116 * value as {@link #getLastBorrowTime()}.
117 *
118 * @return the last time this object was used
119 */
120 @Override
121 public long getLastUsedTime() {
122 if (object instanceof TrackedUse) {
123 return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime);
124 }
125 return lastUseTime;
126 }
127
128 @Override
129 public int compareTo(final PooledObject<T> other) {
130 final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime();
131 if (lastActiveDiff == 0) {
132 // Make sure the natural ordering is broadly consistent with equals
133 // although this will break down if distinct objects have the same
134 // identity hash code.
135 // see java.lang.Comparable Javadocs
136 return System.identityHashCode(this) - System.identityHashCode(other);
137 }
138 // handle int overflow
139 return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
140 }
141
142 @Override
143 public String toString() {
144 final StringBuilder result = new StringBuilder();
145 result.append("Object: ");
146 result.append(object.toString());
147 result.append(", State: ");
148 synchronized (this) {
149 result.append(state.toString());
150 }
151 return result.toString();
152 // TODO add other attributes
153 }
154
155 @Override
156 public synchronized boolean startEvictionTest() {
157 if (state == PooledObjectState.IDLE) {
158 state = PooledObjectState.EVICTION;
159 return true;
160 }
161
162 return false;
163 }
164
165 @Override
166 public synchronized boolean endEvictionTest(
167 final Deque<PooledObject<T>> idleQueue) {
168 if (state == PooledObjectState.EVICTION) {
169 state = PooledObjectState.IDLE;
170 return true;
171 } else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
172 state = PooledObjectState.IDLE;
173 if (!idleQueue.offerFirst(this)) {
174 // TODO - Should never happen
175 }
176 }
177
178 return false;
179 }
180
181 /**
182 * Allocates the object.
183 *
184 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
185 */
186 @Override
187 public synchronized boolean allocate() {
188 if (state == PooledObjectState.IDLE) {
189 state = PooledObjectState.ALLOCATED;
190 lastBorrowTime = System.currentTimeMillis();
191 lastUseTime = lastBorrowTime;
192 borrowedCount++;
193 if (logAbandoned) {
194 borrowedBy.fillInStackTrace();
195 }
196 return true;
197 } else if (state == PooledObjectState.EVICTION) {
198 // TODO Allocate anyway and ignore eviction test
199 state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
200 return false;
201 }
202 // TODO if validating and testOnBorrow == true then pre-allocate for
203 // performance
204 return false;
205 }
206
207 /**
208 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE}
209 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}.
210 *
211 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
212 */
213 @Override
214 public synchronized boolean deallocate() {
215 if (state == PooledObjectState.ALLOCATED ||
216 state == PooledObjectState.RETURNING) {
217 state = PooledObjectState.IDLE;
218 lastReturnTime = System.currentTimeMillis();
219 borrowedBy.clear();
220 return true;
221 }
222
223 return false;
224 }
225
226 /**
227 * Sets the state to {@link PooledObjectState#INVALID INVALID}
228 */
229 @Override
230 public synchronized void invalidate() {
231 state = PooledObjectState.INVALID;
232 }
233
234 @Override
235 public void use() {
236 lastUseTime = System.currentTimeMillis();
237 usedBy.fillInStackTrace();
238 }
239
240 @Override
241 public void printStackTrace(final PrintWriter writer) {
242 boolean written = borrowedBy.printStackTrace(writer);
243 written |= usedBy.printStackTrace(writer);
244 if (written) {
245 writer.flush();
246 }
247 }
248
249 /**
250 * Returns the state of this object.
251 * @return state
252 */
253 @Override
254 public synchronized PooledObjectState getState() {
255 return state;
256 }
257
258 /**
259 * Marks the pooled object as abandoned.
260 */
261 @Override
262 public synchronized void markAbandoned() {
263 state = PooledObjectState.ABANDONED;
264 }
265
266 /**
267 * Marks the object as returning to the pool.
268 */
269 @Override
270 public synchronized void markReturning() {
271 state = PooledObjectState.RETURNING;
272 }
273
274 @Override
275 public void setLogAbandoned(final boolean logAbandoned) {
276 this.logAbandoned = logAbandoned;
277 }
278
279 /**
280 * Configures the stack trace generation strategy based on whether or not fully
281 * detailed stack traces are required. When set to false, abandoned logs may
282 * only include caller class information rather than method names, line numbers,
283 * and other normal metadata available in a full stack trace.
284 *
285 * @param requireFullStackTrace the new configuration setting for abandoned object
286 * logging
287 * @since 2.5
288 */
289 @Override
290 public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
291 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
292 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
293 true, requireFullStackTrace);
294 usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
295 false, requireFullStackTrace);
296 }
297
298 }
299