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