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