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}