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.sql.SQLException; 021import java.time.Instant; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.Iterator; 025import java.util.List; 026import java.util.function.Consumer; 027 028import org.apache.commons.pool2.TrackedUse; 029 030/** 031 * Tracks connection usage for recovering and reporting abandoned connections. 032 * <p> 033 * The JDBC Connection, Statement, and ResultSet classes extend this class. 034 * </p> 035 * 036 * @since 2.0 037 */ 038public class AbandonedTrace implements TrackedUse, AutoCloseable { 039 040 static void add(final AbandonedTrace receiver, final AbandonedTrace trace) { 041 if (receiver != null) { 042 receiver.addTrace(trace); 043 } 044 } 045 046 /** A list of objects created by children of this object. */ 047 private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>(); 048 049 /** Last time this connection was used. */ 050 private volatile Instant lastUsedInstant = Instant.EPOCH; 051 052 /** 053 * Creates a new AbandonedTrace without config and without doing abandoned tracing. 054 */ 055 public AbandonedTrace() { 056 init(null); 057 } 058 059 /** 060 * Constructs a new AbandonedTrace with a parent object. 061 * 062 * @param parent 063 * AbandonedTrace parent object. 064 */ 065 public AbandonedTrace(final AbandonedTrace parent) { 066 init(parent); 067 } 068 069 /** 070 * Adds an object to the list of objects being traced. 071 * 072 * @param trace 073 * AbandonedTrace object to add. 074 */ 075 protected void addTrace(final AbandonedTrace trace) { 076 synchronized (this.traceList) { 077 this.traceList.add(new WeakReference<>(trace)); 078 } 079 setLastUsed(); 080 } 081 082 /** 083 * Clears the list of objects being traced by this object. 084 */ 085 protected void clearTrace() { 086 synchronized (this.traceList) { 087 this.traceList.clear(); 088 } 089 } 090 091 /** 092 * Subclasses can implement this nop. 093 * 094 * @throws SQLException Ignored here, for subclasses. 095 * @since 2.10.0 096 */ 097 @Override 098 public void close() throws SQLException { 099 // nop 100 } 101 102 /** 103 * Closes this resource and if an exception is caught, then calls {@code exceptionHandler}. 104 * 105 * @param exceptionHandler Consumes exception thrown closing this resource. 106 * @since 2.10.0 107 */ 108 protected void close(final Consumer<Exception> exceptionHandler) { 109 Utils.close(this, exceptionHandler); 110 } 111 112 /** 113 * Gets the last time this object was used in milliseconds. 114 * 115 * @return long time in milliseconds. 116 */ 117 @Override 118 @Deprecated 119 public long getLastUsed() { 120 return lastUsedInstant.toEpochMilli(); 121 } 122 123 @Override 124 public Instant getLastUsedInstant() { 125 return lastUsedInstant; 126 } 127 128 /** 129 * Gets a list of objects being traced by this object. 130 * 131 * @return List of objects. 132 */ 133 protected List<AbandonedTrace> getTrace() { 134 final int size = traceList.size(); 135 if (size == 0) { 136 return Collections.emptyList(); 137 } 138 final ArrayList<AbandonedTrace> result = new ArrayList<>(size); 139 synchronized (this.traceList) { 140 final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator(); 141 while (iter.hasNext()) { 142 final AbandonedTrace trace = iter.next().get(); 143 if (trace == null) { 144 // Clean-up since we are here anyway 145 iter.remove(); 146 } else { 147 result.add(trace); 148 } 149 } 150 } 151 return result; 152 } 153 154 /** 155 * Initializes abandoned tracing for this object. 156 * 157 * @param parent 158 * AbandonedTrace parent object. 159 */ 160 private void init(final AbandonedTrace parent) { 161 AbandonedTrace.add(parent, this); 162 } 163 164 /** 165 * Removes this object the source object is tracing. 166 * 167 * @param source The object tracing 168 * @since 2.7.0 169 */ 170 protected void removeThisTrace(final Object source) { 171 if (source instanceof AbandonedTrace) { 172 AbandonedTrace.class.cast(source).removeTrace(this); 173 } 174 } 175 176 /** 177 * Removes a child object this object is tracing. 178 * 179 * @param trace 180 * AbandonedTrace object to remove. 181 */ 182 protected void removeTrace(final AbandonedTrace trace) { 183 synchronized (this.traceList) { 184 final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator(); 185 while (iter.hasNext()) { 186 final AbandonedTrace traceInList = iter.next().get(); 187 if (trace != null && trace.equals(traceInList)) { 188 iter.remove(); 189 break; 190 } 191 if (traceInList == null) { 192 // Clean-up since we are here anyway 193 iter.remove(); 194 } 195 } 196 } 197 } 198 199 /** 200 * Sets the time this object was last used to the current time in milliseconds. 201 */ 202 protected void setLastUsed() { 203 lastUsedInstant = Instant.now(); 204 } 205 206 /** 207 * Sets the instant this object was last used. 208 * 209 * @param lastUsedInstant 210 * instant. 211 * @since 2.10.0 212 */ 213 protected void setLastUsed(final Instant lastUsedInstant) { 214 this.lastUsedInstant = lastUsedInstant; 215 } 216 217 /** 218 * Sets the time in milliseconds this object was last used. 219 * 220 * @param lastUsedMillis 221 * time in milliseconds. 222 * @deprecated Use {@link #setLastUsed(Instant)} 223 */ 224 @Deprecated 225 protected void setLastUsed(final long lastUsedMillis) { 226 this.lastUsedInstant = Instant.ofEpochMilli(lastUsedMillis); 227 } 228}