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