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.pool2.PooledObject;
025import org.apache.commons.pool2.impl.DefaultPooledObject;
026import org.apache.directory.api.asn1.util.Oid;
027import org.apache.directory.api.i18n.I18n;
028import org.apache.directory.api.ldap.model.exception.LdapException;
029import org.apache.directory.api.ldap.model.message.BindRequest;
030import org.apache.directory.api.ldap.model.message.ExtendedRequest;
031import org.apache.directory.api.ldap.model.name.Dn;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * A factory for creating LdapConnection objects managed by LdapConnectionPool. 
038 * A bind operation is executed upon return if any of the following operations 
039 * were performed on the connection while it was checked out:
040 * 
041 * <ul>
042 * <li>{@link LdapConnection#bind() bind()}</li>
043 * <li>{@link LdapConnection#anonymousBind() anonymousBind()}</li>
044 * <li>{@link LdapConnection#bind(String) bind(String)}</li>
045 * <li>{@link LdapConnection#bind(String, String) bind(String, String)}</li>
046 * <li>{@link LdapConnection#bind(Dn) bind(Dn)}</li>
047 * <li>{@link LdapConnection#bind(Dn, String) bind(Dn, String)}</li>
048 * <li>{@link LdapConnection#bind(BindRequest) bind(BindRequest)}</li>
049 * <li>{@link LdapConnection#extended(String) extended(String)} <i>where oid is StartTLS</i></li>
050 * <li>{@link LdapConnection#extended(String, byte[]) extended(String, byte[])} <i>where oid is StartTLS</i></li>
051 * <li>{@link LdapConnection#extended(Oid) extended(String)} <i>where oid is StartTLS</i></li>
052 * <li>{@link LdapConnection#extended(Oid, byte[]) extended(String, byte[])} <i>where oid is StartTLS</i></li>
053 * <li>{@link LdapConnection#extended(ExtendedRequest) extended(ExtendedRequest)} <i>where ExtendedRequest is StartTLS</i></li>
054 * </ul>
055 * 
056 * This is a <i>MOSTLY</i> safe way to handle connections in a pool. If one 
057 * would like to use a slightly less expensive pool factory, the 
058 * {@link DefaultPoolableLdapConnectionFactory} may be the right choice.
059 * 
060 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
061 */
062public class ValidatingPoolableLdapConnectionFactory extends AbstractPoolableLdapConnectionFactory
063{
064    /** This class logger */
065    private static final Logger LOG = LoggerFactory.getLogger( ValidatingPoolableLdapConnectionFactory.class );
066
067
068    /**
069     * Creates a new instance of ValidatingPoolableLdapConnectionFactory.
070     *
071     * @param config the configuration for creating LdapConnections
072     */
073    public ValidatingPoolableLdapConnectionFactory( LdapConnectionConfig config )
074    {
075        this( new DefaultLdapConnectionFactory( config ) );
076    }
077
078
079    /**
080     * Creates a new instance of ValidatingPoolableLdapConnectionFactory.  The
081     * <code>connectionFactoryClass</code> must have a public constructor accepting
082     * an <code>LdapConnectionConfig</code> object or an 
083     * <code>IllegalArgumentException</code> will be thrown.
084     *
085     * @param config the configuration for creating LdapConnections
086     * @param connectionFactoryClass An implementation class of for the 
087     * LDAP connection factory.
088     * @throws IllegalArgumentException If the instantiation of an instance of 
089     * the <code>connectionFactoryClass</code> fails.
090     */
091    public ValidatingPoolableLdapConnectionFactory( LdapConnectionConfig config,
092        Class<? extends LdapConnectionFactory> connectionFactoryClass )
093    {
094        this( newLdapConnectionFactory( config, connectionFactoryClass ) );
095    }
096
097
098    /**
099     * Creates a new instance of ValidatingPoolableLdapConnectionFactory.
100     *
101     * @param connectionFactory the connection factory for creating LdapConnections
102     */
103    public ValidatingPoolableLdapConnectionFactory( LdapConnectionFactory connectionFactory )
104    {
105        this.connectionFactory = connectionFactory;
106    }
107
108
109    /**
110     * {@inheritDoc}
111     * 
112     * There is nothing to do to activate a connection.
113     */
114    @Override
115    public void activateObject( PooledObject<LdapConnection> pooledObject ) throws LdapException
116    {
117        LdapConnection connection = pooledObject.getObject();
118        
119        if ( LOG.isDebugEnabled() )
120        {
121            LOG.debug( I18n.msg( I18n.MSG_04146_ACTIVATING, connection ) );
122        }
123        
124        super.activateObject( pooledObject );
125
126        // clear the monitors
127        ( ( MonitoringLdapConnection ) connection ).resetMonitors();
128    }
129
130
131    /**
132     * {@inheritDoc}
133     * 
134     * Specifically, we are creating a new connection based on the LdapConnection Factory
135     * we used to create this pool of connections. The default is to create bound connections.
136     * 
137     * @throws LdapException If unable to connect.
138     */
139    @Override
140    public PooledObject<LdapConnection> makeObject() throws LdapException
141    {
142        if ( LOG.isDebugEnabled() )
143        {
144            LOG.debug( I18n.msg( I18n.MSG_04150_CREATING_LDAP_CONNECTION ) );
145        }
146        
147        return new DefaultPooledObject<LdapConnection>( new MonitoringLdapConnection( connectionFactory.newLdapConnection() ) );
148    }
149
150
151    /**
152     * {@inheritDoc}
153     * 
154     * Here, passivating a connection means we re-bind it, so that the existing LDAPSession
155     * is reset.
156     * 
157     * @throws LdapException If unable to reconfigure and rebind.
158     */
159    @Override
160    public void passivateObject( PooledObject<LdapConnection> pooledObject ) throws LdapException
161    {
162        LdapConnection connection = pooledObject.getObject();
163
164        if ( LOG.isDebugEnabled() )
165        {
166            LOG.debug( I18n.msg( I18n.MSG_04151_PASSIVATING, connection ) );
167        }
168
169        if ( !connection.isConnected() || !connection.isAuthenticated()
170            || ( ( MonitoringLdapConnection ) connection ).bindCalled() )
171        {
172            if ( LOG.isDebugEnabled() )
173            {
174                LOG.debug( I18n.msg( I18n.MSG_04172_REBIND_BIND_CONNECTION, connection ) );
175            }
176            
177            connectionFactory.bindConnection( connection );
178        }
179        
180        if ( ( ( MonitoringLdapConnection ) connection ).startTlsCalled() )
181        {
182            if ( LOG.isDebugEnabled() )
183            {
184                LOG.debug( I18n.msg( I18n.MSG_04173_UNBIND_START_TLS, connection ) );
185            }
186            
187            // unbind to clear the tls
188            connection.unBind();
189            connectionFactory.bindConnection( connection );
190        }
191
192        // in case connection had configuration changed
193        connectionFactory.configureConnection( connection );
194    }
195}