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.Instant; 022import java.util.Deque; 023 024import org.apache.commons.pool2.PooledObject; 025import org.apache.commons.pool2.PooledObjectState; 026import org.apache.commons.pool2.TrackedUse; 027 028/** 029 * This wrapper is used to track the additional information, such as state, for 030 * the pooled objects. 031 * <p> 032 * This class is intended to be thread-safe. 033 * </p> 034 * 035 * @param <T> the type of object in the pool 036 * 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 if (!idleQueue.offerFirst(this)) { 133 // TODO - Should never happen 134 } 135 } 136 137 return false; 138 } 139 140 @Override 141 public long getActiveTimeMillis() { 142 return getActiveDuration().toMillis(); 143 } 144 145 /** 146 * Gets the number of times this object has been borrowed. 147 * @return The number of times this object has been borrowed. 148 * @since 2.1 149 */ 150 @Override 151 public long getBorrowedCount() { 152 return borrowedCount; 153 } 154 155 @Override 156 public Instant getCreateInstant() { 157 return createInstant; 158 } 159 160 @Override 161 public long getCreateTime() { 162 return createInstant.toEpochMilli(); 163 } 164 165 @Override 166 public long getIdleTimeMillis() { 167 final long elapsed = System.currentTimeMillis() - lastReturnInstant.toEpochMilli(); 168 // elapsed may be negative if: 169 // - another thread updates lastReturnTime during the calculation window 170 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 171 return elapsed >= 0 ? elapsed : 0; 172 } 173 174 @Override 175 public Instant getLastBorrowInstant() { 176 return lastBorrowInstant; 177 } 178 179 @Override 180 public long getLastBorrowTime() { 181 return lastBorrowInstant.toEpochMilli(); 182 } 183 184 @Override 185 public Instant getLastReturnInstant() { 186 return lastReturnInstant; 187 } 188 189 @Override 190 public long getLastReturnTime() { 191 return lastReturnInstant.toEpochMilli(); 192 } 193 194 /** 195 * Gets an estimate of the last time this object was used. If the class 196 * of the pooled object implements {@link TrackedUse}, what is returned is 197 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 198 * {@link #getLastBorrowTime()}; otherwise this method gives the same 199 * value as {@link #getLastBorrowTime()}. 200 * 201 * @return the last Instant this object was used. 202 */ 203 @Override 204 public Instant getLastUsedInstant() { 205 if (object instanceof TrackedUse) { 206 return PoolImplUtils.max(((TrackedUse) object).getLastUsedInstant(), lastUseInstant); 207 } 208 return lastUseInstant; 209 } 210 211 /** 212 * Gets an estimate of the last time this object was used. If the class 213 * of the pooled object implements {@link TrackedUse}, what is returned is 214 * the maximum of {@link TrackedUse#getLastUsedInstant()} and 215 * {@link #getLastBorrowTime()}; otherwise this method gives the same 216 * value as {@link #getLastBorrowTime()}. 217 * 218 * @return the last time this object was used 219 */ 220 @Override 221 public long getLastUsedTime() { 222 return getLastUsedInstant().toEpochMilli(); 223 } 224 225 @Override 226 public T getObject() { 227 return object; 228 } 229 230 /** 231 * Gets the state of this object. 232 * @return state 233 */ 234 @Override 235 public synchronized PooledObjectState getState() { 236 return state; 237 } 238 239 /** 240 * Sets the state to {@link PooledObjectState#INVALID INVALID}. 241 */ 242 @Override 243 public synchronized void invalidate() { 244 state = PooledObjectState.INVALID; 245 } 246 247 /** 248 * Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}. 249 */ 250 @Override 251 public synchronized void markAbandoned() { 252 state = PooledObjectState.ABANDONED; 253 } 254 255 /** 256 * Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}. 257 */ 258 @Override 259 public synchronized void markReturning() { 260 state = PooledObjectState.RETURNING; 261 } 262 263 /** 264 * Gets the current instant of the clock. 265 * 266 * @return the current instant of the clock. 267 */ 268 private Instant now() { 269 return systemClock.instant(); 270 } 271 272 @Override 273 public void printStackTrace(final PrintWriter writer) { 274 boolean written = borrowedBy.printStackTrace(writer); 275 written |= usedBy.printStackTrace(writer); 276 if (written) { 277 writer.flush(); 278 } 279 } 280 281 @Override 282 public void setLogAbandoned(final boolean logAbandoned) { 283 this.logAbandoned = logAbandoned; 284 } 285 286 /** 287 * Configures the stack trace generation strategy based on whether or not fully 288 * detailed stack traces are required. When set to false, abandoned logs may 289 * only include caller class information rather than method names, line numbers, 290 * and other normal metadata available in a full stack trace. 291 * 292 * @param requireFullStackTrace the new configuration setting for abandoned object 293 * logging 294 * @since 2.5 295 */ 296 @Override 297 public void setRequireFullStackTrace(final boolean requireFullStackTrace) { 298 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + 299 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", 300 true, requireFullStackTrace); 301 usedBy = CallStackUtils.newCallStack("The last code to use this object was:", 302 false, requireFullStackTrace); 303 } 304 305 @Override 306 public synchronized boolean startEvictionTest() { 307 if (state == PooledObjectState.IDLE) { 308 state = PooledObjectState.EVICTION; 309 return true; 310 } 311 return false; 312 } 313 314 @Override 315 public String toString() { 316 final StringBuilder result = new StringBuilder(); 317 result.append("Object: "); 318 result.append(object.toString()); 319 result.append(", State: "); 320 synchronized (this) { 321 result.append(state.toString()); 322 } 323 return result.toString(); 324 // TODO add other attributes 325 } 326 327 @Override 328 public void use() { 329 lastUseInstant = now(); 330 usedBy.fillInStackTrace(); 331 } 332 333 334}