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.dbcp2;
018
019import java.lang.ref.WeakReference;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024
025import org.apache.commons.pool2.TrackedUse;
026
027/**
028 * Tracks db connection usage for recovering and reporting abandoned db connections.
029 * <p>
030 * The JDBC Connection, Statement, and ResultSet classes extend this class.
031 * </p>
032 *
033 * @since 2.0
034 */
035public class AbandonedTrace implements TrackedUse {
036
037    /** A list of objects created by children of this object. */
038    private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>();
039
040    /** Last time this connection was used. */
041    private volatile long lastUsedMillis = 0;
042
043    /**
044     * Creates a new AbandonedTrace without config and without doing abandoned tracing.
045     */
046    public AbandonedTrace() {
047        init(null);
048    }
049
050    /**
051     * Constructs a new AbandonedTrace with a parent object.
052     *
053     * @param parent
054     *            AbandonedTrace parent object.
055     */
056    public AbandonedTrace(final AbandonedTrace parent) {
057        init(parent);
058    }
059
060    /**
061     * Initializes abandoned tracing for this object.
062     *
063     * @param parent
064     *            AbandonedTrace parent object.
065     */
066    private void init(final AbandonedTrace parent) {
067        if (parent != null) {
068            parent.addTrace(this);
069        }
070    }
071
072    /**
073     * Gets the last time this object was used in milliseconds.
074     *
075     * @return long time in milliseconds.
076     */
077    @Override
078    public long getLastUsed() {
079        return lastUsedMillis;
080    }
081
082    /**
083     * Sets the time this object was last used to the current time in milliseconds.
084     */
085    protected void setLastUsed() {
086        lastUsedMillis = System.currentTimeMillis();
087    }
088
089    /**
090     * Sets the time in milliseconds this object was last used.
091     *
092     * @param lastUsedMillis
093     *            time in milliseconds.
094     */
095    protected void setLastUsed(final long lastUsedMillis) {
096        this.lastUsedMillis = lastUsedMillis;
097    }
098
099    /**
100     * Adds an object to the list of objects being traced.
101     *
102     * @param trace
103     *            AbandonedTrace object to add.
104     */
105    protected void addTrace(final AbandonedTrace trace) {
106        synchronized (this.traceList) {
107            this.traceList.add(new WeakReference<>(trace));
108        }
109        setLastUsed();
110    }
111
112    /**
113     * Clears the list of objects being traced by this object.
114     */
115    protected void clearTrace() {
116        synchronized (this.traceList) {
117            this.traceList.clear();
118        }
119    }
120
121    /**
122     * Gets a list of objects being traced by this object.
123     *
124     * @return List of objects.
125     */
126    protected List<AbandonedTrace> getTrace() {
127        final int size = traceList.size();
128        if (size == 0) {
129            return Collections.emptyList();
130        }
131        final ArrayList<AbandonedTrace> result = new ArrayList<>(size);
132        synchronized (this.traceList) {
133            final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
134            while (iter.hasNext()) {
135                final AbandonedTrace trace = iter.next().get();
136                if (trace == null) {
137                    // Clean-up since we are here anyway
138                    iter.remove();
139                } else {
140                    result.add(trace);
141                }
142            }
143        }
144        return result;
145    }
146
147    /**
148     * Removes a child object this object is tracing.
149     *
150     * @param trace
151     *            AbandonedTrace object to remove.
152     */
153    protected void removeTrace(final AbandonedTrace trace) {
154        synchronized (this.traceList) {
155            final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
156            while (iter.hasNext()) {
157                final AbandonedTrace traceInList = iter.next().get();
158                if (trace.equals(traceInList)) {
159                    iter.remove();
160                    break;
161                } else if (traceInList == null) {
162                    // Clean-up since we are here anyway
163                    iter.remove();
164                }
165            }
166        }
167    }
168}