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 idleQueue.offerFirst(this); 134 } 135 136 return false; 137 } 138 139 @Override 140 public long getActiveTimeMillis() { 141 return getActiveDuration().toMillis(); 142 } 143 144 /** 145 * Gets the number of times this object has been borrowed. 146 * @return The number of times this object has been borrowed. 147 * @since 2.1 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 // elapsed may be negative if: 167 // - another thread updates lastReturnInstant during the calculation window 168 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 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 * Gets an estimate of the last time this object was used. If the class 205 * of the pooled object implements {@link TrackedUse}, what is returned is 206 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 207 * {@link #getLastBorrowTime()}; otherwise this method gives the same 208 * value as {@link #getLastBorrowTime()}. 209 * 210 * @return the last Instant this object was used. 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 * Gets an estimate of the last time this object was used. If the class 222 * of the pooled object implements {@link TrackedUse}, what is returned is 223 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 224 * {@link #getLastBorrowTime()}; otherwise this method gives the same 225 * value as {@link #getLastBorrowTime()}. 226 * 227 * @return the last time this object was used 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 * Gets the state of this object. 241 * @return state 242 */ 243 @Override 244 public synchronized PooledObjectState getState() { 245 return state; 246 } 247 248 /** 249 * Sets the state to {@link PooledObjectState#INVALID INVALID}. 250 */ 251 @Override 252 public synchronized void invalidate() { 253 state = PooledObjectState.INVALID; 254 } 255 256 /** 257 * Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}. 258 */ 259 @Override 260 public synchronized void markAbandoned() { 261 state = PooledObjectState.ABANDONED; 262 } 263 264 /** 265 * Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}. 266 */ 267 @Override 268 public synchronized void markReturning() { 269 state = PooledObjectState.RETURNING; 270 } 271 272 /** 273 * Gets the current instant of the clock. 274 * 275 * @return the current instant of the clock. 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 * Configures the stack trace generation strategy based on whether or not fully 297 * detailed stack traces are required. When set to false, abandoned logs may 298 * only include caller class information rather than method names, line numbers, 299 * and other normal metadata available in a full stack trace. 300 * 301 * @param requireFullStackTrace the new configuration setting for abandoned object 302 * logging 303 * @since 2.5 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 // TODO add other attributes 334 } 335 336 @Override 337 public void use() { 338 lastUseInstant = now(); 339 usedBy.fillInStackTrace(); 340 } 341 342 343}