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.Method; 023import java.util.UUID; 024 025import org.apache.directory.api.util.FileUtils; 026import org.apache.directory.server.annotations.CreateKdcServer; 027import org.apache.directory.server.annotations.CreateLdapServer; 028import org.apache.directory.server.core.api.DirectoryService; 029import org.apache.directory.server.core.api.changelog.ChangeLog; 030import org.apache.directory.server.core.factory.DSAnnotationProcessor; 031import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory; 032import org.apache.directory.server.core.factory.DirectoryServiceFactory; 033import org.apache.directory.server.core.factory.PartitionFactory; 034import org.apache.directory.server.factory.ServerAnnotationProcessor; 035import org.apache.directory.server.i18n.I18n; 036import org.apache.directory.server.kerberos.kdc.KdcServer; 037import org.apache.directory.server.ldap.LdapServer; 038import org.junit.Ignore; 039import org.junit.runner.Description; 040import org.junit.runner.notification.Failure; 041import org.junit.runner.notification.RunNotifier; 042import org.junit.runners.BlockJUnit4ClassRunner; 043import org.junit.runners.model.FrameworkMethod; 044import org.junit.runners.model.InitializationError; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048 049/** 050 * The class responsible for running all the tests. t read the annotations, 051 * initialize the DirectoryService, call each test and do the cleanup at the end. 052 * 053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 054 */ 055public class FrameworkRunner extends BlockJUnit4ClassRunner 056{ 057 /** A logger for this class */ 058 private static final Logger LOG = LoggerFactory.getLogger( FrameworkRunner.class ); 059 060 /** The 'service' field in the run tests */ 061 private static final String SET_SERVICE_METHOD_NAME = "setService"; 062 063 /** The 'ldapServer' field in the run tests */ 064 private static final String SET_LDAP_SERVER_METHOD_NAME = "setLdapServer"; 065 066 /** The 'kdcServer' field in the run tests */ 067 private static final String SET_KDC_SERVER_METHOD_NAME = "setKdcServer"; 068 069 /** The DirectoryService for this class, if any */ 070 private DirectoryService classDS; 071 072 /** The LdapServer for this class, if any */ 073 private LdapServer classLdapServer; 074 075 /** The KdcServer for this class, if any */ 076 private KdcServer classKdcServer; 077 078 079 /** 080 * Creates a new instance of FrameworkRunner. 081 * 082 * @param clazz The class to run 083 * @throws InitializationError If the initialization failed 084 */ 085 public FrameworkRunner( Class<?> clazz ) throws InitializationError 086 { 087 super( clazz ); 088 } 089 090 091 /** 092 * {@inheritDoc} 093 */ 094 @Override 095 public void run( final RunNotifier notifier ) 096 { 097 // Before running any test, check to see if we must create a class DS 098 // Get the LdapServerBuilder, if any 099 CreateLdapServer classLdapServerBuilder = getDescription().getAnnotation( CreateLdapServer.class ); 100 101 try 102 { 103 classDS = DSAnnotationProcessor.getDirectoryService( getDescription() ); 104 long revision = 0L; 105 DirectoryService directoryService = null; 106 107 if ( classDS != null ) 108 { 109 // We have a class DS defined, update it 110 directoryService = classDS; 111 112 DSAnnotationProcessor.applyLdifs( getDescription(), classDS ); 113 } 114 else 115 { 116 // No : define a default class DS then 117 DirectoryServiceFactory dsf = DefaultDirectoryServiceFactory.class.newInstance(); 118 119 directoryService = dsf.getDirectoryService(); 120 // enable CL explicitly cause we are not using DSAnnotationProcessor 121 directoryService.getChangeLog().setEnabled( true ); 122 123 dsf.init( "default" + UUID.randomUUID().toString() ); 124 125 // Stores the defaultDS in the classDS 126 classDS = directoryService; 127 128 // Load the schemas 129 DSAnnotationProcessor.loadSchemas( getDescription(), directoryService ); 130 131 // Apply the class LDIFs 132 DSAnnotationProcessor.applyLdifs( getDescription(), directoryService ); 133 } 134 135 // check if it has a LdapServerBuilder 136 // then use the DS created above 137 if ( classLdapServerBuilder != null ) 138 { 139 classLdapServer = ServerAnnotationProcessor.createLdapServer( getDescription(), directoryService ); 140 } 141 142 if ( classKdcServer == null ) 143 { 144 int minPort = getMinPort(); 145 146 classKdcServer = ServerAnnotationProcessor.getKdcServer( getDescription(), directoryService, 147 minPort + 1 ); 148 } 149 150 // print out information which partition factory we use 151 DirectoryServiceFactory dsFactory = DefaultDirectoryServiceFactory.class.newInstance(); 152 PartitionFactory partitionFactory = dsFactory.getPartitionFactory(); 153 LOG.debug( "Using partition factory {}", partitionFactory.getClass().getSimpleName() ); 154 155 // Now run the class 156 super.run( notifier ); 157 158 if ( classLdapServer != null ) 159 { 160 classLdapServer.stop(); 161 } 162 163 if ( classKdcServer != null ) 164 { 165 classKdcServer.stop(); 166 } 167 168 // cleanup classService if it is not the same as suite service or 169 // it is not null (this second case happens in the absence of a suite) 170 if ( classDS != null ) 171 { 172 LOG.debug( "Shuting down DS for {}", classDS.getInstanceId() ); 173 classDS.shutdown(); 174 FileUtils.deleteDirectory( classDS.getInstanceLayout().getInstanceDirectory() ); 175 } 176 else 177 { 178 // Revert the ldifs 179 // We use a class or suite DS, just revert the current test's modifications 180 revert( directoryService, revision ); 181 } 182 } 183 catch ( Exception e ) 184 { 185 e.printStackTrace(); 186 LOG.error( I18n.err( I18n.ERR_181, getTestClass().getName() ) ); 187 LOG.error( e.getLocalizedMessage() ); 188 notifier.fireTestFailure( new Failure( getDescription(), e ) ); 189 } 190 finally 191 { 192 // help GC to get rid of the directory service with all its references 193 classDS = null; 194 classLdapServer = null; 195 classKdcServer = null; 196 } 197 } 198 199 200 /** 201 * Get the lower port out of all the transports 202 */ 203 private int getMinPort() 204 { 205 return 0; 206 } 207 208 209 /** 210 * {@inheritDoc} 211 */ 212 @Override 213 protected void runChild( FrameworkMethod method, RunNotifier notifier ) 214 { 215 /** The LdapServer for this method, if any */ 216 LdapServer methodLdapServer = null; 217 218 /** The KdcServer for this method, if any */ 219 KdcServer methodKdcServer = null; 220 221 // Don't run the test if the @Ignored annotation is used 222 if ( method.getAnnotation( Ignore.class ) != null ) 223 { 224 Description description = describeChild( method ); 225 notifier.fireTestIgnored( description ); 226 return; 227 } 228 229 // Get the applyLdifs for each level 230 Description suiteDescription = null; 231 232 Description classDescription = getDescription(); 233 Description methodDescription = describeChild( method ); 234 235 // Before running any test, check to see if we must create a class DS 236 // Get the LdapServerBuilder, if any 237 CreateLdapServer methodLdapServerBuilder = methodDescription.getAnnotation( CreateLdapServer.class ); 238 CreateKdcServer methodKdcServerBuilder = methodDescription.getAnnotation( CreateKdcServer.class ); 239 240 // Ok, ready to run the test 241 try 242 { 243 DirectoryService directoryService = null; 244 245 // Set the revision to 0, we will revert only if it's set to another value 246 long revision = 0L; 247 248 // Check if this method has a dedicated DSBuilder 249 DirectoryService methodDS = DSAnnotationProcessor.getDirectoryService( methodDescription ); 250 251 // give #1 priority to method level DS if present 252 if ( methodDS != null ) 253 { 254 // Apply all the LDIFs 255 DSAnnotationProcessor.applyLdifs( suiteDescription, methodDS ); 256 DSAnnotationProcessor.applyLdifs( classDescription, methodDS ); 257 DSAnnotationProcessor.applyLdifs( methodDescription, methodDS ); 258 259 directoryService = methodDS; 260 } 261 else if ( classDS != null ) 262 { 263 directoryService = classDS; 264 265 // apply the method LDIFs, and tag for reversion 266 revision = getCurrentRevision( directoryService ); 267 268 DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); 269 } 270 // we don't support method level LdapServer so 271 // we check for the presence of Class level LdapServer first 272 else if ( classLdapServer != null ) 273 { 274 directoryService = classLdapServer.getDirectoryService(); 275 276 revision = getCurrentRevision( directoryService ); 277 278 DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); 279 } 280 else if ( classKdcServer != null ) 281 { 282 directoryService = classKdcServer.getDirectoryService(); 283 284 revision = getCurrentRevision( directoryService ); 285 286 DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); 287 } 288 289 if ( methodLdapServerBuilder != null ) 290 { 291 methodLdapServer = ServerAnnotationProcessor.createLdapServer( methodDescription, directoryService ); 292 } 293 294 if ( methodKdcServerBuilder != null ) 295 { 296 int minPort = getMinPort(); 297 298 methodKdcServer = ServerAnnotationProcessor.getKdcServer( methodDescription, directoryService, 299 minPort + 1 ); 300 } 301 302 // At this point, we know which service to use. 303 // Inject it into the class 304 Method setService = null; 305 306 try 307 { 308 setService = getTestClass().getJavaClass().getMethod( SET_SERVICE_METHOD_NAME, 309 DirectoryService.class ); 310 311 setService.invoke( getTestClass().getJavaClass(), directoryService ); 312 } 313 catch ( NoSuchMethodException nsme ) 314 { 315 // Do nothing 316 } 317 318 // if we run this class in a suite, tell it to the test 319 Method setLdapServer = null; 320 321 try 322 { 323 setLdapServer = getTestClass().getJavaClass().getMethod( SET_LDAP_SERVER_METHOD_NAME, 324 LdapServer.class ); 325 } 326 catch ( NoSuchMethodException nsme ) 327 { 328 // Do nothing 329 } 330 331 Method setKdcServer = null; 332 333 try 334 { 335 setKdcServer = getTestClass().getJavaClass().getMethod( SET_KDC_SERVER_METHOD_NAME, KdcServer.class ); 336 } 337 catch ( NoSuchMethodException nsme ) 338 { 339 // Do nothing 340 } 341 342 DirectoryService oldLdapServerDirService = null; 343 DirectoryService oldKdcServerDirService = null; 344 345 if ( methodLdapServer != null ) 346 { 347 // setting the directoryService is required to inject the correct level DS instance in the class or suite level LdapServer 348 methodLdapServer.setDirectoryService( directoryService ); 349 350 setLdapServer.invoke( getTestClass().getJavaClass(), methodLdapServer ); 351 } 352 else if ( classLdapServer != null ) 353 { 354 oldLdapServerDirService = classLdapServer.getDirectoryService(); 355 356 // setting the directoryService is required to inject the correct level DS instance in the class or suite level LdapServer 357 classLdapServer.setDirectoryService( directoryService ); 358 359 setLdapServer.invoke( getTestClass().getJavaClass(), classLdapServer ); 360 } 361 362 if ( methodKdcServer != null ) 363 { 364 // setting the directoryService is required to inject the correct level DS instance in the class or suite level KdcServer 365 methodKdcServer.setDirectoryService( directoryService ); 366 367 setKdcServer.invoke( getTestClass().getJavaClass(), methodKdcServer ); 368 } 369 else if ( classKdcServer != null ) 370 { 371 oldKdcServerDirService = classKdcServer.getDirectoryService(); 372 373 // setting the directoryService is required to inject the correct level DS instance in the class or suite level KdcServer 374 classKdcServer.setDirectoryService( directoryService ); 375 376 setKdcServer.invoke( getTestClass().getJavaClass(), classKdcServer ); 377 } 378 379 // Run the test 380 super.runChild( method, notifier ); 381 382 if ( methodLdapServer != null ) 383 { 384 methodLdapServer.stop(); 385 } 386 387 if ( oldLdapServerDirService != null ) 388 { 389 classLdapServer.setDirectoryService( oldLdapServerDirService ); 390 } 391 392 if ( oldKdcServerDirService != null ) 393 { 394 classKdcServer.setDirectoryService( oldKdcServerDirService ); 395 } 396 397 // Cleanup the methodDS if it has been created 398 if ( methodDS != null ) 399 { 400 LOG.debug( "Shuting down DS for {}", methodDS.getInstanceId() ); 401 methodDS.shutdown(); 402 FileUtils.deleteDirectory( methodDS.getInstanceLayout().getInstanceDirectory() ); 403 } 404 else 405 { 406 // We use a class or suite DS, just revert the current test's modifications 407 revert( directoryService, revision ); 408 } 409 } 410 catch ( Exception e ) 411 { 412 LOG.error( I18n.err( I18n.ERR_182, method.getName() ) ); 413 LOG.error( "", e ); 414 notifier.fireTestFailure( new Failure( getDescription(), e ) ); 415 } 416 } 417 418 419 private long getCurrentRevision( DirectoryService dirService ) throws Exception 420 { 421 if ( ( dirService != null ) && ( dirService.getChangeLog().isEnabled() ) ) 422 { 423 long revision = dirService.getChangeLog().getCurrentRevision(); 424 LOG.debug( "Create revision {}", revision ); 425 426 return revision; 427 } 428 429 return 0; 430 } 431 432 433 private void revert( DirectoryService dirService, long revision ) throws Exception 434 { 435 if ( dirService == null ) 436 { 437 return; 438 } 439 440 ChangeLog cl = dirService.getChangeLog(); 441 if ( cl.isEnabled() && ( revision < cl.getCurrentRevision() ) ) 442 { 443 LOG.debug( "Revert revision {}", revision ); 444 dirService.revert( revision ); 445 } 446 } 447}