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 Exception borrowedByCopy = this.borrowedBy; 247 if (borrowedByCopy != null) { 248 borrowedByCopy.printStackTrace(writer); 249 } 250 Exception usedByCopy = this.usedBy; 251 if (usedByCopy != null) { 252 usedByCopy.printStackTrace(writer); 253 } 254 } 255 256 /** 257 * Returns the state of this object. 258 * @return state 259 */ 260 @Override 261 public synchronized PooledObjectState getState() { 262 return state; 263 } 264 265 /** 266 * Marks the pooled object as abandoned. 267 */ 268 @Override 269 public synchronized void markAbandoned() { 270 state = PooledObjectState.ABANDONED; 271 } 272 273 /** 274 * Marks the object as returning to the pool. 275 */ 276 @Override 277 public synchronized void markReturning() { 278 state = PooledObjectState.RETURNING; 279 } 280 281 @Override 282 public void setLogAbandoned(boolean logAbandoned) { 283 this.logAbandoned = logAbandoned; 284 } 285 286 /** 287 * Used to track how an object was obtained from the pool (the stack trace 288 * of the exception will show which code borrowed the object) and when the 289 * object was borrowed. 290 */ 291 static class AbandonedObjectCreatedException extends Exception { 292 293 private static final long serialVersionUID = 7398692158058772916L; 294 295 /** Date format */ 296 //@GuardedBy("format") 297 private static final SimpleDateFormat format = new SimpleDateFormat 298 ("'Pooled object created' yyyy-MM-dd HH:mm:ss Z " + 299 "'by the following code has not been returned to the pool:'"); 300 301 private final long _createdTime; 302 303 /** 304 * Create a new instance. 305 * <p> 306 * @see Exception#Exception() 307 */ 308 public AbandonedObjectCreatedException() { 309 super(); 310 _createdTime = System.currentTimeMillis(); 311 } 312 313 // Override getMessage to avoid creating objects and formatting 314 // dates unless the log message will actually be used. 315 @Override 316 public String getMessage() { 317 String msg; 318 synchronized(format) { 319 msg = format.format(new Date(_createdTime)); 320 } 321 return msg; 322 } 323 } 324}