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