View Javadoc
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         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 }