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 org.apache.commons.pool2.PooledObject; 020import org.apache.commons.pool2.PooledObjectState; 021import org.apache.commons.pool2.TrackedUse; 022 023import java.io.PrintWriter; 024import java.util.Deque; 025 026/** 027 * This wrapper is used to track the additional information, such as state, for 028 * the pooled objects. 029 * <p> 030 * This class is intended to be thread-safe. 031 * </p> 032 * 033 * @param <T> the type of object in the pool 034 * 035 * @since 2.0 036 */ 037public class DefaultPooledObject<T> implements PooledObject<T> { 038 039 private final T object; 040 private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid 041 private final long createTime = System.currentTimeMillis(); 042 private volatile long lastBorrowTime = createTime; 043 private volatile long lastUseTime = createTime; 044 private volatile long lastReturnTime = createTime; 045 private volatile boolean logAbandoned = false; 046 private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE; 047 private volatile CallStack usedBy = NoOpCallStack.INSTANCE; 048 private volatile long borrowedCount = 0; 049 050 /** 051 * Creates a new instance that wraps the provided object so that the pool can 052 * track the state of the pooled object. 053 * 054 * @param object The object to wrap 055 */ 056 public DefaultPooledObject(final T object) { 057 this.object = object; 058 } 059 060 @Override 061 public T getObject() { 062 return object; 063 } 064 065 @Override 066 public long getCreateTime() { 067 return createTime; 068 } 069 070 @Override 071 public long getActiveTimeMillis() { 072 // Take copies to avoid threading issues 073 final long rTime = lastReturnTime; 074 final long bTime = lastBorrowTime; 075 076 if (rTime > bTime) { 077 return rTime - bTime; 078 } 079 return System.currentTimeMillis() - bTime; 080 } 081 082 @Override 083 public long getIdleTimeMillis() { 084 final long elapsed = System.currentTimeMillis() - lastReturnTime; 085 // elapsed may be negative if: 086 // - another thread updates lastReturnTime during the calculation window 087 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 088 return elapsed >= 0 ? elapsed : 0; 089 } 090 091 @Override 092 public long getLastBorrowTime() { 093 return lastBorrowTime; 094 } 095 096 @Override 097 public long getLastReturnTime() { 098 return lastReturnTime; 099 } 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}