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: 882113 $ $Date: 2009-11-19 06:35:14 -0500 (Thu, 19 Nov 2009) $
35 */
36 public class AbandonedTrace {
37
38 /** DBCP AbandonedConfig */
39 private AbandonedConfig config = null;
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 trace = 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 init(null);
53 }
54
55 /**
56 * Construct a new AbandonedTrace with no parent object.
57 *
58 * @param config AbandonedConfig
59 */
60 public AbandonedTrace(AbandonedConfig config) {
61 this.config = config;
62 init(null);
63 }
64
65 /**
66 * Construct a new AbandonedTrace with a parent object.
67 *
68 * @param parent AbandonedTrace parent object
69 */
70 public AbandonedTrace(AbandonedTrace parent) {
71 this.config = parent.getConfig();
72 init(parent);
73 }
74
75 /**
76 * Initialize abandoned tracing for this object.
77 *
78 * @param parent AbandonedTrace parent object
79 */
80 private void init(AbandonedTrace parent) {
81 if (parent != null) {
82 parent.addTrace(this);
83 }
84
85 if (config == null) {
86 return;
87 }
88 if (config.getLogAbandoned()) {
89 createdBy = new AbandonedObjectException();
90 }
91 }
92
93 /**
94 * Get the abandoned config for this object.
95 *
96 * @return AbandonedConfig for this object
97 */
98 protected AbandonedConfig getConfig() {
99 return config;
100 }
101
102 /**
103 * Get the last time this object was used in ms.
104 *
105 * @return long time in ms
106 */
107 protected long getLastUsed() {
108 return lastUsed;
109 }
110
111 /**
112 * Set the time this object was last used to the
113 * current time in ms.
114 */
115 protected void setLastUsed() {
116 lastUsed = System.currentTimeMillis();
117 }
118
119 /**
120 * Set the time in ms this object was last used.
121 *
122 * @param time time in ms
123 */
124 protected void setLastUsed(long time) {
125 lastUsed = time;
126 }
127
128 /**
129 * If logAbandoned=true generate a stack trace
130 * for this object then add this object to the parent
131 * object trace list.
132 */
133 protected void setStackTrace() {
134 if (config == null) {
135 return;
136 }
137 if (config.getLogAbandoned()) {
138 createdBy = new AbandonedObjectException();
139 }
140 }
141
142 /**
143 * Add an object to the list of objects being
144 * traced.
145 *
146 * @param trace AbandonedTrace object to add
147 */
148 protected void addTrace(AbandonedTrace trace) {
149 synchronized (this.trace) {
150 this.trace.add(trace);
151 }
152 setLastUsed();
153 }
154
155 /**
156 * Clear the list of objects being traced by this
157 * object.
158 */
159 protected void clearTrace() {
160 synchronized(this.trace) {
161 this.trace.clear();
162 }
163 }
164
165 /**
166 * Get a list of objects being traced by this object.
167 *
168 * @return List of objects
169 */
170 protected List getTrace() {
171 synchronized (this.trace) {
172 return new ArrayList(trace);
173 }
174 }
175
176 /**
177 * Prints a stack trace of the code that
178 * created this object.
179 */
180 public void printStackTrace() {
181 if (createdBy != null && config != null) {
182 createdBy.printStackTrace(config.getLogWriter());
183 }
184 synchronized(this.trace) {
185 Iterator it = this.trace.iterator();
186 while (it.hasNext()) {
187 AbandonedTrace at = (AbandonedTrace)it.next();
188 at.printStackTrace();
189 }
190 }
191 }
192
193 /**
194 * Remove a child object this object is tracing.
195 *
196 * @param trace AbandonedTrace object to remove
197 */
198 protected void removeTrace(AbandonedTrace trace) {
199 synchronized(this.trace) {
200 this.trace.remove(trace);
201 }
202 }
203
204 static class AbandonedObjectException extends Exception {
205
206 /** Date format */
207 private static final SimpleDateFormat format = new SimpleDateFormat
208 ("'DBCP object created' yyyy-MM-dd HH:mm:ss " +
209 "'by the following code was never closed:'");
210
211 private long _createdTime;
212
213 public AbandonedObjectException() {
214 _createdTime = System.currentTimeMillis();
215 }
216
217 // Override getMessage to avoid creating objects and formatting
218 // dates unless the log message will actually be used.
219 public String getMessage() {
220 String msg;
221 synchronized(format) {
222 msg = format.format(new Date(_createdTime));
223 }
224 return msg;
225 }
226 }
227 }