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