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 * Create 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 * Get 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 public long getBorrowedCount() { 107 return borrowedCount; 108 } 109 110 /** 111 * Return an estimate of the last time this object was used. If the class 112 * of the pooled object implements {@link TrackedUse}, what is returned is 113 * the maximum of {@link TrackedUse#getLastUsed()} and 114 * {@link #getLastBorrowTime()}; otherwise this method gives the same 115 * value as {@link #getLastBorrowTime()}. 116 * 117 * @return the last time this object was used 118 */ 119 @Override 120 public long getLastUsedTime() { 121 if (object instanceof TrackedUse) { 122 return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime); 123 } 124 return lastUseTime; 125 } 126 127 @Override 128 public int compareTo(final 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 final 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 final 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.fillInStackTrace(); 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 borrowedBy.clear(); 219 return true; 220 } 221 222 return false; 223 } 224 225 /** 226 * Sets the state to {@link PooledObjectState#INVALID INVALID} 227 */ 228 @Override 229 public synchronized void invalidate() { 230 state = PooledObjectState.INVALID; 231 } 232 233 @Override 234 public void use() { 235 lastUseTime = System.currentTimeMillis(); 236 usedBy.fillInStackTrace(); 237 } 238 239 @Override 240 public void printStackTrace(final PrintWriter writer) { 241 boolean written = borrowedBy.printStackTrace(writer); 242 written |= usedBy.printStackTrace(writer); 243 if (written) { 244 writer.flush(); 245 } 246 } 247 248 /** 249 * Returns the state of this object. 250 * @return state 251 */ 252 @Override 253 public synchronized PooledObjectState getState() { 254 return state; 255 } 256 257 /** 258 * Marks the pooled object as abandoned. 259 */ 260 @Override 261 public synchronized void markAbandoned() { 262 state = PooledObjectState.ABANDONED; 263 } 264 265 /** 266 * Marks the object as returning to the pool. 267 */ 268 @Override 269 public synchronized void markReturning() { 270 state = PooledObjectState.RETURNING; 271 } 272 273 @Override 274 public void setLogAbandoned(final boolean logAbandoned) { 275 this.logAbandoned = logAbandoned; 276 } 277 278 /** 279 * Configures the stack trace generation strategy based on whether or not fully 280 * detailed stack traces are required. When set to false, abandoned logs may 281 * only include caller class information rather than method names, line numbers, 282 * and other normal metadata available in a full stack trace. 283 * 284 * @param requireFullStackTrace the new configuration setting for abandoned object 285 * logging 286 * @since 2.5 287 */ 288 // TODO: uncomment below in 3.0 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}