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