001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020
021package org.apache.directory.ldap.client.api;
022
023
024import org.apache.commons.pool.PoolableObjectFactory;
025import org.apache.commons.pool.impl.GenericObjectPool;
026import org.apache.directory.api.ldap.codec.api.LdapApiService;
027import org.apache.directory.api.ldap.model.exception.LdapException;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031
032/**
033 * A pool implementation for LdapConnection objects.
034 * 
035 * This class is just a wrapper around the commons GenericObjectPool, and has
036 * a more meaningful name to represent the pool type.
037 * 
038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039 */
040public class LdapConnectionPool extends GenericObjectPool<LdapConnection>
041{
042    private static final Logger LOG = LoggerFactory.getLogger( LdapConnectionPool.class );
043
044    private PoolableObjectFactory<LdapConnection> factory;
045
046
047    /**
048     * Instantiates a new LDAP connection pool.
049     *
050     * @param connectionConfig The connection configuration
051     * @param apiService The api service (codec)
052     * @param timeout The connection timeout in millis
053     */
054    public LdapConnectionPool( LdapConnectionConfig connectionConfig,
055        LdapApiService apiService, long timeout )
056    {
057        this( connectionConfig, apiService, timeout, null );
058    }
059
060
061    /**
062     * Instantiates a new LDAP connection pool.
063     *
064     * @param connectionConfig The connection configuration
065     * @param apiService The api service (codec)
066     * @param timeout The connection timeout in millis
067     * @param poolConfig The pool configuration
068     */
069    public LdapConnectionPool( LdapConnectionConfig connectionConfig,
070        LdapApiService apiService, long timeout, Config poolConfig )
071    {
072        this( newPoolableConnectionFactory( connectionConfig, apiService, timeout ), poolConfig );
073    }
074
075
076    /**
077     * Instantiates a new LDAP connection pool.
078     *
079     * @param factory The LDAP connection factory
080     */
081    public LdapConnectionPool( PoolableObjectFactory<LdapConnection> factory )
082    {
083        this( factory, null );
084    }
085
086
087    /**
088     * Instantiates a new LDAP connection pool.
089     *
090     * @param factory The LDAP connection factory
091     * @param poolConfig The pool configuration
092     */
093    public LdapConnectionPool( PoolableObjectFactory<LdapConnection> factory, Config poolConfig )
094    {
095        super( factory, poolConfig == null ? new Config() : poolConfig );
096        this.factory = factory;
097    }
098
099
100    /**
101     * Returns the LdapApiService instance used by this connection pool.
102     *
103     * @return The LdapApiService instance used by this connection pool.
104     */
105    public LdapApiService getLdapApiService()
106    {
107        return ( ( AbstractPoolableLdapConnectionFactory ) factory ).getLdapApiService();
108    }
109
110
111    /**
112     * Gives a LdapConnection fetched from the pool.
113     *
114     * @return an LdapConnection object from pool
115     * @throws LdapException if an error occurs while obtaining a connection from the factory
116     */
117    public LdapConnection getConnection() throws LdapException
118    {
119        LdapConnection connection;
120
121        try
122        {
123            connection = super.borrowObject();
124            LOG.trace( "borrowed connection {}", connection );
125        }
126        catch ( LdapException | RuntimeException e )
127        {
128            throw e;
129        }
130        catch ( Exception e )
131        {
132            // wrap in runtime, but this should NEVER happen per published 
133            // contract as it only throws what the makeObject throws and our 
134            // PoolableLdapConnectionFactory only throws LdapException
135            LOG.error( "An unexpected exception was thrown: ", e );
136            throw new RuntimeException( e );
137        }
138
139        return connection;
140    }
141
142
143    private static ValidatingPoolableLdapConnectionFactory newPoolableConnectionFactory(
144        LdapConnectionConfig connectionConfig, LdapApiService apiService,
145        long timeout )
146    {
147        DefaultLdapConnectionFactory connectionFactory =
148            new DefaultLdapConnectionFactory( connectionConfig );
149        connectionFactory.setLdapApiService( apiService );
150        connectionFactory.setTimeOut( timeout );
151        return new ValidatingPoolableLdapConnectionFactory( connectionFactory );
152    }
153
154
155    /**
156     * Places the given LdapConnection back in the pool.
157     * 
158     * @param connection the LdapConnection to be released
159     * @throws LdapException if an error occurs while releasing the connection
160     */
161    public void releaseConnection( LdapConnection connection ) throws LdapException
162    {
163        try
164        {
165            super.returnObject( connection );
166            LOG.trace( "returned connection {}", connection );
167        }
168        catch ( LdapException | RuntimeException e )
169        {
170            throw e;
171        }
172        catch ( Exception e )
173        {
174            // wrap in runtime, but this should NEVER happen as it only throws 
175            // what the passivateObject throws and our 
176            // PoolableLdapConnectionFactory only throws LdapException
177            LOG.error( "An unexpected exception was thrown: ", e );
178            throw new RuntimeException( e );
179        }
180    }
181}