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