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