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.Duration;
022import java.time.Instant;
023import java.util.Deque;
024
025import org.apache.commons.pool2.PooledObject;
026import org.apache.commons.pool2.PooledObjectState;
027import org.apache.commons.pool2.TrackedUse;
028
029/**
030 * This wrapper is used to track the additional information, such as state, for
031 * the pooled objects.
032 * <p>
033 * This class is intended to be thread-safe.
034 * </p>
035 *
036 * @param <T> the type of object in the pool
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            idleQueue.offerFirst(this);
133        }
134
135        return false;
136    }
137
138    @Override
139    public long getActiveTimeMillis() {
140        return getActiveDuration().toMillis();
141    }
142
143    /**
144     * Gets the number of times this object has been borrowed.
145     * @return The number of times this object has been borrowed.
146     * @since 2.1
147     */
148    @Override
149    public long getBorrowedCount() {
150        return borrowedCount;
151    }
152
153    @Override
154    public Instant getCreateInstant() {
155        return createInstant;
156    }
157
158    @Override
159    public long getCreateTime() {
160        return createInstant.toEpochMilli();
161    }
162
163    @Override
164    public Duration getIdleDuration() {
165        // elapsed may be negative if:
166        // - another thread updates lastReturnInstant during the calculation window
167        // - System.currentTimeMillis() is not monotonic (e.g. system time is set back)
168        final Duration elapsed = Duration.between(lastReturnInstant, now());
169        return elapsed.isNegative() ? Duration.ZERO : elapsed;
170    }
171
172    @Override
173    public Duration getIdleTime() {
174        return getIdleDuration();
175    }
176
177    @Override
178    public long getIdleTimeMillis() {
179        return getIdleDuration().toMillis();
180    }
181
182    @Override
183    public Instant getLastBorrowInstant() {
184        return lastBorrowInstant;
185    }
186
187    @Override
188    public long getLastBorrowTime() {
189        return lastBorrowInstant.toEpochMilli();
190    }
191
192    @Override
193    public Instant getLastReturnInstant() {
194        return lastReturnInstant;
195    }
196
197    @Override
198    public long getLastReturnTime() {
199        return lastReturnInstant.toEpochMilli();
200    }
201
202    /**
203     * Gets an estimate of the last time this object was used.  If the class
204     * of the pooled object implements {@link TrackedUse}, what is returned is
205     * the maximum of {@link TrackedUse#getLastUsedInstant()} and
206     * {@link #getLastBorrowTime()}; otherwise this method gives the same
207     * value as {@link #getLastBorrowTime()}.
208     *
209     * @return the last Instant this object was used.
210     */
211    @Override
212    public Instant getLastUsedInstant() {
213        if (object instanceof TrackedUse) {
214            return PoolImplUtils.max(((TrackedUse) object).getLastUsedInstant(), lastUseInstant);
215        }
216        return lastUseInstant;
217    }
218
219    /**
220     * Gets an estimate of the last time this object was used.  If the class
221     * of the pooled object implements {@link TrackedUse}, what is returned is
222     * the maximum of {@link TrackedUse#getLastUsedInstant()} and
223     * {@link #getLastBorrowTime()}; otherwise this method gives the same
224     * value as {@link #getLastBorrowTime()}.
225     *
226     * @return the last time this object was used
227     */
228    @Override
229    public long getLastUsedTime() {
230        return getLastUsedInstant().toEpochMilli();
231    }
232
233    @Override
234    public T getObject() {
235        return object;
236    }
237
238    /**
239     * Gets the state of this object.
240     * @return state
241     */
242    @Override
243    public synchronized PooledObjectState getState() {
244        return state;
245    }
246
247    /**
248     * Sets the state to {@link PooledObjectState#INVALID INVALID}.
249     */
250    @Override
251    public synchronized void invalidate() {
252        state = PooledObjectState.INVALID;
253    }
254
255    /**
256     * Marks the pooled object as {@link PooledObjectState#ABANDONED ABANDONED}.
257     */
258    @Override
259    public synchronized void markAbandoned() {
260        state = PooledObjectState.ABANDONED;
261    }
262
263    /**
264     * Marks the pooled object as {@link PooledObjectState#RETURNING RETURNING}.
265     */
266    @Override
267    public synchronized void markReturning() {
268        state = PooledObjectState.RETURNING;
269    }
270
271    /**
272     * Gets the current instant of the clock.
273     *
274     * @return the current instant of the clock.
275     */
276    private Instant now() {
277        return systemClock.instant();
278    }
279
280    @Override
281    public void printStackTrace(final PrintWriter writer) {
282        boolean written = borrowedBy.printStackTrace(writer);
283        written |= usedBy.printStackTrace(writer);
284        if (written) {
285            writer.flush();
286        }
287    }
288
289    @Override
290    public void setLogAbandoned(final boolean logAbandoned) {
291        this.logAbandoned = logAbandoned;
292    }
293
294    /**
295     * Configures the stack trace generation strategy based on whether or not fully
296     * detailed stack traces are required. When set to false, abandoned logs may
297     * only include caller class information rather than method names, line numbers,
298     * and other normal metadata available in a full stack trace.
299     *
300     * @param requireFullStackTrace the new configuration setting for abandoned object
301     *                              logging
302     * @since 2.5
303     */
304    @Override
305    public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
306        borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
307            "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
308            true, requireFullStackTrace);
309        usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
310            false, requireFullStackTrace);
311    }
312
313    @Override
314    public synchronized boolean startEvictionTest() {
315        if (state == PooledObjectState.IDLE) {
316            state = PooledObjectState.EVICTION;
317            return true;
318        }
319        return false;
320    }
321
322    @Override
323    public String toString() {
324        final StringBuilder result = new StringBuilder();
325        result.append("Object: ");
326        result.append(object.toString());
327        result.append(", State: ");
328        synchronized (this) {
329            result.append(state.toString());
330        }
331        return result.toString();
332        // TODO add other attributes
333    }
334
335    @Override
336    public void use() {
337        lastUseInstant = now();
338        usedBy.fillInStackTrace();
339    }
340
341}