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