| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.dbcp; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.commons.pool.PoolableObjectFactory; |
| import org.apache.commons.pool.impl.GenericObjectPool; |
| |
| /** |
| * <p>An implementation of a Jakarta-Commons ObjectPool which |
| * tracks JDBC connections and can recover abandoned db connections. |
| * If logAbandoned=true, a stack trace will be printed for any |
| * abandoned db connections recovered. |
| * |
| * @author Glenn L. Nielsen |
| * @version $Revision$ $Date$ |
| */ |
| public class AbandonedObjectPool extends GenericObjectPool { |
| |
| /** |
| * DBCP AbandonedConfig |
| */ |
| private final AbandonedConfig config; |
| |
| /** |
| * A list of connections in use |
| */ |
| private final List trace = new ArrayList(); |
| |
| /** |
| * Create an ObjectPool which tracks db connections. |
| * |
| * @param factory PoolableObjectFactory used to create this |
| * @param config configuration for abandoned db connections |
| */ |
| public AbandonedObjectPool(PoolableObjectFactory factory, |
| AbandonedConfig config) { |
| super(factory); |
| this.config = config; |
| } |
| |
| /** |
| * Get a db connection from the pool. |
| * |
| * If removeAbandoned=true, recovers db connections which |
| * have been idle > removeAbandonedTimeout and |
| * getNumActive() > getMaxActive() - 3 and |
| * getNumIdle() < 2 |
| * |
| * @return Object JDBC Connection |
| * @throws Exception if an exception occurs retrieving a |
| * connection from the pool |
| */ |
| public Object borrowObject() throws Exception { |
| if (config != null |
| && config.getRemoveAbandoned() |
| && (getNumIdle() < 2) |
| && (getNumActive() > getMaxActive() - 3) ) { |
| removeAbandoned(); |
| } |
| Object obj = super.borrowObject(); |
| if (obj instanceof AbandonedTrace) { |
| ((AbandonedTrace) obj).setStackTrace(); |
| } |
| if (obj != null && config != null && config.getRemoveAbandoned()) { |
| synchronized (trace) { |
| trace.add(obj); |
| } |
| } |
| return obj; |
| } |
| |
| /** |
| * Return a db connection to the pool. |
| * |
| * @param obj db Connection to return |
| * @throws Exception if an exception occurs returning the connection |
| * to the pool |
| */ |
| public void returnObject(Object obj) throws Exception { |
| if (config != null && config.getRemoveAbandoned()) { |
| synchronized (trace) { |
| boolean foundObject = trace.remove(obj); |
| if (!foundObject) { |
| return; // This connection has already been invalidated. Stop now. |
| } |
| } |
| } |
| super.returnObject(obj); |
| } |
| |
| /** |
| * Invalidates an object from the pool. |
| * |
| * @param obj object to be returned |
| * @throws Exception if an exception occurs invalidating the object |
| */ |
| public void invalidateObject(Object obj) throws Exception { |
| if (config != null && config.getRemoveAbandoned()) { |
| synchronized (trace) { |
| boolean foundObject = trace.remove(obj); |
| if (!foundObject) { |
| return; // This connection has already been invalidated. Stop now. |
| } |
| } |
| } |
| super.invalidateObject(obj); |
| } |
| |
| /** |
| * Recover abandoned db connections which have been idle |
| * greater than the removeAbandonedTimeout. |
| */ |
| private void removeAbandoned() { |
| // Generate a list of abandoned connections to remove |
| long now = System.currentTimeMillis(); |
| long timeout = now - (config.getRemoveAbandonedTimeout() * 1000); |
| ArrayList remove = new ArrayList(); |
| synchronized (trace) { |
| Iterator it = trace.iterator(); |
| while (it.hasNext()) { |
| AbandonedTrace pc = (AbandonedTrace) it.next(); |
| if (pc.getLastUsed() > timeout) { |
| continue; |
| } |
| if (pc.getLastUsed() > 0) { |
| remove.add(pc); |
| } |
| } |
| } |
| |
| // Now remove the abandoned connections |
| Iterator it = remove.iterator(); |
| while (it.hasNext()) { |
| AbandonedTrace pc = (AbandonedTrace) it.next(); |
| if (config.getLogAbandoned()) { |
| pc.printStackTrace(); |
| } |
| try { |
| invalidateObject(pc); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| } |
| } |
| } |
| |