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.text.SimpleDateFormat; 021import java.util.Date; 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 * 034 * @param <T> the type of object in the pool 035 * 036 * @version $Revision: $ 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 long createTime = System.currentTimeMillis(); 045 private volatile long lastBorrowTime = createTime; 046 private volatile long lastUseTime = createTime; 047 private volatile long lastReturnTime = createTime; 048 private volatile boolean logAbandoned = false; 049 private volatile Exception borrowedBy = null; 050 private volatile Exception usedBy = null; 051 private volatile long borrowedCount = 0; 052 053 /** 054 * Create a new instance that wraps the provided object so that the pool can 055 * track the state of the pooled object. 056 * 057 * @param object The object to wrap 058 */ 059 public DefaultPooledObject(T object) { 060 this.object = object; 061 } 062 063 @Override 064 public T getObject() { 065 return object; 066 } 067 068 @Override 069 public long getCreateTime() { 070 return createTime; 071 } 072 073 @Override 074 public long getActiveTimeMillis() { 075 // Take copies to avoid threading issues 076 long rTime = lastReturnTime; 077 long bTime = lastBorrowTime; 078 079 if (rTime > bTime) { 080 return rTime - bTime; 081 } else { 082 return System.currentTimeMillis() - bTime; 083 } 084 } 085 086 @Override 087 public long getIdleTimeMillis() { 088 final long elapsed = System.currentTimeMillis() - lastReturnTime; 089 // elapsed may be negative if: 090 // - another thread updates lastReturnTime during the calculation window 091 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 092 return elapsed >= 0 ? elapsed : 0; 093 } 094 095 @Override 096 public long getLastBorrowTime() { 097 return lastBorrowTime; 098 } 099 100 @Override 101 public long getLastReturnTime() { 102 return lastReturnTime; 103 } 104 105 /** 106 * Get the number of times this object has been borrowed. 107 * @return The number of times this object has been borrowed. 108 * @since 2.1 109 */ 110 public long getBorrowedCount() { 111 return borrowedCount; 112 } 113 114 /** 115 * Return an estimate of the last time this object was used. If the class 116 * of the pooled object implements {@link TrackedUse}, what is returned is 117 * the maximum of {@link TrackedUse#getLastUsed()} and 118 * {@link #getLastBorrowTime()}; otherwise this method gives the same 119 * value as {@link #getLastBorrowTime()}. 120 * 121 * @return the last time this object was used 122 */ 123 @Override 124 public long getLastUsedTime() { 125 if (object instanceof TrackedUse) { 126 return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime); 127 } else { 128 return lastUseTime; 129 } 130 } 131 132 @Override 133 public int compareTo(PooledObject<T> other) { 134 final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime(); 135 if (lastActiveDiff == 0) { 136 // Make sure the natural ordering is broadly consistent with equals 137 // although this will break down if distinct objects have the same 138 // identity hash code. 139 // see java.lang.Comparable Javadocs 140 return System.identityHashCode(this) - System.identityHashCode(other); 141 } 142 // handle int overflow 143 return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE); 144 } 145 146 @Override 147 public String toString() { 148 StringBuilder result = new StringBuilder(); 149 result.append("Object: "); 150 result.append(object.toString()); 151 result.append(", State: "); 152 synchronized (this) { 153 result.append(state.toString()); 154 } 155 return result.toString(); 156 // TODO add other attributes 157 } 158 159 @Override 160 public synchronized boolean startEvictionTest() { 161 if (state == PooledObjectState.IDLE) { 162 state = PooledObjectState.EVICTION; 163 return true; 164 } 165 166 return false; 167 } 168 169 @Override 170 public synchronized boolean endEvictionTest( 171 Deque<PooledObject<T>> idleQueue) { 172 if (state == PooledObjectState.EVICTION) { 173 state = PooledObjectState.IDLE; 174 return true; 175 } else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { 176 state = PooledObjectState.IDLE; 177 if (!idleQueue.offerFirst(this)) { 178 // TODO - Should never happen 179 } 180 } 181 182 return false; 183 } 184 185 /** 186 * Allocates the object. 187 * 188 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 189 */ 190 @Override 191 public synchronized boolean allocate() { 192 if (state == PooledObjectState.IDLE) { 193 state = PooledObjectState.ALLOCATED; 194 lastBorrowTime = System.currentTimeMillis(); 195 lastUseTime = lastBorrowTime; 196 borrowedCount++; 197 if (logAbandoned) { 198 borrowedBy = new AbandonedObjectCreatedException(); 199 } 200 return true; 201 } else if (state == PooledObjectState.EVICTION) { 202 // TODO Allocate anyway and ignore eviction test 203 state = PooledObjectState.EVICTION_RETURN_TO_HEAD; 204 return false; 205 } 206 // TODO if validating and testOnBorrow == true then pre-allocate for 207 // performance 208 return false; 209 } 210 211 /** 212 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} 213 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. 214 * 215 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} 216 */ 217 @Override 218 public synchronized boolean deallocate() { 219 if (state == PooledObjectState.ALLOCATED || 220 state == PooledObjectState.RETURNING) { 221 state = PooledObjectState.IDLE; 222 lastReturnTime = System.currentTimeMillis(); 223 borrowedBy = null; 224 return true; 225 } 226 227 return false; 228 } 229 230 /** 231 * Sets the state to {@link PooledObjectState#INVALID INVALID} 232 */ 233 @Override 234 public synchronized void invalidate() { 235 state = PooledObjectState.INVALID; 236 } 237 238 @Override 239 public void use() { 240 lastUseTime = System.currentTimeMillis(); 241 usedBy = new Exception("The last code to use this object was:"); 242 } 243 244 @Override 245 public void printStackTrace(PrintWriter writer) { 246 boolean written = false; 247 Exception borrowedByCopy = this.borrowedBy; 248 if (borrowedByCopy != null) { 249 borrowedByCopy.printStackTrace(writer); 250 written = true; 251 } 252 Exception usedByCopy = this.usedBy; 253 if (usedByCopy != null) { 254 usedByCopy.printStackTrace(writer); 255 written = true; 256 } 257 if (written) { 258 writer.flush(); 259 } 260 } 261 262 /** 263 * Returns the state of this object. 264 * @return state 265 */ 266 @Override 267 public synchronized PooledObjectState getState() { 268 return state; 269 } 270 271 /** 272 * Marks the pooled object as abandoned. 273 */ 274 @Override 275 public synchronized void markAbandoned() { 276 state = PooledObjectState.ABANDONED; 277 } 278 279 /** 280 * Marks the object as returning to the pool. 281 */ 282 @Override 283 public synchronized void markReturning() { 284 state = PooledObjectState.RETURNING; 285 } 286 287 @Override 288 public void setLogAbandoned(boolean logAbandoned) { 289 this.logAbandoned = logAbandoned; 290 } 291 292 /** 293 * Used to track how an object was obtained from the pool (the stack trace 294 * of the exception will show which code borrowed the object) and when the 295 * object was borrowed. 296 */ 297 static class AbandonedObjectCreatedException extends Exception { 298 299 private static final long serialVersionUID = 7398692158058772916L; 300 301 /** Date format */ 302 //@GuardedBy("format") 303 private static final SimpleDateFormat format = new SimpleDateFormat 304 ("'Pooled object created' yyyy-MM-dd HH:mm:ss Z " + 305 "'by the following code has not been returned to the pool:'"); 306 307 private final long _createdTime; 308 309 /** 310 * Create a new instance. 311 * <p> 312 * @see Exception#Exception() 313 */ 314 public AbandonedObjectCreatedException() { 315 super(); 316 _createdTime = System.currentTimeMillis(); 317 } 318 319 // Override getMessage to avoid creating objects and formatting 320 // dates unless the log message will actually be used. 321 @Override 322 public String getMessage() { 323 String msg; 324 synchronized(format) { 325 msg = format.format(new Date(_createdTime)); 326 } 327 return msg; 328 } 329 } 330}