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
029 * abandoned db connections.
030 *
031 * The JDBC Connection, Statement, and ResultSet classes
032 * extend this class.
033 *
034 * @author Glenn L. Nielsen
035 * @since 2.0
036 */
037public class AbandonedTrace implements TrackedUse {
038
039    /** A list of objects created by children of this object */
040    private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>();
041    /** Last time this connection was used */
042    private volatile long lastUsed = 0;
043
044    /**
045     * Create a new AbandonedTrace without config and
046     * without doing abandoned tracing.
047     */
048    public AbandonedTrace() {
049        init(null);
050    }
051
052    /**
053     * Construct a new AbandonedTrace with a parent object.
054     *
055     * @param parent AbandonedTrace parent object
056     */
057    public AbandonedTrace(final AbandonedTrace parent) {
058        init(parent);
059    }
060
061    /**
062     * Initialize abandoned tracing for this object.
063     *
064     * @param parent AbandonedTrace parent object
065     */
066    private void init(final AbandonedTrace parent) {
067        if (parent != null) {
068            parent.addTrace(this);
069        }
070    }
071
072    /**
073     * Get the last time this object was used in ms.
074     *
075     * @return long time in ms
076     */
077    @Override
078    public long getLastUsed() {
079        return lastUsed;
080    }
081
082    /**
083     * Set the time this object was last used to the
084     * current time in ms.
085     */
086    protected void setLastUsed() {
087        lastUsed = System.currentTimeMillis();
088    }
089
090    /**
091     * Set the time in ms this object was last used.
092     *
093     * @param time time in ms
094     */
095    protected void setLastUsed(final long time) {
096        lastUsed = time;
097    }
098
099    /**
100     * Add an object to the list of objects being
101     * traced.
102     *
103     * @param trace 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     * Clear the list of objects being traced by this
114     * object.
115     */
116    protected void clearTrace() {
117        synchronized(this.traceList) {
118            this.traceList.clear();
119        }
120    }
121
122    /**
123     * Get a list of objects being traced by this object.
124     *
125     * @return List of objects
126     */
127    protected List<AbandonedTrace> getTrace() {
128        final int size = traceList.size();
129        if (size == 0) {
130            return Collections.emptyList();
131        }
132        final ArrayList<AbandonedTrace> result = new ArrayList<>(size);
133        synchronized (this.traceList) {
134            final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
135            while (iter.hasNext()) {
136                final WeakReference<AbandonedTrace> ref = iter.next();
137                if (ref.get() == null) {
138                    // Clean-up since we are here anyway
139                    iter.remove();
140                } else {
141                    result.add(ref.get());
142                }
143            }
144        }
145        return result;
146    }
147
148    /**
149     * Remove a child object this object is tracing.
150     *
151     * @param trace 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 WeakReference<AbandonedTrace> ref = iter.next();
158                if (trace.equals(ref.get())) {
159                    iter.remove();
160                    break;
161                } else if (ref.get() == null) {
162                    // Clean-up since we are here anyway
163                    iter.remove();
164                }
165            }
166        }
167    }
168}