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 * @since 2.0 038 */ 039public class DefaultPooledObject<T> implements PooledObject<T> { 040 041 private final T object; 042 private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid 043 private final Clock systemClock = Clock.systemUTC(); 044 private final Instant createInstant = now(); 045 046 private volatile Instant lastBorrowInstant = createInstant; 047 private volatile Instant lastUseInstant = createInstant; 048 private volatile Instant lastReturnInstant = createInstant; 049 private volatile boolean logAbandoned; 050 private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE; 051 private volatile CallStack usedBy = NoOpCallStack.INSTANCE; 052 private volatile long borrowedCount; 053 054 /** 055 * Creates a new instance that wraps the provided object so that the pool can 056 * track the state of the pooled object. 057 * 058 * @param object The object to wrap 059 */ 060 public DefaultPooledObject(final T object) { 061 this.object = object; 062 } 063 064 /** 065 * Allocates the object. 066 * 067 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 068 */ 069 @Override 070 public synchronized boolean allocate() { 071 if (state == PooledObjectState.IDLE) { 072 state = PooledObjectState.ALLOCATED; 073 lastBorrowInstant = now(); 074 lastUseInstant = lastBorrowInstant; 075 borrowedCount++; 076 if (logAbandoned) { 077 borrowedBy.fillInStackTrace(); 078 } 079 return true; 080 } 081 if (state == PooledObjectState.EVICTION) { 082 // TODO Allocate anyway and ignore eviction test 083 state = PooledObjectState.EVICTION_RETURN_TO_HEAD; 084 } 085 // TODO if validating and testOnBorrow == true then pre-allocate for 086 // performance 087 return false; 088 } 089 090 @Override 091 public int compareTo(final PooledObject<T> other) { 092 final int compareTo = getLastReturnInstant().compareTo(other.getLastReturnInstant()); 093 if (compareTo == 0) { 094 // Make sure the natural ordering is broadly consistent with equals 095 // although this will break down if distinct objects have the same 096 // identity hash code. 097 // see java.lang.Comparable Javadocs 098 return System.identityHashCode(this) - System.identityHashCode(other); 099 } 100 return compareTo; 101 } 102 103 /** 104 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} 105 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED} 106 * or {@link PooledObjectState#RETURNING RETURNING}. 107 * 108 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} 109 * or {@link PooledObjectState#RETURNING RETURNING}. 110 */ 111 @Override 112 public synchronized boolean deallocate() { 113 if (state == PooledObjectState.ALLOCATED || state == PooledObjectState.RETURNING) { 114 state = PooledObjectState.IDLE; 115 lastReturnInstant = now(); 116 borrowedBy.clear(); 117 return true; 118 } 119 120 return false; 121 } 122 123 @Override 124 public synchronized boolean endEvictionTest( 125 final Deque<PooledObject<T>> idleQueue) { 126 if (state == PooledObjectState.EVICTION) { 127 state = PooledObjectState.IDLE; 128 return true; 129 } 130 if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { 131 state = PooledObjectState.IDLE; 132 idleQueue.offerFirst(this); 133 } 134 135 return false; 136 } 137 138 @Override 139 public long getActiveTimeMillis() { 140 return getActiveDuration().toMillis(); 141 } 142 143 /** 144 * Gets the number of times this object has been borrowed. 145 * @return The number of times this object has been borrowed. 146 * @since 2.1 147 */ 148 @Override 149 public long getBorrowedCount() { 150 return borrowedCount; 151 } 152 153 @Override 154 public Instant getCreateInstant() { 155 return createInstant; 156 } 157 158 @Override 159 public long getCreateTime() { 160 return createInstant.toEpochMilli(); 161 } 162 163 @Override 164 public Duration getIdleDuration() { 165 // elapsed may be negative if: 166 // - another thread updates lastReturnInstant during the calculation window 167 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 168 final Duration elapsed = Duration.between(lastReturnInstant, now()); 169 return elapsed.isNegative() ? Duration.ZERO : elapsed; 170 } 171 172 @Override 173 public Duration getIdleTime() { 174 return getIdleDuration(); 175 } 176 177 @Override 178 public long getIdleTimeMillis() { 179 return getIdleDuration().toMillis(); 180 } 181 182 @Override 183 public Instant getLastBorrowInstant() { 184 return lastBorrowInstant; 185 } 186 187 @Override 188 public long getLastBorrowTime() { 189 return lastBorrowInstant.toEpochMilli(); 190 } 191 192 @Override 193 public Instant getLastReturnInstant() { 194 return lastReturnInstant; 195 } 196 197 @Override 198 public long getLastReturnTime() { 199 return lastReturnInstant.toEpochMilli(); 200 } 201 202 /** 203 * Gets an estimate of the last time this object was used. If the class 204 * of the pooled object implements {@link TrackedUse}, what is returned is 205 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 206 * {@link #getLastBorrowTime()}; otherwise this method gives the same 207 * value as {@link #getLastBorrowTime()}. 208 * 209 * @return the last Instant this object was used. 210 */ 211 @Override 212 public Instant getLastUsedInstant() { 213 if (object instanceof TrackedUse) { 214 return PoolImplUtils.max(((TrackedUse) object).getLastUsedInstant(), lastUseInstant); 215 } 216 return lastUseInstant; 217 } 218 219 /** 220 * Gets an estimate of the last time this object was used. If the class 221 * of the pooled object implements {@link TrackedUse}, what is returned is 222 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 223 * {@link #getLastBorrowTime()}; otherwise this method gives the same 224 * value as {@link #getLastBorrowTime()}. 225 * 226 * @return the last time this object was used 227 */ 228 @Override 229 public long getLastUsedTime() { 230 return getLastUsedInstant().toEpochMilli(); 231 } 232 233 @Override 234 public T getObject() { 235 return object; 236 } 237 238 /** 239 * Gets the state of this object. 240 * @return state 241 */ 242 @Override 243 public synchronized PooledObjectState getState() { 244 return state; 245 } 246 247 /** 248 * Sets the state to {@link PooledObjectState#INVALID INVALID}. 249 */ 250 @Override 251 public synchronized void invalidate() { 252 state = PooledObjectState.INVALID; 253 } 254 255 /** 256 * Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}. 257 */ 258 @Override 259 public synchronized void markAbandoned() { 260 state = PooledObjectState.ABANDONED; 261 } 262 263 /** 264 * Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}. 265 */ 266 @Override 267 public synchronized void markReturning() { 268 state = PooledObjectState.RETURNING; 269 } 270 271 /** 272 * Gets the current instant of the clock. 273 * 274 * @return the current instant of the clock. 275 */ 276 private Instant now() { 277 return systemClock.instant(); 278 } 279 280 @Override 281 public void printStackTrace(final PrintWriter writer) { 282 boolean written = borrowedBy.printStackTrace(writer); 283 written |= usedBy.printStackTrace(writer); 284 if (written) { 285 writer.flush(); 286 } 287 } 288 289 @Override 290 public void setLogAbandoned(final boolean logAbandoned) { 291 this.logAbandoned = logAbandoned; 292 } 293 294 /** 295 * Configures the stack trace generation strategy based on whether or not fully 296 * detailed stack traces are required. When set to false, abandoned logs may 297 * only include caller class information rather than method names, line numbers, 298 * and other normal metadata available in a full stack trace. 299 * 300 * @param requireFullStackTrace the new configuration setting for abandoned object 301 * logging 302 * @since 2.5 303 */ 304 @Override 305 public void setRequireFullStackTrace(final boolean requireFullStackTrace) { 306 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + 307 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", 308 true, requireFullStackTrace); 309 usedBy = CallStackUtils.newCallStack("The last code to use this object was:", 310 false, requireFullStackTrace); 311 } 312 313 @Override 314 public synchronized boolean startEvictionTest() { 315 if (state == PooledObjectState.IDLE) { 316 state = PooledObjectState.EVICTION; 317 return true; 318 } 319 return false; 320 } 321 322 @Override 323 public String toString() { 324 final StringBuilder result = new StringBuilder(); 325 result.append("Object: "); 326 result.append(object.toString()); 327 result.append(", State: "); 328 synchronized (this) { 329 result.append(state.toString()); 330 } 331 return result.toString(); 332 // TODO add other attributes 333 } 334 335 @Override 336 public void use() { 337 lastUseInstant = now(); 338 usedBy.fillInStackTrace(); 339 } 340 341}