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 */
020package org.apache.directory.server.core.integ;
021
022import java.lang.reflect.AnnotatedElement;
023import java.lang.reflect.Constructor;
024import java.lang.reflect.Field;
025import java.lang.reflect.Method;
026
027import org.apache.commons.pool2.PooledObjectFactory;
028import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
029import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector;
030import org.apache.directory.api.util.Network;
031import org.apache.directory.ldap.client.api.LdapConnection;
032import org.apache.directory.ldap.client.api.LdapConnectionConfig;
033import org.apache.directory.ldap.client.api.LdapConnectionFactory;
034import org.apache.directory.ldap.client.api.LdapConnectionPool;
035import org.apache.directory.ldap.client.api.LdapConnectionValidator;
036import org.apache.directory.ldap.client.template.LdapConnectionTemplate;
037import org.apache.directory.server.annotations.CreateLdapConnectionPool;
038import org.apache.directory.server.ldap.LdapServer;
039import org.junit.jupiter.api.extension.AfterAllCallback;
040import org.junit.jupiter.api.extension.BeforeAllCallback;
041import org.junit.jupiter.api.extension.ExtensionContext;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045public class CreateLdapConnectionPoolExtension implements BeforeAllCallback, AfterAllCallback
046{
047    private static final Logger LOG = LoggerFactory.getLogger( CreateLdapConnectionPoolExtension.class );
048    private static final String LDAP_CONNECTION_TEMPLATE = "ldapConnectionTemplate";
049    private static final String LDAP_CONNECTION_FACTORY = "ldapConnectionFactory";
050    private static final String LDAP_CONNECTION_POOL = "ldapConnectionPool";
051    
052    private LdapConnectionFactory ldapConnectionFactory;
053
054    private void setLdapConnectionTemplate( ExtensionContext context, LdapConnectionTemplate ldapConnectionTemplate ) 
055        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
056    {
057        Class<?> testClass = context.getTestClass().get();
058
059        try
060        {
061            Field field = testClass.getField( LDAP_CONNECTION_TEMPLATE );
062            field.set( null, ldapConnectionTemplate );
063        }
064        catch ( NoSuchFieldException nsfe )
065        {
066            // Ignore
067        }
068    }
069    
070    
071    private void setLdapConnectionPool( ExtensionContext context, LdapConnectionPool ldapConnectionPool ) 
072        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
073    {
074        Class<?> testClass = context.getTestClass().get();
075        
076        try
077        {
078            Field field = testClass.getField( LDAP_CONNECTION_POOL );
079            field.set( null, ldapConnectionPool );
080        }
081        catch ( NoSuchFieldException nsfe )
082        {
083            // Ignore
084        }
085    }
086    
087    
088    private void setLdapConnectionFactory( ExtensionContext context, LdapConnectionFactory ldapConnectionFactory ) 
089        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
090    {
091        Class<?> testClass = context.getTestClass().get();
092        
093        try
094        {
095            Field field = testClass.getField( LDAP_CONNECTION_FACTORY );
096        
097            field.set( null, ldapConnectionFactory );
098        }
099        catch ( NoSuchFieldException nsfe )
100        {
101            // Ignore
102        }
103    }
104    
105    
106    private LdapServer getLdapServer( ExtensionContext context ) 
107        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
108    {
109        Class<?> testClass = context.getTestClass().get();
110        Field field = testClass.getField( ApacheDSTestExtension.CLASS_LS );
111        
112        if ( field != null )
113        {
114            return ( LdapServer ) field.get( testClass );
115        }
116        else
117        {
118            return null;
119        }
120    }
121
122    @Override
123    public void beforeAll( ExtensionContext context ) throws Exception
124    {
125        AnnotatedElement annotations = context.getTestClass().get();
126        CreateLdapConnectionPool createLdapConnectionPool = annotations.getAnnotation( CreateLdapConnectionPool.class );
127        
128        LdapConnectionTemplate ldapConnectionTemplate;
129        LdapServer ldapServer = getLdapServer( context );
130
131        if ( createLdapConnectionPool != null )
132        {
133            LOG.trace( "Creating connection pool to new ldap server" );
134
135            Class<? extends PooledObjectFactory<LdapConnection>> factoryClass =
136                    createLdapConnectionPool.factoryClass();
137            Class<? extends LdapConnectionFactory> connectionFactoryClass =
138                    createLdapConnectionPool.connectionFactoryClass();
139            Class<? extends LdapConnectionValidator> validatorClass =
140                    createLdapConnectionPool.validatorClass();
141            LdapConnectionPool ldapConnectionPool = createLdapConnectionPool( createLdapConnectionPool, ldapServer, factoryClass, 
142                        connectionFactoryClass, validatorClass );
143            ldapConnectionTemplate = new LdapConnectionTemplate( ldapConnectionPool );
144
145            setLdapConnectionTemplate( context, ldapConnectionTemplate );
146            setLdapConnectionFactory( context, ldapConnectionFactory );
147            setLdapConnectionPool( context, ldapConnectionPool );
148        }
149    }
150
151
152    private LdapConnectionPool createLdapConnectionPool( 
153        CreateLdapConnectionPool createLdapConnectionPool,
154        LdapServer ldapServer, 
155        Class<? extends PooledObjectFactory<LdapConnection>> factoryClass,
156        Class<? extends LdapConnectionFactory> connectionFactoryClass,
157        Class<? extends LdapConnectionValidator> validatorClass )
158    {
159        LdapConnectionConfig config = new LdapConnectionConfig();
160        
161        config.setLdapHost( Network.LOOPBACK_HOSTNAME );
162        
163        config.setLdapPort( ldapServer.getPort() );
164        config.setName( "uid=admin,ou=system" );
165        config.setCredentials( "secret" );
166
167        if ( ( createLdapConnectionPool.additionalBinaryAttributes() != null )
168            && ( createLdapConnectionPool.additionalBinaryAttributes().length > 0 ) )
169        {
170            DefaultConfigurableBinaryAttributeDetector binaryAttributeDetector =
171                new DefaultConfigurableBinaryAttributeDetector();
172            binaryAttributeDetector.addBinaryAttribute(
173                createLdapConnectionPool.additionalBinaryAttributes() );
174            config.setBinaryAttributeDetector( binaryAttributeDetector );
175        }
176
177        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
178        poolConfig.setLifo( createLdapConnectionPool.lifo() );
179        poolConfig.setMaxTotal( createLdapConnectionPool.maxActive() );
180        poolConfig.setMaxIdle( createLdapConnectionPool.maxIdle() );
181        poolConfig.setMaxWaitMillis( createLdapConnectionPool.maxWait() );
182        poolConfig.setMinEvictableIdleTimeMillis( createLdapConnectionPool
183            .minEvictableIdleTimeMillis() );
184        poolConfig.setMinIdle( createLdapConnectionPool.minIdle() );
185        poolConfig.setNumTestsPerEvictionRun( createLdapConnectionPool
186            .numTestsPerEvictionRun() );
187        poolConfig.setSoftMinEvictableIdleTimeMillis( createLdapConnectionPool
188            .softMinEvictableIdleTimeMillis() );
189        poolConfig.setTestOnBorrow( createLdapConnectionPool.testOnBorrow() );
190        poolConfig.setTestOnReturn( createLdapConnectionPool.testOnReturn() );
191        poolConfig.setTestWhileIdle( createLdapConnectionPool.testWhileIdle() );
192        poolConfig.setTimeBetweenEvictionRunsMillis( createLdapConnectionPool
193            .timeBetweenEvictionRunsMillis() );
194        poolConfig.setBlockWhenExhausted( createLdapConnectionPool
195            .whenExhaustedAction() == 1 );
196        
197        PooledObjectFactory<LdapConnection> poolableLdapConnectionFactory;
198        
199        try
200        {
201            Constructor<? extends LdapConnectionFactory> constructor = 
202                    connectionFactoryClass.getConstructor( LdapConnectionConfig.class );
203            ldapConnectionFactory = constructor.newInstance( config );
204        }
205        catch ( Exception e )
206        {
207            throw new IllegalArgumentException( "invalid connectionFactoryClass " 
208                    + connectionFactoryClass.getName() + ": " + e.getMessage(), e );
209        }
210        try
211        {
212            Method timeoutSetter = connectionFactoryClass.getMethod( "setTimeOut", Long.TYPE );
213            
214            if ( timeoutSetter != null )
215            {
216                timeoutSetter.invoke( ldapConnectionFactory, createLdapConnectionPool.timeout() );
217            }
218        }
219        catch ( Exception e )
220        {
221            throw new IllegalArgumentException( "invalid connectionFactoryClass "
222                    + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 
223                    + e.getMessage(), e );
224        }
225        
226        try
227        {
228            Constructor<? extends PooledObjectFactory<LdapConnection>> constructor = 
229                    factoryClass.getConstructor( LdapConnectionFactory.class );
230            poolableLdapConnectionFactory = constructor.newInstance( ldapConnectionFactory );
231        }
232        catch ( Exception e )
233        {
234            throw new IllegalArgumentException( "invalid factoryClass " 
235                    + factoryClass.getName() + ": " + e.getMessage(), e );
236        }
237        try
238        {
239            Method setValidator = factoryClass.getMethod( "setValidator", LdapConnectionValidator.class );
240            
241            if ( setValidator != null )
242            {
243                setValidator.invoke( poolableLdapConnectionFactory, validatorClass.newInstance() );
244            }
245        }
246        catch ( Exception e )
247        {
248            throw new IllegalArgumentException( "invalid connectionFactoryClass "
249                    + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 
250                    + e.getMessage(), e );
251        }
252
253        return new LdapConnectionPool( poolableLdapConnectionFactory, poolConfig );
254    }
255
256
257    @Override
258    public void afterAll( ExtensionContext context ) throws Exception
259    {
260        // TODO Auto-generated method stub
261        
262    }
263}