    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 AbandonedTrace trace = iter.next().get();
    137                if (trace == null) {
    138                    // Clean-up since we are here anyway
    139                    iter.remove();
    140                } else {
    141                    result.add(trace);
    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 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}