001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.pool2.impl; 018 019import java.io.PrintWriter; 020import java.time.Clock; 021import java.time.Duration; 022import java.time.Instant; 023import java.util.Deque; 024 025import org.apache.commons.pool2.PooledObject; 026import org.apache.commons.pool2.PooledObjectState; 027import org.apache.commons.pool2.TrackedUse; 028 029/** 030 * This wrapper is used to track the additional information, such as state, for 031 * the pooled objects. 032 * <p> 033 * This class is intended to be thread-safe. 034 * </p> 035 * 036 * @param <T> the type of object in the pool 037 * 038 * @since 2.0 039 */ 040public class DefaultPooledObject<T> implements PooledObject<T> { 041 042 private final T object; 043 private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid 044 private final Clock systemClock = Clock.systemUTC(); 045 private final Instant createInstant = now(); 046 047 private volatile Instant lastBorrowInstant = createInstant; 048 private volatile Instant lastUseInstant = createInstant; 049 private volatile Instant lastReturnInstant = createInstant; 050 private volatile boolean logAbandoned; 051 private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE; 052 private volatile CallStack usedBy = NoOpCallStack.INSTANCE; 053 private volatile long borrowedCount; 054 055 /** 056 * Creates a new instance that wraps the provided object so that the pool can 057 * track the state of the pooled object. 058 * 059 * @param object The object to wrap 060 */ 061 public DefaultPooledObject(final T object) { 062 this.object = object; 063 } 064 065 /** 066 * Allocates the object. 067 * 068 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 069 */ 070 @Override 071 public synchronized boolean allocate() { 072 if (state == PooledObjectState.IDLE) { 073 state = PooledObjectState.ALLOCATED; 074 lastBorrowInstant = now(); 075 lastUseInstant = lastBorrowInstant; 076 borrowedCount++; 077 if (logAbandoned) { 078 borrowedBy.fillInStackTrace(); 079 } 080 return true; 081 } 082 if (state == PooledObjectState.EVICTION) { 083 // TODO Allocate anyway and ignore eviction test 084 state = PooledObjectState.EVICTION_RETURN_TO_HEAD; 085 } 086 // TODO if validating and testOnBorrow == true then pre-allocate for 087 // performance 088 return false; 089 } 090 091 @Override 092 public int compareTo(final PooledObject<T> other) { 093 final int compareTo = getLastReturnInstant().compareTo(other.getLastReturnInstant()); 094 if (compareTo == 0) { 095 // Make sure the natural ordering is broadly consistent with equals 096 // although this will break down if distinct objects have the same 097 // identity hash code. 098 // see java.lang.Comparable Javadocs 099 return System.identityHashCode(this) - System.identityHashCode(other); 100 } 101 return compareTo; 102 } 103 104 /** 105 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} 106 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED} 107 * or {@link PooledObjectState#RETURNING RETURNING}. 108 * 109 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} 110 * or {@link PooledObjectState#RETURNING RETURNING}. 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 if (!idleQueue.offerFirst(this)) { 134 // TODO - Should never happen 135 } 136 } 137 138 return false; 139 } 140 141 @Override 142 public long getActiveTimeMillis() { 143 return getActiveDuration().toMillis(); 144 } 145 146 /** 147 * Gets the number of times this object has been borrowed. 148 * @return The number of times this object has been borrowed. 149 * @since 2.1 150 */ 151 @Override 152 public long getBorrowedCount() { 153 return borrowedCount; 154 } 155 156 @Override 157 public Instant getCreateInstant() { 158 return createInstant; 159 } 160 161 @Override 162 public long getCreateTime() { 163 return createInstant.toEpochMilli(); 164 } 165 166 @Override 167 public Duration getIdleDuration() { 168 // elapsed may be negative if: 169 // - another thread updates lastReturnInstant during the calculation window 170 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 171 final Duration elapsed = Duration.between(lastReturnInstant, now()); 172 return elapsed.isNegative() ? Duration.ZERO : elapsed; 173 } 174 175 @Override 176 public Duration getIdleTime() { 177 return getIdleDuration(); 178 } 179 180 @Override 181 public long getIdleTimeMillis() { 182 return getIdleDuration().toMillis(); 183 } 184 185 @Override 186 public Instant getLastBorrowInstant() { 187 return lastBorrowInstant; 188 } 189 190 @Override 191 public long getLastBorrowTime() { 192 return lastBorrowInstant.toEpochMilli(); 193 } 194 195 @Override 196 public Instant getLastReturnInstant() { 197 return lastReturnInstant; 198 } 199 200 @Override 201 public long getLastReturnTime() { 202 return lastReturnInstant.toEpochMilli(); 203 } 204 205 /** 206 * Gets an estimate of the last time this object was used. If the class 207 * of the pooled object implements {@link TrackedUse}, what is returned is 208 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 209 * {@link #getLastBorrowTime()}; otherwise this method gives the same 210 * value as {@link #getLastBorrowTime()}. 211 * 212 * @return the last Instant this object was used. 213 */ 214 @Override 215 public Instant getLastUsedInstant() { 216 if (object instanceof TrackedUse) { 217 return PoolImplUtils.max(((TrackedUse) object).getLastUsedInstant(), lastUseInstant); 218 } 219 return lastUseInstant; 220 } 221 222 /** 223 * Gets an estimate of the last time this object was used. If the class 224 * of the pooled object implements {@link TrackedUse}, what is returned is 225 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 226 * {@link #getLastBorrowTime()}; otherwise this method gives the same 227 * value as {@link #getLastBorrowTime()}. 228 * 229 * @return the last time this object was used 230 */ 231 @Override 232 public long getLastUsedTime() { 233 return getLastUsedInstant().toEpochMilli(); 234 } 235 236 @Override 237 public T getObject() { 238 return object; 239 } 240 241 /** 242 * Gets the state of this object. 243 * @return state 244 */ 245 @Override 246 public synchronized PooledObjectState getState() { 247 return state; 248 } 249 250 /** 251 * Sets the state to {@link PooledObjectState#INVALID INVALID}. 252 */ 253 @Override 254 public synchronized void invalidate() { 255 state = PooledObjectState.INVALID; 256 } 257 258 /** 259 * Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}. 260 */ 261 @Override 262 public synchronized void markAbandoned() { 263 state = PooledObjectState.ABANDONED; 264 } 265 266 /** 267 * Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}. 268 */ 269 @Override 270 public synchronized void markReturning() { 271 state = PooledObjectState.RETURNING; 272 } 273 274 /** 275 * Gets the current instant of the clock. 276 * 277 * @return the current instant of the clock. 278 */ 279 private Instant now() { 280 return systemClock.instant(); 281 } 282 283 @Override 284 public void printStackTrace(final PrintWriter writer) { 285 boolean written = borrowedBy.printStackTrace(writer); 286 written |= usedBy.printStackTrace(writer); 287 if (written) { 288 writer.flush(); 289 } 290 } 291 292 @Override 293 public void setLogAbandoned(final boolean logAbandoned) { 294 this.logAbandoned = logAbandoned; 295 } 296 297 /** 298 * Configures the stack trace generation strategy based on whether or not fully 299 * detailed stack traces are required. When set to false, abandoned logs may 300 * only include caller class information rather than method names, line numbers, 301 * and other normal metadata available in a full stack trace. 302 * 303 * @param requireFullStackTrace the new configuration setting for abandoned object 304 * logging 305 * @since 2.5 306 */ 307 @Override 308 public void setRequireFullStackTrace(final boolean requireFullStackTrace) { 309 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + 310 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", 311 true, requireFullStackTrace); 312 usedBy = CallStackUtils.newCallStack("The last code to use this object was:", 313 false, requireFullStackTrace); 314 } 315 316 @Override 317 public synchronized boolean startEvictionTest() { 318 if (state == PooledObjectState.IDLE) { 319 state = PooledObjectState.EVICTION; 320 return true; 321 } 322 return false; 323 } 324 325 @Override 326 public String toString() { 327 final StringBuilder result = new StringBuilder(); 328 result.append("Object: "); 329 result.append(object.toString()); 330 result.append(", State: "); 331 synchronized (this) { 332 result.append(state.toString()); 333 } 334 return result.toString(); 335 // TODO add other attributes 336 } 337 338 @Override 339 public void use() { 340 lastUseInstant = now(); 341 usedBy.fillInStackTrace(); 342 } 343 344 345}