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     * 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    @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     * Gets 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    @Override
107    public long getBorrowedCount() {
108        return borrowedCount;
109    }
110
111    /**
112     * Returns an estimate of the last time this object was used.  If the class
113     * of the pooled object implements {@link TrackedUse}, what is returned is
114     * the maximum of {@link TrackedUse#getLastUsed()} and
115     * {@link #getLastBorrowTime()}; otherwise this method gives the same
116     * value as {@link #getLastBorrowTime()}.
117     *
118     * @return the last time this object was used
119     */
120    @Override
121    public long getLastUsedTime() {
122        if (object instanceof TrackedUse) {
123            return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime);
124        }
125        return lastUseTime;
126    }
127
128    @Override
129    public int compareTo(final PooledObject<T> other) {
130        final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime();
131        if (lastActiveDiff == 0) {
132            // Make sure the natural ordering is broadly consistent with equals
133            // although this will break down if distinct objects have the same
134            // identity hash code.
135            // see java.lang.Comparable Javadocs
136            return System.identityHashCode(this) - System.identityHashCode(other);
137        }
138        // handle int overflow
139        return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
140    }
141
142    @Override
143    public String toString() {
144        final StringBuilder result = new StringBuilder();
145        result.append("Object: ");
146        result.append(object.toString());
147        result.append(", State: ");
148        synchronized (this) {
149            result.append(state.toString());
150        }
151        return result.toString();
152        // TODO add other attributes
153    }
154
155    @Override
156    public synchronized boolean startEvictionTest() {
157        if (state == PooledObjectState.IDLE) {
158            state = PooledObjectState.EVICTION;
159            return true;
160        }
161
162        return false;
163    }
164
165    @Override
166    public synchronized boolean endEvictionTest(
167            final Deque<PooledObject<T>> idleQueue) {
168        if (state == PooledObjectState.EVICTION) {
169            state = PooledObjectState.IDLE;
170            return true;
171        } else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
172            state = PooledObjectState.IDLE;
173            if (!idleQueue.offerFirst(this)) {
174                // TODO - Should never happen
175            }
176        }
177
178        return false;
179    }
180
181    /**
182     * Allocates the object.
183     *
184     * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
185     */
186    @Override
187    public synchronized boolean allocate() {
188        if (state == PooledObjectState.IDLE) {
189            state = PooledObjectState.ALLOCATED;
190            lastBorrowTime = System.currentTimeMillis();
191            lastUseTime = lastBorrowTime;
192            borrowedCount++;
193            if (logAbandoned) {
194                borrowedBy.fillInStackTrace();
195            }
196            return true;
197        } else if (state == PooledObjectState.EVICTION) {
198            // TODO Allocate anyway and ignore eviction test
199            state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
200            return false;
201        }
202        // TODO if validating and testOnBorrow == true then pre-allocate for
203        // performance
204        return false;
205    }
206
207    /**
208     * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE}
209     * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}
210     * or {@link PooledObjectState#RETURNING RETURNING}.
211     *
212     * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
213     *         or {@link PooledObjectState#RETURNING RETURNING}.
214     */
215    @Override
216    public synchronized boolean deallocate() {
217        if (state == PooledObjectState.ALLOCATED ||
218                state == PooledObjectState.RETURNING) {
219            state = PooledObjectState.IDLE;
220            lastReturnTime = System.currentTimeMillis();
221            borrowedBy.clear();
222            return true;
223        }
224
225        return false;
226    }
227
228    /**
229     * Sets the state to {@link PooledObjectState#INVALID INVALID}
230     */
231    @Override
232    public synchronized void invalidate() {
233        state = PooledObjectState.INVALID;
234    }
235
236    @Override
237    public void use() {
238        lastUseTime = System.currentTimeMillis();
239        usedBy.fillInStackTrace();
240    }
241
242    @Override
243    public void printStackTrace(final PrintWriter writer) {
244        boolean written = borrowedBy.printStackTrace(writer);
245        written |= usedBy.printStackTrace(writer);
246        if (written) {
247            writer.flush();
248        }
249    }
250
251    /**
252     * Returns the state of this object.
253     * @return state
254     */
255    @Override
256    public synchronized PooledObjectState getState() {
257        return state;
258    }
259
260    /**
261     * Marks the pooled object as abandoned.
262     */
263    @Override
264    public synchronized void markAbandoned() {
265        state = PooledObjectState.ABANDONED;
266    }
267
268    /**
269     * Marks the object as returning to the pool.
270     */
271    @Override
272    public synchronized void markReturning() {
273        state = PooledObjectState.RETURNING;
274    }
275
276    @Override
277    public void setLogAbandoned(final boolean logAbandoned) {
278        this.logAbandoned = logAbandoned;
279    }
280
281    /**
282     * Configures the stack trace generation strategy based on whether or not fully
283     * detailed stack traces are required. When set to false, abandoned logs may
284     * only include caller class information rather than method names, line numbers,
285     * and other normal metadata available in a full stack trace.
286     *
287     * @param requireFullStackTrace the new configuration setting for abandoned object
288     *                              logging
289     * @since 2.5
290     */
291    @Override
292    public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
293        borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
294            "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
295            true, requireFullStackTrace);
296        usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
297            false, requireFullStackTrace);
298    }
299
300}