1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.dbcp2;
18
19 import java.lang.ref.WeakReference;
20 import java.sql.SQLException;
21 import java.time.Instant;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.function.Consumer;
27
28 import org.apache.commons.pool2.TrackedUse;
29
30 /**
31 * Tracks connection usage for recovering and reporting abandoned connections.
32 * <p>
33 * The JDBC Connection, Statement, and ResultSet classes extend this class.
34 * </p>
35 *
36 * @since 2.0
37 */
38 public class AbandonedTrace implements TrackedUse, AutoCloseable {
39
40 static void add(final AbandonedTrace receiver, final AbandonedTrace trace) {
41 if (receiver != null) {
42 receiver.addTrace(trace);
43 }
44 }
45
46 /** A list of objects created by children of this object. */
47 private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>();
48
49 /** Last time this connection was used. */
50 private volatile Instant lastUsedInstant = Instant.EPOCH;
51
52 /**
53 * Creates a new AbandonedTrace without config and without doing abandoned tracing.
54 */
55 public AbandonedTrace() {
56 init(null);
57 }
58
59 /**
60 * Constructs a new AbandonedTrace with a parent object.
61 *
62 * @param parent
63 * AbandonedTrace parent object.
64 */
65 public AbandonedTrace(final AbandonedTrace parent) {
66 init(parent);
67 }
68
69 /**
70 * Adds an object to the list of objects being traced.
71 *
72 * @param trace
73 * AbandonedTrace object to add.
74 */
75 protected void addTrace(final AbandonedTrace trace) {
76 synchronized (this.traceList) {
77 this.traceList.add(new WeakReference<>(trace));
78 }
79 setLastUsed();
80 }
81
82 /**
83 * Clears the list of objects being traced by this object.
84 */
85 protected void clearTrace() {
86 synchronized (this.traceList) {
87 this.traceList.clear();
88 }
89 }
90
91 /**
92 * Subclasses can implement this nop.
93 *
94 * @throws SQLException Ignored here, for subclasses.
95 * @since 2.10.0
96 */
97 @Override
98 public void close() throws SQLException {
99 // 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 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 }