| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.directory.server.core.integ; |
| |
| |
| import java.lang.reflect.Field; |
| import java.util.UUID; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.directory.server.annotations.CreateLdapServer; |
| import org.apache.directory.server.core.DirectoryService; |
| import org.apache.directory.server.core.changelog.ChangeLog; |
| import org.apache.directory.server.core.factory.DSAnnotationProcessor; |
| import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory; |
| import org.apache.directory.server.core.factory.DirectoryServiceFactory; |
| import org.apache.directory.server.core.factory.PartitionFactory; |
| import org.apache.directory.server.factory.ServerAnnotationProcessor; |
| import org.apache.directory.server.i18n.I18n; |
| import org.apache.directory.server.kerberos.kdc.KdcServer; |
| import org.apache.directory.server.ldap.LdapServer; |
| import org.apache.directory.server.protocol.shared.transport.Transport; |
| import org.junit.Ignore; |
| import org.junit.runner.Description; |
| import org.junit.runner.notification.Failure; |
| import org.junit.runner.notification.RunNotifier; |
| import org.junit.runners.BlockJUnit4ClassRunner; |
| import org.junit.runners.model.FrameworkMethod; |
| import org.junit.runners.model.InitializationError; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * The class responsible for running all the tests. t read the annotations, |
| * initialize the DirectoryService, call each test and do the cleanup at the end. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class FrameworkRunner extends BlockJUnit4ClassRunner |
| { |
| /** A logger for this class */ |
| private static final Logger LOG = LoggerFactory.getLogger( FrameworkRunner.class ); |
| |
| /** The 'service' field in the run tests */ |
| private static final String DIRECTORY_SERVICE_FIELD_NAME = "service"; |
| |
| /** The 'ldapServer' field in the run tests */ |
| private static final String LDAP_SERVER_FIELD_NAME = "ldapServer"; |
| |
| /** The 'kdcServer' field in the run tests */ |
| private static final String KDC_SERVER_FIELD_NAME = "kdcServer"; |
| |
| /** The filed used to tell the test that it is run in a suite */ |
| private static final String IS_RUN_IN_SUITE_FIELD_NAME = "isRunInSuite"; |
| |
| /** The suite this class depend on, if any */ |
| private FrameworkSuite suite; |
| |
| /** The DirectoryService for this class, if any */ |
| private DirectoryService classDS; |
| |
| /** The LdapServer for this class, if any */ |
| private LdapServer classLdapServer; |
| |
| /** The KdcServer for this class, if any */ |
| private KdcServer classKdcServer; |
| |
| |
| /** |
| * Creates a new instance of FrameworkRunner. |
| */ |
| public FrameworkRunner( Class<?> clazz ) throws InitializationError |
| { |
| super( clazz ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run( final RunNotifier notifier ) |
| { |
| // Before running any test, check to see if we must create a class DS |
| // Get the LdapServerBuilder, if any |
| CreateLdapServer classLdapServerBuilder = getDescription().getAnnotation( CreateLdapServer.class ); |
| |
| try |
| { |
| classDS = DSAnnotationProcessor.getDirectoryService( getDescription() ); |
| long revision = 0L; |
| DirectoryService directoryService = null; |
| |
| if ( classDS != null ) |
| { |
| // We have a class DS defined, update it |
| directoryService = classDS; |
| |
| // Get the applyLdifs for each level and apply them |
| if ( suite != null ) |
| { |
| DSAnnotationProcessor.applyLdifs( suite.getDescription(), classDS ); |
| } |
| |
| DSAnnotationProcessor.applyLdifs( getDescription(), classDS ); |
| } |
| else |
| { |
| // No class DS. Do we have a Suite ? |
| if ( suite != null ) |
| { |
| // yes. Do we have a suite DS ? |
| directoryService = suite.getDirectoryService(); |
| |
| if ( directoryService != null ) |
| { |
| // yes : apply the class LDIFs only, and tag for reversion |
| revision = getCurrentRevision( directoryService ); |
| |
| // apply the class LDIFs |
| DSAnnotationProcessor.applyLdifs( getDescription(), directoryService ); |
| } |
| else |
| { |
| // No : define a default DS for the suite then |
| DirectoryServiceFactory dsf = DefaultDirectoryServiceFactory.DEFAULT; |
| |
| directoryService = dsf.getDirectoryService(); |
| // enable CL explicitly cause we are not using DSAnnotationProcessor |
| directoryService.getChangeLog().setEnabled( true ); |
| |
| dsf.init( "default" + UUID.randomUUID().toString() ); |
| |
| // Stores it into the suite |
| suite.setDirectoryService( directoryService ); |
| |
| // Apply the suite LDIF first |
| DSAnnotationProcessor.applyLdifs( suite.getDescription(), directoryService ); |
| |
| // Then tag for reversion and apply the class LDIFs |
| revision = getCurrentRevision( directoryService ); |
| |
| DSAnnotationProcessor.applyLdifs( getDescription(), directoryService ); |
| } |
| } |
| else |
| { |
| // No : define a default class DS then |
| DirectoryServiceFactory dsf = DefaultDirectoryServiceFactory.DEFAULT; |
| |
| directoryService = dsf.getDirectoryService(); |
| // enable CL explicitly cause we are not using DSAnnotationProcessor |
| directoryService.getChangeLog().setEnabled( true ); |
| |
| dsf.init( "default" + UUID.randomUUID().toString() ); |
| |
| // Stores the defaultDS in the classDS |
| classDS = directoryService; |
| |
| // Apply the class LDIFs |
| DSAnnotationProcessor.applyLdifs( getDescription(), directoryService ); |
| } |
| } |
| |
| // check if it has a LdapServerBuilder |
| // then use the DS created above |
| if ( classLdapServerBuilder != null ) |
| { |
| int minPort = getMinPort(); |
| |
| classLdapServer = ServerAnnotationProcessor.createLdapServer( getDescription(), directoryService, |
| minPort + 1 ); |
| } |
| else if ( ( suite != null ) && ( suite.getLdapServer() != null ) ) |
| { |
| classLdapServer = suite.getLdapServer(); |
| |
| // set directoryService only if there is no class level DS |
| if ( directoryService == null ) |
| { |
| directoryService = classLdapServer.getDirectoryService(); |
| } |
| |
| // no need to inject the LDIF data that would have been done above |
| // if ApplyLdifs is present |
| } |
| |
| if ( classKdcServer == null ) |
| { |
| classKdcServer = ServerAnnotationProcessor.getKdcServer( getDescription(), directoryService, 0 ); |
| } |
| else if ( suite != null ) |
| { |
| // TODO add suite level KdcServer support |
| } |
| |
| if ( suite == null ) |
| { |
| // print out information which partition factory we use |
| PartitionFactory partitionFactory = DefaultDirectoryServiceFactory.DEFAULT.getPartitionFactory(); |
| LOG.debug( "Using partition factory {}", partitionFactory.getClass().getSimpleName() ); |
| } |
| |
| // Now run the class |
| super.run( notifier ); |
| |
| if ( classLdapServer != null && ( ( suite == null ) || ( suite.getLdapServer() != classLdapServer ) ) ) |
| { |
| classLdapServer.stop(); |
| } |
| |
| if ( classKdcServer != null ) |
| { |
| classKdcServer.stop(); |
| } |
| |
| // cleanup classService if it is not the same as suite service or |
| // it is not null (this second case happens in the absence of a suite) |
| if ( classDS != null ) |
| { |
| LOG.debug( "Shuting down DS for {}", classDS.getInstanceId() ); |
| classDS.shutdown(); |
| FileUtils.deleteDirectory( classDS.getInstanceLayout().getInstanceDirectory() ); |
| } |
| else |
| { |
| // Revert the ldifs |
| // We use a class or suite DS, just revert the current test's modifications |
| revert( directoryService, revision ); |
| } |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_181, getTestClass().getName() ) ); |
| LOG.error( e.getLocalizedMessage() ); |
| notifier.fireTestFailure( new Failure( getDescription(), e ) ); |
| } |
| } |
| |
| |
| /** |
| * Get the lower port out of all the transports |
| */ |
| private int getMinPort() |
| { |
| int minPort = 0; |
| |
| if ( suite != null ) |
| { |
| LdapServer suiteServer = suite.getLdapServer(); |
| |
| if ( suiteServer != null ) |
| { |
| for ( Transport transport : suiteServer.getTransports() ) |
| { |
| if ( minPort <= transport.getPort() ) |
| { |
| minPort = transport.getPort(); |
| } |
| } |
| } |
| } |
| |
| return minPort; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void runChild( FrameworkMethod method, RunNotifier notifier ) |
| { |
| /** The LdapServer for this method, if any */ |
| LdapServer methodLdapServer = null; |
| |
| // Don't run the test if the @Ignored annotation is used |
| if ( method.getAnnotation( Ignore.class ) != null ) |
| { |
| Description description = describeChild( method ); |
| notifier.fireTestIgnored( description ); |
| return; |
| } |
| |
| // Get the applyLdifs for each level |
| Description suiteDescription = null; |
| |
| if ( suite != null ) |
| { |
| suiteDescription = suite.getDescription(); |
| } |
| |
| Description classDescription = getDescription(); |
| Description methodDescription = describeChild( method ); |
| |
| // Before running any test, check to see if we must create a class DS |
| // Get the LdapServerBuilder, if any |
| CreateLdapServer methodLdapServerBuilder = methodDescription.getAnnotation( CreateLdapServer.class ); |
| |
| // Ok, ready to run the test |
| try |
| { |
| DirectoryService directoryService = null; |
| |
| // Set the revision to 0, we will revert only if it's set to another value |
| long revision = 0L; |
| |
| // Check if this method has a dedicated DSBuilder |
| DirectoryService methodDS = DSAnnotationProcessor.getDirectoryService( methodDescription ); |
| |
| // give #1 priority to method level DS if present |
| if ( methodDS != null ) |
| { |
| // Apply all the LDIFs |
| DSAnnotationProcessor.applyLdifs( suiteDescription, methodDS ); |
| DSAnnotationProcessor.applyLdifs( classDescription, methodDS ); |
| DSAnnotationProcessor.applyLdifs( methodDescription, methodDS ); |
| |
| directoryService = methodDS; |
| } |
| else if ( classDS != null ) |
| { |
| directoryService = classDS; |
| |
| // apply the method LDIFs, and tag for reversion |
| revision = getCurrentRevision( directoryService ); |
| |
| DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); |
| } |
| // we don't support method level LdapServer so |
| // we check for the presence of Class level LdapServer first |
| else if ( classLdapServer != null ) |
| { |
| directoryService = classLdapServer.getDirectoryService(); |
| |
| revision = getCurrentRevision( directoryService ); |
| |
| DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); |
| } |
| else if ( classKdcServer != null ) |
| { |
| directoryService = classKdcServer.getDirectoryService(); |
| |
| revision = getCurrentRevision( directoryService ); |
| |
| DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); |
| } |
| else if ( suite != null ) |
| { |
| directoryService = suite.getDirectoryService(); |
| |
| // apply the method LDIFs, and tag for reversion |
| revision = getCurrentRevision( directoryService ); |
| |
| DSAnnotationProcessor.applyLdifs( methodDescription, directoryService ); |
| } |
| |
| if ( methodLdapServerBuilder != null ) |
| { |
| int minPort = getMinPort(); |
| |
| methodLdapServer = ServerAnnotationProcessor.createLdapServer( methodDescription, directoryService, |
| minPort + 1 ); |
| } |
| |
| // At this point, we know which service to use. |
| // Inject it into the class |
| Field dirServiceField = getTestClass().getJavaClass().getField( DIRECTORY_SERVICE_FIELD_NAME ); |
| dirServiceField.set( getTestClass().getJavaClass(), directoryService ); |
| |
| // if we run this class in a suite, tell it to the test |
| Field runInSuiteField = getTestClass().getJavaClass().getField( IS_RUN_IN_SUITE_FIELD_NAME ); |
| runInSuiteField.set( getTestClass().getJavaClass(), suite != null ); |
| |
| Field ldapServerField = getTestClass().getJavaClass().getField( LDAP_SERVER_FIELD_NAME ); |
| |
| dirServiceField.set( getTestClass().getJavaClass(), directoryService ); |
| |
| DirectoryService oldLdapServerDirService = null; |
| DirectoryService oldKdcServerDirService = null; |
| |
| if ( methodLdapServer != null ) { |
| // setting the directoryService is required to inject the correct level DS instance in the class or suite level LdapServer |
| methodLdapServer.setDirectoryService( directoryService ); |
| |
| ldapServerField.set( getTestClass().getJavaClass(), methodLdapServer ); |
| } |
| else if ( classLdapServer != null ) |
| { |
| oldLdapServerDirService = classLdapServer.getDirectoryService(); |
| |
| // setting the directoryService is required to inject the correct level DS instance in the class or suite level LdapServer |
| classLdapServer.setDirectoryService( directoryService ); |
| |
| ldapServerField.set( getTestClass().getJavaClass(), classLdapServer ); |
| } |
| else if ( classKdcServer != null ) |
| { |
| oldKdcServerDirService = classKdcServer.getDirectoryService(); |
| |
| // setting the directoryService is required to inject the correct level DS instance in the class or suite level KdcServer |
| classKdcServer.setDirectoryService( directoryService ); |
| |
| Field kdcServerField = getTestClass().getJavaClass().getField( KDC_SERVER_FIELD_NAME ); |
| kdcServerField.set( getTestClass().getJavaClass(), classKdcServer ); |
| } |
| |
| // Run the test |
| super.runChild( method, notifier ); |
| |
| if ( methodLdapServer != null ) |
| { |
| methodLdapServer.stop(); |
| } |
| |
| if ( oldLdapServerDirService != null ) |
| { |
| classLdapServer.setDirectoryService( oldLdapServerDirService ); |
| } |
| |
| if ( oldKdcServerDirService != null ) |
| { |
| classKdcServer.setDirectoryService( oldKdcServerDirService ); |
| } |
| |
| // Cleanup the methodDS if it has been created |
| if ( methodDS != null ) |
| { |
| LOG.debug( "Shuting down DS for {}", methodDS.getInstanceId() ); |
| methodDS.shutdown(); |
| FileUtils.deleteDirectory( methodDS.getInstanceLayout().getInstanceDirectory() ); |
| } |
| else |
| { |
| // We use a class or suite DS, just revert the current test's modifications |
| revert( directoryService, revision ); |
| } |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_182, method.getName() ) ); |
| LOG.error( "", e ); |
| notifier.fireTestFailure( new Failure( getDescription(), e ) ); |
| } |
| } |
| |
| |
| /** |
| * Set the Suite reference into this class |
| * |
| * @param suite The suite this classd is contained into |
| */ |
| public void setSuite( FrameworkSuite suite ) |
| { |
| this.suite = suite; |
| } |
| |
| |
| /** |
| * @return The Suite this class is contained nto, if any |
| */ |
| public FrameworkSuite getSuite() |
| { |
| return suite; |
| } |
| |
| |
| private long getCurrentRevision( DirectoryService dirService ) throws Exception |
| { |
| if ( ( dirService != null ) && ( dirService.getChangeLog().isEnabled() ) ) |
| { |
| long revision = dirService.getChangeLog().getCurrentRevision(); |
| LOG.debug( "Create revision {}", revision ); |
| |
| return revision; |
| } |
| |
| return 0; |
| } |
| |
| |
| private void revert( DirectoryService dirService, long revision ) throws Exception |
| { |
| if ( dirService == null ) |
| { |
| return; |
| } |
| |
| ChangeLog cl = dirService.getChangeLog(); |
| if ( cl.isEnabled() && ( revision < cl.getCurrentRevision() ) ) |
| { |
| LOG.debug( "Revert revision {}", revision ); |
| dirService.revert( revision ); |
| } |
| } |
| } |