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        return System.currentTimeMillis() - lastReturnTime;
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        } else {
124            return lastUseTime;
125        }
126    }
127
128    @Override
129    public int compareTo(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        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            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 = new AbandonedObjectCreatedException();
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     *
211     * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
212     */
213    @Override
214    public synchronized boolean deallocate() {
215        if (state == PooledObjectState.ALLOCATED ||
216                state == PooledObjectState.RETURNING) {
217            state = PooledObjectState.IDLE;
218            lastReturnTime = System.currentTimeMillis();
219            if (borrowedBy != null) {
220                borrowedBy = null;
221            }
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 = new Exception("The last code to use this object was:");
240    }
241
242    @Override
243    public void printStackTrace(PrintWriter writer) {
244        Exception borrowedByCopy = this.borrowedBy;
245        if (borrowedByCopy != null) {
246            borrowedByCopy.printStackTrace(writer);
247        }
248        Exception usedByCopy = this.usedBy;
249        if (usedByCopy != null) {
250            usedByCopy.printStackTrace(writer);
251        }
252    }
253
254    /**
255     * Returns the state of this object.
256     * @return state
257     */
258    @Override
259    public synchronized PooledObjectState getState() {
260        return state;
261    }
262
263    /**
264     * Marks the pooled object as abandoned.
265     */
266    @Override
267    public synchronized void markAbandoned() {
268        state = PooledObjectState.ABANDONED;
269    }
270
271    /**
272     * Marks the object as returning to the pool.
273     */
274    @Override
275    public synchronized void markReturning() {
276        state = PooledObjectState.RETURNING;
277    }
278
279    @Override
280    public void setLogAbandoned(boolean logAbandoned) {
281        this.logAbandoned = logAbandoned;
282    }
283
284    /**
285     * Used to track how an object was obtained from the pool (the stack trace
286     * of the exception will show which code borrowed the object) and when the
287     * object was borrowed.
288     */
289    static class AbandonedObjectCreatedException extends Exception {
290
291        private static final long serialVersionUID = 7398692158058772916L;
292
293        /** Date format */
294        //@GuardedBy("this")
295        private static final SimpleDateFormat format = new SimpleDateFormat
296            ("'Pooled object created' yyyy-MM-dd HH:mm:ss Z " +
297             "'by the following code has not been returned to the pool:'");
298
299        private final long _createdTime;
300
301        /**
302         * Create a new instance.
303         * <p>
304         * @see Exception#Exception()
305         */
306        public AbandonedObjectCreatedException() {
307            super();
308            _createdTime = System.currentTimeMillis();
309        }
310
311        // Override getMessage to avoid creating objects and formatting
312        // dates unless the log message will actually be used.
313        @Override
314        public String getMessage() {
315            String msg;
316            synchronized(format) {
317                msg = format.format(new Date(_createdTime));
318            }
319            return msg;
320        }
321    }
322}