/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/providers/trunk/jldap/src/java/edu/amc/sakai/user/PoolingLdapConnectionManager.java $
* $Id: PoolingLdapConnectionManager.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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 edu.amc.sakai.user;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPException;
/**
* Allocates connected, constrained, bound and optionally secure <code>LDAPConnection</code>s.
* Uses commons-pool to provide a pool of connections instead of creating a new
* connection for each request. Originally tried implementing this with
* <code>om.novell.ldap.connectionpool.PoolManager</code>, but it did not handle
* recovering connections that had suffered a network error or connections that
* were never returned but dropped out of scope.
* @see LdapConnectionManagerConfig
* @see PooledLDAPConnection
* @see PooledLDAPConnectionFactory
* @author John Lewis, Unicon Inc
*/
public class PoolingLdapConnectionManager extends SimpleLdapConnectionManager {
/** Class-specific logger */
private static Log log = LogFactory.getLog(PoolingLdapConnectionManager.class);
/** LDAP connection pool */
private ObjectPool pool;
private PooledLDAPConnectionFactory factory;
/** How long to block waiting for an available connection before throwing an exception */
private static final int POOL_MAX_WAIT = 60000;
/**
* {@inheritDoc}
*/
public void init() {
super.init();
if ( pool != null ) {
return;
}
if ( factory == null ) {
factory = new PooledLDAPConnectionFactory();
}
factory.setConnectionManager(this);
pool = new GenericObjectPool(factory,
getConfig().getPoolMaxConns(), // maxActive
GenericObjectPool.WHEN_EXHAUSTED_BLOCK, // whenExhaustedAction
POOL_MAX_WAIT, // maxWait (millis)
getConfig().getPoolMaxConns(), // maxIdle
true, // testOnBorrow
false // testOnReturn
);
}
/**
* {@inheritDoc}
*/
public LDAPConnection getConnection() throws LDAPException {
if (log.isDebugEnabled()) log.debug("getConnection(): attempting to borrow connection from pool");
try {
LDAPConnection conn = (LDAPConnection)pool.borrowObject();
if (log.isDebugEnabled()) log.debug("getConnection(): successfully to borrowed connection from pool");
return conn;
} catch (Exception e) {
if (e instanceof LDAPException) throw (LDAPException) e;
throw new RuntimeException("failed to get pooled connection", e);
}
}
public LDAPConnection getBoundConnection(String dn, String pw) throws LDAPException {
if (log.isDebugEnabled()) log.debug("getBoundConnection():dn=["+dn+"] attempting to borrow connection from pool and bind to dn");
LDAPConnection conn = null;
try {
conn = (LDAPConnection)pool.borrowObject();
if (log.isDebugEnabled()) log.debug("getBoundConnection():dn=["+dn+"] successfully borrowed connection from pool");
conn.bind(LDAPConnection.LDAP_V3, dn, pw.getBytes("UTF8"));
if (log.isDebugEnabled()) log.debug("getBoundConnection():dn=["+dn+"] successfully bound to dn");
return conn;
} catch (Exception e) {
if ( conn != null ) {
try {
if (log.isDebugEnabled()) log.debug("getBoundConnection():dn=["+dn+"]; error occurred, returning connection to pool");
returnConnection(conn);
} catch ( Exception ee ) {
if (log.isDebugEnabled()) log.debug("getBoundConnection():dn=["+dn+"] failed to return connection to pool", ee);
}
}
if (e instanceof LDAPException) throw (LDAPException) e;
throw new RuntimeException("failed to get pooled connection", e);
}
}
/**
* {@inheritDoc}
*/
public void returnConnection(LDAPConnection conn) {
if ( conn == null ) {
if (log.isDebugEnabled()) log.debug("returnConnection() received null connection; nothing to do");
return;
} else {
if (log.isDebugEnabled()) log.debug("returnConnection(): attempting to return connection to the pool");
}
try {
pool.returnObject(conn);
if (log.isDebugEnabled()) log.debug("returnConnection(): successfully returned connection to pool");
} catch (Exception e) {
throw new RuntimeException("failed to return pooled connection", e);
}
}
/**
* {@inheritDoc}
*/
public void destroy() {
try {
if ( log.isDebugEnabled() ) log.debug("destroy(): closing connection pool");
pool.close();
if ( log.isDebugEnabled() ) log.debug("destroy(): successfully closed connection pool");
} catch (Exception e) {
throw new RuntimeException("failed to shutdown connection pool", e);
}
if ( log.isDebugEnabled() ) log.debug("destroy(): delegating to parent destroy() impl");
super.destroy();
}
public PooledLDAPConnectionFactory getFactory() {
return factory;
}
public void setFactory(PooledLDAPConnectionFactory factory) {
this.factory = factory;
}
/**
* Assign a pool implementation. If not specified, one will
* be constructed by {@link #init()}. If specified,
* {@link #setFactory(PooledLDAPConnectionFactory)} will have
* no effect.
*
* <p>This method exists almost entirely for testing purposes.</p>
*
* @param pool the pool to cache; accepts <code>null</code>
*/
protected void setPool(ObjectPool pool) {
this.pool = pool;
}
}