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 */ 017 018 package org.apache.commons.dbcp; 019 020 import java.text.SimpleDateFormat; 021 import java.util.ArrayList; 022 import java.util.Date; 023 import java.util.Iterator; 024 import java.util.List; 025 026 /** 027 * Tracks db connection usage for recovering and reporting 028 * abandoned db connections. 029 * 030 * The JDBC Connection, Statement, and ResultSet classes 031 * extend this class. 032 * 033 * @author Glenn L. Nielsen 034 * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $ 035 */ 036 public class AbandonedTrace { 037 038 /** DBCP AbandonedConfig */ 039 private final AbandonedConfig config; 040 /** A stack trace of the code that created me (if in debug mode) */ 041 private volatile Exception createdBy; 042 /** A list of objects created by children of this object */ 043 private final List traceList = new ArrayList(); 044 /** Last time this connection was used */ 045 private volatile long lastUsed = 0; 046 047 /** 048 * Create a new AbandonedTrace without config and 049 * without doing abandoned tracing. 050 */ 051 public AbandonedTrace() { 052 this.config = null; 053 init(null); 054 } 055 056 /** 057 * Construct a new AbandonedTrace with no parent object. 058 * 059 * @param config AbandonedConfig 060 */ 061 public AbandonedTrace(AbandonedConfig config) { 062 this.config = config; 063 init(null); 064 } 065 066 /** 067 * Construct a new AbandonedTrace with a parent object. 068 * 069 * @param parent AbandonedTrace parent object 070 */ 071 public AbandonedTrace(AbandonedTrace parent) { 072 this.config = parent.getConfig(); 073 init(parent); 074 } 075 076 /** 077 * Initialize abandoned tracing for this object. 078 * 079 * @param parent AbandonedTrace parent object 080 */ 081 private void init(AbandonedTrace parent) { 082 if (parent != null) { 083 parent.addTrace(this); 084 } 085 086 if (config == null) { 087 return; 088 } 089 if (config.getLogAbandoned()) { 090 createdBy = new AbandonedObjectException(); 091 } 092 } 093 094 /** 095 * Get the abandoned config for this object. 096 * 097 * @return AbandonedConfig for this object 098 */ 099 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 }