View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.directory.server.core.integ;
20  
21  
22  import java.lang.reflect.Method;
23  import java.util.UUID;
24  
25  import org.apache.directory.api.util.FileUtils;
26  import org.apache.directory.server.annotations.CreateKdcServer;
27  import org.apache.directory.server.annotations.CreateLdapServer;
28  import org.apache.directory.server.core.api.DirectoryService;
29  import org.apache.directory.server.core.api.changelog.ChangeLog;
30  import org.apache.directory.server.core.factory.DSAnnotationProcessor;
31  import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
32  import org.apache.directory.server.core.factory.DirectoryServiceFactory;
33  import org.apache.directory.server.core.factory.PartitionFactory;
34  import org.apache.directory.server.factory.ServerAnnotationProcessor;
35  import org.apache.directory.server.i18n.I18n;
36  import org.apache.directory.server.kerberos.kdc.KdcServer;
37  import org.apache.directory.server.ldap.LdapServer;
38  import org.junit.Ignore;
39  import org.junit.runner.Description;
40  import org.junit.runner.notification.Failure;
41  import org.junit.runner.notification.RunNotifier;
42  import org.junit.runners.BlockJUnit4ClassRunner;
43  import org.junit.runners.model.FrameworkMethod;
44  import org.junit.runners.model.InitializationError;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  
49  /**
50   * The class responsible for running all the tests. t read the annotations, 
51   * initialize the DirectoryService, call each test and do the cleanup at the end.
52   * 
53   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
54   */
55  public class FrameworkRunner extends BlockJUnit4ClassRunner
56  {
57      /** A logger for this class */
58      private static final Logger LOG = LoggerFactory.getLogger( FrameworkRunner.class );
59  
60      /** The 'service' field in the run tests */
61      private static final String SET_SERVICE_METHOD_NAME = "setService";
62  
63      /** The 'ldapServer' field in the run tests */
64      private static final String SET_LDAP_SERVER_METHOD_NAME = "setLdapServer";
65  
66      /** The 'kdcServer' field in the run tests */
67      private static final String SET_KDC_SERVER_METHOD_NAME = "setKdcServer";
68  
69      /** The DirectoryService for this class, if any */
70      private DirectoryService classDS;
71  
72      /** The LdapServer for this class, if any */
73      private LdapServer classLdapServer;
74  
75      /** The KdcServer for this class, if any */
76      private KdcServer classKdcServer;
77  
78  
79      /**
80       * Creates a new instance of FrameworkRunner.
81       * 
82       * @param clazz The class to run
83       * @throws InitializationError If the initialization failed
84       */
85      public FrameworkRunner( Class<?> clazz ) throws InitializationError
86      {
87          super( clazz );
88      }
89  
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      public void run( final RunNotifier notifier )
96      {
97          // Before running any test, check to see if we must create a class DS
98          // Get the LdapServerBuilder, if any
99          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 }