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.util.Deque; 021 022import org.apache.commons.pool2.PooledObject; 023import org.apache.commons.pool2.PooledObjectState; 024import org.apache.commons.pool2.TrackedUse; 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 createTimeMillis = System.currentTimeMillis(); 042 private volatile long lastBorrowTimeMillis = createTimeMillis; 043 private volatile long lastUseTimeMillis = createTimeMillis; 044 private volatile long lastReturnTimeMillis = createTimeMillis; 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; 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 /** 061 * Allocates the object. 062 * 063 * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 064 */ 065 @Override 066 public synchronized boolean allocate() { 067 if (state == PooledObjectState.IDLE) { 068 state = PooledObjectState.ALLOCATED; 069 lastBorrowTimeMillis = System.currentTimeMillis(); 070 lastUseTimeMillis = lastBorrowTimeMillis; 071 borrowedCount++; 072 if (logAbandoned) { 073 borrowedBy.fillInStackTrace(); 074 } 075 return true; 076 } 077 if (state == PooledObjectState.EVICTION) { 078 // TODO Allocate anyway and ignore eviction test 079 state = PooledObjectState.EVICTION_RETURN_TO_HEAD; 080 } 081 // TODO if validating and testOnBorrow == true then pre-allocate for 082 // performance 083 return false; 084 } 085 086 @Override 087 public int compareTo(final PooledObject<T> other) { 088 final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime(); 089 if (lastActiveDiff == 0) { 090 // Make sure the natural ordering is broadly consistent with equals 091 // although this will break down if distinct objects have the same 092 // identity hash code. 093 // see java.lang.Comparable Javadocs 094 return System.identityHashCode(this) - System.identityHashCode(other); 095 } 096 // handle int overflow 097 return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE); 098 } 099 100 /** 101 * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} 102 * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED} 103 * or {@link PooledObjectState#RETURNING RETURNING}. 104 * 105 * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} 106 * or {@link PooledObjectState#RETURNING RETURNING}. 107 */ 108 @Override 109 public synchronized boolean deallocate() { 110 if (state == PooledObjectState.ALLOCATED || 111 state == PooledObjectState.RETURNING) { 112 state = PooledObjectState.IDLE; 113 lastReturnTimeMillis = System.currentTimeMillis(); 114 borrowedBy.clear(); 115 return true; 116 } 117 118 return false; 119 } 120 121 @Override 122 public synchronized boolean endEvictionTest( 123 final Deque<PooledObject<T>> idleQueue) { 124 if (state == PooledObjectState.EVICTION) { 125 state = PooledObjectState.IDLE; 126 return true; 127 } 128 if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { 129 state = PooledObjectState.IDLE; 130 if (!idleQueue.offerFirst(this)) { 131 // TODO - Should never happen 132 } 133 } 134 135 return false; 136 } 137 138 @Override 139 public long getActiveTimeMillis() { 140 // Take copies to avoid threading issues 141 final long rTime = lastReturnTimeMillis; 142 final long bTime = lastBorrowTimeMillis; 143 144 if (rTime > bTime) { 145 return rTime - bTime; 146 } 147 return System.currentTimeMillis() - bTime; 148 } 149 150 /** 151 * Gets the number of times this object has been borrowed. 152 * @return The number of times this object has been borrowed. 153 * @since 2.1 154 */ 155 @Override 156 public long getBorrowedCount() { 157 return borrowedCount; 158 } 159 160 @Override 161 public long getCreateTime() { 162 return createTimeMillis; 163 } 164 165 @Override 166 public long getIdleTimeMillis() { 167 final long elapsed = System.currentTimeMillis() - lastReturnTimeMillis; 168 // elapsed may be negative if: 169 // - another thread updates lastReturnTime during the calculation window 170 // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) 171 return elapsed >= 0 ? elapsed : 0; 172 } 173 174 @Override 175 public long getLastBorrowTime() { 176 return lastBorrowTimeMillis; 177 } 178 179 @Override 180 public long getLastReturnTime() { 181 return lastReturnTimeMillis; 182 } 183 184 /** 185 * Gets an estimate of the last time this object was used. If the class 186 * of the pooled object implements {@link TrackedUse}, what is returned is 187 * the maximum of {@link TrackedUse#getLastUsed()} and 188 * {@link #getLastBorrowTime()}; otherwise this method gives the same 189 * value as {@link #getLastBorrowTime()}. 190 * 191 * @return the last time this object was used 192 */ 193 @Override 194 public long getLastUsedTime() { 195 if (object instanceof TrackedUse) { 196 return Math.max(((TrackedUse) object).getLastUsed(), lastUseTimeMillis); 197 } 198 return lastUseTimeMillis; 199 } 200 201 @Override 202 public T getObject() { 203 return object; 204 } 205 206 /** 207 * Gets the state of this object. 208 * @return state 209 */ 210 @Override 211 public synchronized PooledObjectState getState() { 212 return state; 213 } 214 215 /** 216 * Sets the state to {@link PooledObjectState#INVALID INVALID} 217 */ 218 @Override 219 public synchronized void invalidate() { 220 state = PooledObjectState.INVALID; 221 } 222 223 /** 224 * Marks the pooled object as abandoned. 225 */ 226 @Override 227 public synchronized void markAbandoned() { 228 state = PooledObjectState.ABANDONED; 229 } 230 231 /** 232 * Marks the object as returning to the pool. 233 */ 234 @Override 235 public synchronized void markReturning() { 236 state = PooledObjectState.RETURNING; 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 @Override 249 public void setLogAbandoned(final boolean logAbandoned) { 250 this.logAbandoned = logAbandoned; 251 } 252 253 /** 254 * Configures the stack trace generation strategy based on whether or not fully 255 * detailed stack traces are required. When set to false, abandoned logs may 256 * only include caller class information rather than method names, line numbers, 257 * and other normal metadata available in a full stack trace. 258 * 259 * @param requireFullStackTrace the new configuration setting for abandoned object 260 * logging 261 * @since 2.5 262 */ 263 @Override 264 public void setRequireFullStackTrace(final boolean requireFullStackTrace) { 265 borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + 266 "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", 267 true, requireFullStackTrace); 268 usedBy = CallStackUtils.newCallStack("The last code to use this object was:", 269 false, requireFullStackTrace); 270 } 271 272 @Override 273 public synchronized boolean startEvictionTest() { 274 if (state == PooledObjectState.IDLE) { 275 state = PooledObjectState.EVICTION; 276 return true; 277 } 278 279 return false; 280 } 281 282 @Override 283 public String toString() { 284 final StringBuilder result = new StringBuilder(); 285 result.append("Object: "); 286 result.append(object.toString()); 287 result.append(", State: "); 288 synchronized (this) { 289 result.append(state.toString()); 290 } 291 return result.toString(); 292 // TODO add other attributes 293 } 294 295 @Override 296 public void use() { 297 lastUseTimeMillis = System.currentTimeMillis(); 298 usedBy.fillInStackTrace(); 299 } 300 301}