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.util.ArrayList;
021 import java.util.Iterator;
022 import java.util.List;
023
024 import org.apache.commons.pool.PoolableObjectFactory;
025 import org.apache.commons.pool.impl.GenericObjectPool;
026
027 /**
028 * <p>An implementation of a Jakarta-Commons ObjectPool which
029 * tracks JDBC connections and can recover abandoned db connections.
030 * If logAbandoned=true, a stack trace will be printed for any
031 * abandoned db connections recovered.
032 *
033 * @author Glenn L. Nielsen
034 * @version $Revision: 899987 $ $Date: 2010-01-16 11:51:16 -0500 (Sat, 16 Jan 2010) $
035 */
036 public class AbandonedObjectPool extends GenericObjectPool {
037
038 /**
039 * DBCP AbandonedConfig
040 */
041 private final AbandonedConfig config;
042
043 /**
044 * A list of connections in use
045 */
046 private final List trace = new ArrayList();
047
048 /**
049 * Create an ObjectPool which tracks db connections.
050 *
051 * @param factory PoolableObjectFactory used to create this
052 * @param config configuration for abandoned db connections
053 */
054 public AbandonedObjectPool(PoolableObjectFactory factory,
055 AbandonedConfig config) {
056 super(factory);
057 this.config = config;
058 }
059
060 /**
061 * Get a db connection from the pool.
062 *
063 * If removeAbandoned=true, recovers db connections which
064 * have been idle > removeAbandonedTimeout and
065 * getNumActive() > getMaxActive() - 3 and
066 * getNumIdle() < 2
067 *
068 * @return Object jdbc Connection
069 * @throws Exception if an exception occurs retrieving a
070 * connection from the pool
071 */
072 public Object borrowObject() throws Exception {
073 if (config != null
074 && config.getRemoveAbandoned()
075 && (getNumIdle() < 2)
076 && (getNumActive() > getMaxActive() - 3) ) {
077 removeAbandoned();
078 }
079 Object obj = super.borrowObject();
080 if (obj instanceof AbandonedTrace) {
081 ((AbandonedTrace) obj).setStackTrace();
082 }
083 if (obj != null && config != null && config.getRemoveAbandoned()) {
084 synchronized (trace) {
085 trace.add(obj);
086 }
087 }
088 return obj;
089 }
090
091 /**
092 * Return a db connection to the pool.
093 *
094 * @param obj db Connection to return
095 * @throws Exception if an exception occurs returning the connection
096 * to the pool
097 */
098 public void returnObject(Object obj) throws Exception {
099 if (config != null && config.getRemoveAbandoned()) {
100 synchronized (trace) {
101 boolean foundObject = trace.remove(obj);
102 if (!foundObject) {
103 return; // This connection has already been invalidated. Stop now.
104 }
105 }
106 }
107 super.returnObject(obj);
108 }
109
110 /**
111 * Invalidates an object from the pool.
112 *
113 * @param obj object to be returned
114 * @throws Exception if an exception occurs invalidating the object
115 */
116 public void invalidateObject(Object obj) throws Exception {
117 if (config != null && config.getRemoveAbandoned()) {
118 synchronized (trace) {
119 boolean foundObject = trace.remove(obj);
120 if (!foundObject) {
121 return; // This connection has already been invalidated. Stop now.
122 }
123 }
124 }
125 super.invalidateObject(obj);
126 }
127
128 /**
129 * Recover abandoned db connections which have been idle
130 * greater than the removeAbandonedTimeout.
131 */
132 private void removeAbandoned() {
133 // Generate a list of abandoned connections to remove
134 long now = System.currentTimeMillis();
135 long timeout = now - (config.getRemoveAbandonedTimeout() * 1000);
136 ArrayList remove = new ArrayList();
137 synchronized (trace) {
138 Iterator it = trace.iterator();
139 while (it.hasNext()) {
140 AbandonedTrace pc = (AbandonedTrace) it.next();
141 if (pc.getLastUsed() > timeout) {
142 continue;
143 }
144 if (pc.getLastUsed() > 0) {
145 remove.add(pc);
146 }
147 }
148 }
149
150 // Now remove the abandoned connections
151 Iterator it = remove.iterator();
152 while (it.hasNext()) {
153 AbandonedTrace pc = (AbandonedTrace) it.next();
154 if (config.getLogAbandoned()) {
155 pc.printStackTrace();
156 }
157 try {
158 invalidateObject(pc);
159 } catch (Exception e) {
160 e.printStackTrace();
161 }
162
163 }
164 }
165 }
166