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 */ 019package org.apache.directory.server.core.integ; 020 021 022import java.lang.reflect.Constructor; 023import java.lang.reflect.Method; 024 025import org.apache.commons.pool2.PooledObjectFactory; 026import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 027import org.apache.directory.api.ldap.codec.api.DefaultConfigurableBinaryAttributeDetector; 028import org.apache.directory.api.util.Network; 029import org.apache.directory.ldap.client.api.LdapConnection; 030import org.apache.directory.ldap.client.api.LdapConnectionConfig; 031import org.apache.directory.ldap.client.api.LdapConnectionFactory; 032import org.apache.directory.ldap.client.api.LdapConnectionPool; 033import org.apache.directory.ldap.client.api.LdapConnectionValidator; 034import org.apache.directory.ldap.client.template.LdapConnectionTemplate; 035import org.apache.directory.server.annotations.CreateLdapConnectionPool; 036import org.apache.directory.server.ldap.LdapServer; 037import org.junit.rules.TestRule; 038import org.junit.runner.Description; 039import org.junit.runners.model.Statement; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A {@link TestRule} for creating connection pools. 046 * 047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 048 */ 049public class CreateLdapConnectionPoolRule extends CreateLdapServerRule 050{ 051 private static final Logger LOG = LoggerFactory.getLogger( CreateLdapConnectionPoolRule.class ); 052 private CreateLdapConnectionPoolRule classCreateLdapConnectionPoolRule; 053 private CreateLdapConnectionPool createLdapConnectionPool; 054 private LdapConnectionPool ldapConnectionPool; 055 private LdapConnectionFactory ldapConnectionFactory; 056 private LdapConnectionTemplate ldapConnectionTemplate; 057 private PooledObjectFactory<LdapConnection> poolableLdapConnectionFactory; 058 059 060 public CreateLdapConnectionPoolRule() 061 { 062 this( null ); 063 } 064 065 066 public CreateLdapConnectionPoolRule( 067 CreateLdapConnectionPoolRule classCreateLdapConnectionPoolRule ) 068 { 069 super( classCreateLdapConnectionPoolRule ); 070 this.classCreateLdapConnectionPoolRule = classCreateLdapConnectionPoolRule; 071 } 072 073 074 @Override 075 public Statement apply( final Statement base, final Description description ) 076 { 077 return super.apply( buildStatement( base, description ), description ); 078 } 079 080 081 private Statement buildStatement( final Statement base, final Description description ) 082 { 083 createLdapConnectionPool = description.getAnnotation( CreateLdapConnectionPool.class ); 084 if ( createLdapConnectionPool == null ) 085 { 086 return new Statement() 087 { 088 @Override 089 public void evaluate() throws Throwable 090 { 091 LdapServer ldapServer = getLdapServer(); 092 if ( classCreateLdapConnectionPoolRule != null 093 && classCreateLdapConnectionPoolRule.getLdapServer() != ldapServer ) 094 { 095 LOG.trace( "Creating connection pool to new ldap server" ); 096 097 LdapConnectionPool oldLdapConnectionPool = ldapConnectionPool; 098 LdapConnectionTemplate oldLdapConnectionTemplate = ldapConnectionTemplate; 099 100 Class<? extends PooledObjectFactory<LdapConnection>> factoryClass = 101 classCreateLdapConnectionPoolRule.createLdapConnectionPool.factoryClass(); 102 Class<? extends LdapConnectionFactory> connectionFactoryClass = 103 classCreateLdapConnectionPoolRule.createLdapConnectionPool.connectionFactoryClass(); 104 Class<? extends LdapConnectionValidator> validatorClass = 105 classCreateLdapConnectionPoolRule.createLdapConnectionPool.validatorClass(); 106 ldapConnectionPool = classCreateLdapConnectionPoolRule 107 .createLdapConnectionPool( ldapServer, factoryClass, 108 connectionFactoryClass, validatorClass ); 109 ldapConnectionTemplate = new LdapConnectionTemplate( ldapConnectionPool ); 110 111 try 112 { 113 base.evaluate(); 114 } 115 finally 116 { 117 LOG.trace( "Reverting to old connection pool" ); 118 ldapConnectionPool = oldLdapConnectionPool; 119 ldapConnectionTemplate = oldLdapConnectionTemplate; 120 } 121 } 122 else 123 { 124 LOG.trace( "no @CreateLdapConnectionPool on: {}", description ); 125 base.evaluate(); 126 } 127 } 128 }; 129 } 130 else 131 { 132 return new Statement() 133 { 134 @Override 135 public void evaluate() throws Throwable 136 { 137 LOG.trace( "Creating ldap connection pool" ); 138 Class<? extends PooledObjectFactory<LdapConnection>> factoryClass = 139 createLdapConnectionPool.factoryClass(); 140 Class<? extends LdapConnectionFactory> connectionFactoryClass = 141 createLdapConnectionPool.connectionFactoryClass(); 142 Class<? extends LdapConnectionValidator> validatorClass = 143 createLdapConnectionPool.validatorClass(); 144 ldapConnectionPool = createLdapConnectionPool( getLdapServer(), factoryClass, 145 connectionFactoryClass, validatorClass ); 146 ldapConnectionTemplate = new LdapConnectionTemplate( ldapConnectionPool ); 147 148 try 149 { 150 base.evaluate(); 151 } 152 finally 153 { 154 LOG.trace( "Closing ldap connection pool" ); 155 ldapConnectionPool.close(); 156 ldapConnectionTemplate = null; 157 } 158 } 159 }; 160 } 161 } 162 163 164 private LdapConnectionPool createLdapConnectionPool( LdapServer ldapServer, 165 Class<? extends PooledObjectFactory<LdapConnection>> factoryClass, 166 Class<? extends LdapConnectionFactory> connectionFactoryClass, 167 Class<? extends LdapConnectionValidator> validatorClass ) 168 { 169 LdapConnectionConfig config = new LdapConnectionConfig(); 170 171 config.setLdapHost( Network.LOOPBACK_HOSTNAME ); 172 173 config.setLdapPort( ldapServer.getPort() ); 174 config.setName( "uid=admin,ou=system" ); 175 config.setCredentials( "secret" ); 176 177 if ( ( createLdapConnectionPool.additionalBinaryAttributes() != null ) 178 && ( createLdapConnectionPool.additionalBinaryAttributes().length > 0 ) ) 179 { 180 DefaultConfigurableBinaryAttributeDetector binaryAttributeDetector = 181 new DefaultConfigurableBinaryAttributeDetector(); 182 binaryAttributeDetector.addBinaryAttribute( 183 createLdapConnectionPool.additionalBinaryAttributes() ); 184 config.setBinaryAttributeDetector( binaryAttributeDetector ); 185 } 186 187 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 188 poolConfig.setLifo( createLdapConnectionPool.lifo() ); 189 poolConfig.setMaxTotal( createLdapConnectionPool.maxActive() ); 190 poolConfig.setMaxIdle( createLdapConnectionPool.maxIdle() ); 191 poolConfig.setMaxWaitMillis( createLdapConnectionPool.maxWait() ); 192 poolConfig.setMinEvictableIdleTimeMillis( createLdapConnectionPool 193 .minEvictableIdleTimeMillis() ); 194 poolConfig.setMinIdle( createLdapConnectionPool.minIdle() ); 195 poolConfig.setNumTestsPerEvictionRun( createLdapConnectionPool 196 .numTestsPerEvictionRun() ); 197 poolConfig.setSoftMinEvictableIdleTimeMillis( createLdapConnectionPool 198 .softMinEvictableIdleTimeMillis() ); 199 poolConfig.setTestOnBorrow( createLdapConnectionPool.testOnBorrow() ); 200 poolConfig.setTestOnReturn( createLdapConnectionPool.testOnReturn() ); 201 poolConfig.setTestWhileIdle( createLdapConnectionPool.testWhileIdle() ); 202 poolConfig.setTimeBetweenEvictionRunsMillis( createLdapConnectionPool 203 .timeBetweenEvictionRunsMillis() ); 204 poolConfig.setBlockWhenExhausted( createLdapConnectionPool 205 .whenExhaustedAction() == 1 ); 206 207 try 208 { 209 Constructor<? extends LdapConnectionFactory> constructor = 210 connectionFactoryClass.getConstructor( LdapConnectionConfig.class ); 211 ldapConnectionFactory = constructor.newInstance( config ); 212 } 213 catch ( Exception e ) 214 { 215 throw new IllegalArgumentException( "invalid connectionFactoryClass " 216 + connectionFactoryClass.getName() + ": " + e.getMessage(), e ); 217 } 218 try 219 { 220 Method timeoutSetter = connectionFactoryClass.getMethod( "setTimeOut", Long.TYPE ); 221 if ( timeoutSetter != null ) 222 { 223 timeoutSetter.invoke( ldapConnectionFactory, createLdapConnectionPool.timeout() ); 224 } 225 } 226 catch ( Exception e ) 227 { 228 throw new IllegalArgumentException( "invalid connectionFactoryClass " 229 + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 230 + e.getMessage(), e ); 231 } 232 233 try 234 { 235 Constructor<? extends PooledObjectFactory<LdapConnection>> constructor = 236 factoryClass.getConstructor( LdapConnectionFactory.class ); 237 poolableLdapConnectionFactory = constructor.newInstance( ldapConnectionFactory ); 238 } 239 catch ( Exception e ) 240 { 241 throw new IllegalArgumentException( "invalid factoryClass " 242 + factoryClass.getName() + ": " + e.getMessage(), e ); 243 } 244 try 245 { 246 Method setValidator = factoryClass.getMethod( "setValidator", LdapConnectionValidator.class ); 247 if ( setValidator != null ) 248 { 249 setValidator.invoke( poolableLdapConnectionFactory, 250 validatorClass.newInstance() ); 251 } 252 } 253 catch ( Exception e ) 254 { 255 throw new IllegalArgumentException( "invalid connectionFactoryClass " 256 + connectionFactoryClass.getName() + ", missing setTimeOut(long): " 257 + e.getMessage(), e ); 258 } 259 260 return new LdapConnectionPool( poolableLdapConnectionFactory, poolConfig ); 261 } 262 263 264 public LdapConnectionFactory getLdapConnectionFactory() 265 { 266 return ldapConnectionFactory == null 267 ? ( classCreateLdapConnectionPoolRule == null 268 ? null 269 : classCreateLdapConnectionPoolRule.getLdapConnectionFactory() ) 270 : ldapConnectionFactory; 271 } 272 273 274 public LdapConnectionPool getLdapConnectionPool() 275 { 276 return ldapConnectionPool == null 277 ? ( classCreateLdapConnectionPoolRule == null 278 ? null 279 : classCreateLdapConnectionPoolRule.getLdapConnectionPool() ) 280 : ldapConnectionPool; 281 } 282 283 284 public LdapConnectionTemplate getLdapConnectionTemplate() 285 { 286 return ldapConnectionTemplate == null 287 ? ( classCreateLdapConnectionPoolRule == null 288 ? null 289 : classCreateLdapConnectionPoolRule.getLdapConnectionTemplate() ) 290 : ldapConnectionTemplate; 291 } 292}