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