Java tutorial
package org.apache.ojb.broker.accesslayer; /* Copyright 2002-2005 The Apache Software Foundation * * Licensed 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. */ import org.apache.commons.pool.BasePoolableObjectFactory; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.PoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; import org.apache.ojb.broker.OJBRuntimeException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.PreparedStatement; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; /** * Connection factory which pools the requested * connections for different JdbcConnectionDescriptors * using Commons Pool API. * * @version $Id: ConnectionFactoryPooledImpl.java,v 1.15.2.9 2005/10/27 14:54:50 arminw Exp $ * @see <a href="http://jakarta.apache.org/commons/pool/">Commons Pool Website</a> */ public class ConnectionFactoryPooledImpl extends ConnectionFactoryAbstractImpl { private Logger log = LoggerFactory.getLogger(ConnectionFactoryPooledImpl.class); /** Key=PBKey, value=ObjectPool. */ private Map poolMap = new HashMap(); /** Synchronize object for operations not synchronized on Map only. */ private final Object poolSynch = new Object(); public void releaseJdbcConnection(JdbcConnectionDescriptor jcd, Connection con) throws LookupException { final ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey()); try { /* mkalen: NB - according to the Commons Pool API we should _not_ perform * any additional checks here since we will then break testOnX semantics * * To enable Connection validation on releaseJdbcConnection, * set a validation query and specify testOnRelease=true * * Destruction of pooled objects is performed by the actual Commons Pool * ObjectPool implementation when the object factory's validateObject method * returns false. See ConPoolFactory#validateObject. */ op.returnObject(con); } catch (Exception e) { throw new LookupException(e); } } public Connection checkOutJdbcConnection(JdbcConnectionDescriptor jcd) throws LookupException { ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey()); if (op == null) { synchronized (poolSynch) { log.info("Create new connection pool:" + jcd); op = createConnectionPool(jcd); poolMap.put(jcd.getPBKey(), op); } } final Connection conn; try { conn = (Connection) op.borrowObject(); } catch (NoSuchElementException e) { int active = 0; int idle = 0; try { active = op.getNumActive(); idle = op.getNumIdle(); } catch (Exception ignore) { } throw new LookupException("Could not borrow connection from pool, seems ObjectPool is exhausted." + " Active/Idle instances in pool=" + active + "/" + idle + ". " + JdbcConnectionDescriptor.class.getName() + ": " + jcd, e); } catch (Exception e) { int active = 0; int idle = 0; try { active = op.getNumActive(); idle = op.getNumIdle(); } catch (Exception ignore) { } throw new LookupException("Could not borrow connection from pool." + " Active/Idle instances in pool=" + active + "/" + idle + ". " + JdbcConnectionDescriptor.class.getName() + ": " + jcd, e); } return conn; } /** * Create the pool for pooling the connections of the given connection descriptor. * Override this method to implement your on {@link org.apache.commons.pool.ObjectPool}. */ public ObjectPool createConnectionPool(JdbcConnectionDescriptor jcd) { if (log.isDebugEnabled()) log.debug("createPool was called"); PoolableObjectFactory pof = new ConPoolFactory(this, jcd); GenericObjectPool.Config conf = jcd.getConnectionPoolDescriptor().getObjectPoolConfig(); return (ObjectPool) new GenericObjectPool(pof, conf); } /** * Closes all managed pools. */ public void releaseAllResources() { synchronized (poolSynch) { Collection pools = poolMap.values(); poolMap = new HashMap(poolMap.size()); ObjectPool op = null; for (Iterator iterator = pools.iterator(); iterator.hasNext();) { try { op = ((ObjectPool) iterator.next()); op.close(); } catch (Exception e) { log.error("Exception occured while closing pool " + op, e); } } } super.releaseAllResources(); } //************************************************************************************** // Inner classes //************************************************************************************ /** * Inner class - {@link org.apache.commons.pool.PoolableObjectFactory} * used as factory for connection pooling. */ class ConPoolFactory extends BasePoolableObjectFactory { final private JdbcConnectionDescriptor jcd; final private ConnectionFactoryPooledImpl cf; private int failedValidationQuery; public ConPoolFactory(ConnectionFactoryPooledImpl cf, JdbcConnectionDescriptor jcd) { this.cf = cf; this.jcd = jcd; } public boolean validateObject(Object obj) { boolean isValid = false; if (obj != null) { final Connection con = (Connection) obj; try { isValid = !con.isClosed(); } catch (SQLException e) { log.warn("Connection validation failed: " + e.getMessage()); if (log.isDebugEnabled()) log.debug(e); isValid = false; } if (isValid) { final String validationQuery; validationQuery = jcd.getConnectionPoolDescriptor().getValidationQuery(); if (validationQuery != null) { isValid = validateConnection(con, validationQuery); } } } return isValid; } private boolean validateConnection(Connection conn, String query) { PreparedStatement stmt = null; ResultSet rset = null; boolean isValid = false; if (failedValidationQuery > 100) { --failedValidationQuery; throw new OJBRuntimeException("Validation of connection " + conn + " using validation query '" + query + "' failed more than 100 times."); } try { stmt = conn.prepareStatement(query); stmt.setMaxRows(1); stmt.setFetchSize(1); rset = stmt.executeQuery(); if (rset.next()) { failedValidationQuery = 0; isValid = true; } else { ++failedValidationQuery; log.warn("Validation query '" + query + "' result set does not match, discard connection"); isValid = false; } } catch (SQLException e) { ++failedValidationQuery; log.warn("Validation query for connection failed, discard connection. Query was '" + query + "', Message was " + e.getMessage()); if (log.isDebugEnabled()) log.debug(e); } finally { try { if (rset != null) rset.close(); } catch (SQLException t) { if (log.isDebugEnabled()) log.debug("ResultSet already closed.", t); } try { if (stmt != null) stmt.close(); } catch (SQLException t) { if (log.isDebugEnabled()) log.debug("Statement already closed.", t); } } return isValid; } public Object makeObject() throws Exception { if (log.isDebugEnabled()) log.debug("makeObject called"); return cf.newConnectionFromDriverManager(jcd); } public void destroyObject(Object obj) throws Exception { log.info("Destroy object was called, try to close connection: " + obj); try { ((Connection) obj).close(); } catch (SQLException ignore) { //ignore it } } } }