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 return System.currentTimeMillis() - lastReturnTime; 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 * Get the number of times this object has been borrowed. 103 * @return The number of times this object has been borrowed. 104 */ 105 public long getBorrowedCount() { 106 return borrowedCount; 107 } 108 109 /** 110 * Return an estimate of the last time this object was used. If the class 111 * of the pooled object implements {@link TrackedUse}, what is returned is 112 * the maximum of {@link TrackedUse#getLastUsed()} and 113 * {@link #getLastBorrowTime()}; otherwise this method gives the same 114 * value as {@link #getLastBorrowTime()}. 115 * 116 * @return the last time this object was used 117 */ 118 @Override 119 public long getLastUsedTime() { 120 if (object instanceof TrackedUse) { 121 return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime); 122 } else { 123 return lastUseTime; 124 } 125 } 126 127 @Override 128 public int compareTo(PooledObject<T> other) { 129 final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime(); 130 if (lastActiveDiff == 0) { 131 // Make sure the natural ordering is broadly consistent with equals 132 // although this will break down if distinct objects have the same 133 // identity hash code. 134 // see java.lang.Comparable Javadocs 135 return System.identityHashCode(this) - System.identityHashCode(other); 136 } 137 // handle int overflow 138 return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE); 139 } 140 141 @Override 142 public String toString() { 143 StringBuilder result = new StringBuilder(); 144 result.append("Object: "); 145 result.append(object.toString()); 146 result.append(", State: "); 147 synchronized (this) { 148 result.append(state.toString()); 149 } 150 return result.toString(); 151 // TODO add other attributes 152 } 153 154 @Override 155 public synchronized boolean startEvictionTest() { 156 if (state == PooledObjectState.IDLE) { 157 state = PooledObjectState.EVICTION; 158 return true; 159 } 160 161 return false; 162 } 163 164 @Override 165 public synchronized boolean endEvictionTest( 166 Deque<PooledObject<T>> idleQueue) { 167 if (state == PooledObjectState.EVICTION) { 168 state = PooledObjectState.IDLE; 169 return true; 170 } else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { 171 state = PooledObjectState.IDLE; 172 if (!idleQueue.offerFirst(this)) { 173 // TODO - Should never happen 174 } 175 } 176 177 return false; 178 } 179 180 /** 181 * Allocates the object. 182 * 183 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 184 */ 185 @Override 186 public synchronized boolean allocate() { 187 if (state == PooledObjectState.IDLE) { 188 state = PooledObjectState.ALLOCATED; 189 lastBorrowTime = System.currentTimeMillis(); 190 lastUseTime = lastBorrowTime; 191 borrowedCount++; 192 if (logAbandoned) { 193 borrowedBy = new AbandonedObjectCreatedException(); 194 } 195 return true; 196 } else if (state == PooledObjectState.EVICTION) { 197 // TODO Allocate anyway and ignore eviction test 198 state = PooledObjectState.EVICTION_RETURN_TO_HEAD; 199 return false; 200 } 201 // TODO if validating and testOnBorrow == true then pre-allocate for 202 // performance 203 return false; 204 } 205 206 /** 207 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} 208 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. 209 * 210 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} 211 */ 212 @Override 213 public synchronized boolean deallocate() { 214 if (state == PooledObjectState.ALLOCATED || 215 state == PooledObjectState.RETURNING) { 216 state = PooledObjectState.IDLE; 217 lastReturnTime = System.currentTimeMillis(); 218 if (borrowedBy != null) { 219 borrowedBy = null; 220 } 221 return true; 222 } 223 224 return false; 225 } 226 227 /** 228 * Sets the state to {@link PooledObjectState#INVALID INVALID} 229 */ 230 @Override 231 public synchronized void invalidate() { 232 state = PooledObjectState.INVALID; 233 } 234 235 @Override 236 public void use() { 237 lastUseTime = System.currentTimeMillis(); 238 usedBy = new Exception("The last code to use this object was:"); 239 } 240 241 @Override 242 public void printStackTrace(PrintWriter writer) { 243 Exception borrowedByCopy = this.borrowedBy; 244 if (borrowedByCopy != null) { 245 borrowedByCopy.printStackTrace(writer); 246 } 247 Exception usedByCopy = this.usedBy; 248 if (usedByCopy != null) { 249 usedByCopy.printStackTrace(writer); 250 } 251 } 252 253 /** 254 * Returns the state of this object. 255 * @return state 256 */ 257 @Override 258 public synchronized PooledObjectState getState() { 259 return state; 260 } 261 262 /** 263 * Marks the pooled object as abandoned. 264 */ 265 @Override 266 public synchronized void markAbandoned() { 267 state = PooledObjectState.ABANDONED; 268 } 269 270 /** 271 * Marks the object as returning to the pool. 272 */ 273 @Override 274 public synchronized void markReturning() { 275 state = PooledObjectState.RETURNING; 276 } 277 278 @Override 279 public void setLogAbandoned(boolean logAbandoned) { 280 this.logAbandoned = logAbandoned; 281 } 282 283 /** 284 * Used to track how an object was obtained from the pool (the stack trace 285 * of the exception will show which code borrowed the object) and when the 286 * object was borrowed. 287 */ 288 static class AbandonedObjectCreatedException extends Exception { 289 290 private static final long serialVersionUID = 7398692158058772916L; 291 292 /** Date format */ 293 //@GuardedBy("this") 294 private static final SimpleDateFormat format = new SimpleDateFormat 295 ("'Pooled object created' yyyy-MM-dd HH:mm:ss Z " + 296 "'by the following code has not been returned to the pool:'"); 297 298 private final long _createdTime; 299 300 /** 301 * Create a new instance. 302 * <p> 303 * @see Exception#Exception() 304 */ 305 public AbandonedObjectCreatedException() { 306 super(); 307 _createdTime = System.currentTimeMillis(); 308 } 309 310 // Override getMessage to avoid creating objects and formatting 311 // dates unless the log message will actually be used. 312 @Override 313 public String getMessage() { 314 String msg; 315 synchronized(format) { 316 msg = format.format(new Date(_createdTime)); 317 } 318 return msg; 319 } 320 } 321}