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}