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
18 package org.apache.commons.dbcp;
19
20 import java.text.SimpleDateFormat;
21 import java.util.ArrayList;
22 import java.util.Date;
23 import java.util.Iterator;
24 import java.util.List;
25
26 /**
27 * Tracks db connection usage for recovering and reporting
28 * abandoned db connections.
29 *
30 * The JDBC Connection, Statement, and ResultSet classes
31 * extend this class.
32 *
33 * @author Glenn L. Nielsen
34 * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
35 */
36 public class AbandonedTrace {
37
38 /** DBCP AbandonedConfig */
39 private final AbandonedConfig config;
40 /** A stack trace of the code that created me (if in debug mode) */
41 private volatile Exception createdBy;
42 /** A list of objects created by children of this object */
43 private final List traceList = new ArrayList();
44 /** Last time this connection was used */
45 private volatile long lastUsed = 0;
46
47 /**
48 * Create a new AbandonedTrace without config and
49 * without doing abandoned tracing.
50 */
51 public AbandonedTrace() {
52 this.config = null;
53 init(null);
54 }
55
56 /**
57 * Construct a new AbandonedTrace with no parent object.
58 *
59 * @param config AbandonedConfig
60 */
61 public AbandonedTrace(AbandonedConfig config) {
62 this.config = config;
63 init(null);
64 }
65
66 /**
67 * Construct a new AbandonedTrace with a parent object.
68 *
69 * @param parent AbandonedTrace parent object
70 */
71 public AbandonedTrace(AbandonedTrace parent) {
72 this.config = parent.getConfig();
73 init(parent);
74 }
75
76 /**
77 * Initialize abandoned tracing for this object.
78 *
79 * @param parent AbandonedTrace parent object
80 */
81 private void init(AbandonedTrace parent) {
82 if (parent != null) {
83 parent.addTrace(this);
84 }
85
86 if (config == null) {
87 return;
88 }
89 if (config.getLogAbandoned()) {
90 createdBy = new AbandonedObjectException();
91 }
92 }
93
94 /**
95 * Get the abandoned config for this object.
96 *
97 * @return AbandonedConfig for this object
98 */
99 protected AbandonedConfig getConfig() {
100 return config;
101 }
102
103 /**
104 * Get the last time this object was used in ms.
105 *
106 * @return long time in ms
107 */
108 protected long getLastUsed() {
109 return lastUsed;
110 }
111
112 /**
113 * Set the time this object was last used to the
114 * current time in ms.
115 */
116 protected void setLastUsed() {
117 lastUsed = System.currentTimeMillis();
118 }
119
120 /**
121 * Set the time in ms this object was last used.
122 *
123 * @param time time in ms
124 */
125 protected void setLastUsed(long time) {
126 lastUsed = time;
127 }
128
129 /**
130 * If logAbandoned=true generate a stack trace
131 * for this object then add this object to the parent
132 * object trace list.
133 */
134 protected void setStackTrace() {
135 if (config == null) {
136 return;
137 }
138 if (config.getLogAbandoned()) {
139 createdBy = new AbandonedObjectException();
140 }
141 }
142
143 /**
144 * Add an object to the list of objects being
145 * traced.
146 *
147 * @param trace AbandonedTrace object to add
148 */
149 protected void addTrace(AbandonedTrace trace) {
150 synchronized (this.traceList) {
151 this.traceList.add(trace);
152 }
153 setLastUsed();
154 }
155
156 /**
157 * Clear the list of objects being traced by this
158 * object.
159 */
160 protected void clearTrace() {
161 synchronized(this.traceList) {
162 this.traceList.clear();
163 }
164 }
165
166 /**
167 * Get a list of objects being traced by this object.
168 *
169 * @return List of objects
170 */
171 protected List getTrace() {
172 synchronized (this.traceList) {
173 return new ArrayList(traceList);
174 }
175 }
176
177 /**
178 * Prints a stack trace of the code that
179 * created this object.
180 */
181 public void printStackTrace() {
182 if (createdBy != null && config != null) {
183 createdBy.printStackTrace(config.getLogWriter());
184 }
185 synchronized(this.traceList) {
186 Iterator it = this.traceList.iterator();
187 while (it.hasNext()) {
188 AbandonedTrace at = (AbandonedTrace)it.next();
189 at.printStackTrace();
190 }
191 }
192 }
193
194 /**
195 * Remove a child object this object is tracing.
196 *
197 * @param trace AbandonedTrace object to remove
198 */
199 protected void removeTrace(AbandonedTrace trace) {
200 synchronized(this.traceList) {
201 this.traceList.remove(trace);
202 }
203 }
204
205 static class AbandonedObjectException extends Exception {
206
207 private static final long serialVersionUID = 7398692158058772916L;
208
209 /** Date format */
210 //@GuardedBy("this")
211 private static final SimpleDateFormat format = new SimpleDateFormat
212 ("'DBCP object created' yyyy-MM-dd HH:mm:ss " +
213 "'by the following code was never closed:'");
214
215 private final long _createdTime;
216
217 public AbandonedObjectException() {
218 _createdTime = System.currentTimeMillis();
219 }
220
221 // Override getMessage to avoid creating objects and formatting
222 // dates unless the log message will actually be used.
223 public String getMessage() {
224 String msg;
225 synchronized(format) {
226 msg = format.format(new Date(_createdTime));
227 }
228 return msg;
229 }
230 }
231 }