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
021import java.lang.reflect.AnnotatedElement;
022import java.lang.reflect.Field;
023import java.lang.reflect.Method;
024import java.util.UUID;
025
026import org.apache.directory.api.util.FileUtils;
027import org.apache.directory.server.annotations.CreateLdapServer;
028import org.apache.directory.server.core.annotations.CreateDS;
029import org.apache.directory.server.core.api.DirectoryService;
030import org.apache.directory.server.core.api.changelog.Tag;
031import org.apache.directory.server.core.factory.DSAnnotationProcessor;
032import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
033import org.apache.directory.server.core.factory.DirectoryServiceFactory;
034import org.apache.directory.server.factory.ServerAnnotationProcessor;
035import org.apache.directory.server.ldap.LdapServer;
036import org.junit.jupiter.api.Disabled;
037import org.junit.jupiter.api.extension.AfterAllCallback;
038import org.junit.jupiter.api.extension.AfterEachCallback;
039import org.junit.jupiter.api.extension.BeforeAllCallback;
040import org.junit.jupiter.api.extension.BeforeEachCallback;
041import org.junit.jupiter.api.extension.ExtensionContext;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045public class CreateDSTestExtension implements BeforeEachCallback, AfterEachCallback, BeforeAllCallback, AfterAllCallback
046{
047    private static final Logger LOG = LoggerFactory.getLogger( CreateDSTestExtension.class );
048    
049    private static final String CLASS_DS = "classDirectoryService";
050    private static final String METHOD_DS = "methodDirectoryService";
051
052    private void setDirectoryService( ExtensionContext context, String fieldName, DirectoryService directoryService ) 
053        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
054    {
055        Class<?> testClass = context.getTestClass().get();
056        Field field = testClass.getField( fieldName );
057        field.set( null, directoryService );
058    }
059
060
061    private DirectoryService getDirectoryService( ExtensionContext context, String fieldName ) 
062        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
063    {
064        Class<?> testClass = context.getTestClass().get();
065        Field directoryServiceField = testClass.getField( fieldName );
066        
067        if ( directoryServiceField != null )
068        {
069            return ( DirectoryService ) directoryServiceField.get( testClass );
070        }
071        else
072        {
073            return null;
074        }
075    }
076    
077    
078    /**
079     * In the BeforeALl method, we will create a DirectoryService instance that will be used by all
080     * the tests for this class. This DirectoryService will be destroyed in the AfterAll callback.
081     * 
082     * Either we create it from the given description, or we create a default one
083     * 
084     * In any case, we will inject the instance in the test class.
085     */
086    @Override
087    public void beforeAll( ExtensionContext context ) throws Exception
088    {
089        // Don't run the test if the @Disabled annotation is used
090        if ( context.getTestClass().get().getAnnotation( Disabled.class ) != null )
091        {
092            return;
093        }
094        
095        AnnotatedElement classAnnotation = context.getTestClass().get();
096        
097        // Check if we have a CreateS annotation. If not, we will create the DirectoryService instance
098        CreateDS createDs = classAnnotation.getAnnotation( CreateDS.class );
099        DirectoryService directoryService;
100        
101        if ( createDs == null )
102        {
103            // No description: create a default DS
104            DirectoryServiceFactory dsf = DefaultDirectoryServiceFactory.class.newInstance();
105
106            directoryService = dsf.getDirectoryService();
107
108            // enable CL explicitly cause we are not using DSAnnotationProcessor
109            directoryService.getChangeLog().setEnabled( true );
110
111            dsf.init( "default" + UUID.randomUUID().toString() );
112
113            if ( directoryService != null )
114            {
115                Tag tag = directoryService.getChangeLog().tag();
116                DSAnnotationProcessor.applyLdifs( classAnnotation, classAnnotation.getClass().getName(), directoryService );
117                LOG.debug( "Tagged change log: {}", tag );
118            }
119            else
120            {
121                LOG.trace( "no @CreateDS and no outer @CreateDS on: {}", classAnnotation.getClass().getName() );
122            }
123        }
124        else
125        {
126            // We have a description, use it
127            LOG.trace( "Creating directory service" );
128            directoryService = DSAnnotationProcessor.getDirectoryService( createDs );
129            DSAnnotationProcessor.applyLdifs( classAnnotation, classAnnotation.getClass().getName(), directoryService );
130        }
131        
132        // Check if we have a LdapServer annotation
133        CreateLdapServer createLapServer = classAnnotation.getAnnotation( CreateLdapServer.class );
134
135        if ( createLapServer == null )
136        {
137            System.out.println( "" );
138        }
139        else
140        {
141            LdapServer classLdapServer = ServerAnnotationProcessor.createLdapServer( createLapServer, directoryService );
142        }
143
144
145        // The created DS is now stored in the test class
146        setDirectoryService( context, CLASS_DS, directoryService );
147    }
148    
149
150    /** 
151     * We have to shutown the global DS now
152     */
153    @Override
154    public void afterAll( ExtensionContext context ) throws Exception
155    {
156        LOG.trace( "Shutting down global directory service" );
157        Class<?> testClass = context.getTestClass().get();
158        Field directoryServiceField = testClass.getField( CLASS_DS );
159        DirectoryService directoryService = ( DirectoryService ) directoryServiceField.get( testClass );
160        Method shutdownMethod = directoryService.getClass().getDeclaredMethod( "shutdown", new Class[]{} );
161        shutdownMethod.invoke( directoryService );
162        
163        FileUtils.deleteDirectory( directoryService.getInstanceLayout().getInstanceDirectory() );
164    }
165
166    
167    /**
168     * Here, we will create a local directoryService if needed, and only if needed
169     */
170    @Override
171    public void beforeEach( ExtensionContext context ) throws Exception
172    {
173        // Don't run the test if the @Disabled annotation is used
174        if ( context.getTestMethod().get().getAnnotation( Disabled.class ) != null )
175        {
176            return;
177        }
178        
179        AnnotatedElement methodAnnotation = context.getTestMethod().get();
180        AnnotatedElement classAnnotation = context.getTestClass().get();
181        DirectoryService directoryService;
182        
183        CreateDS createDs = methodAnnotation.getAnnotation( CreateDS.class );
184        
185        if ( createDs != null )
186        {
187            LOG.trace( "Creating directory service" );
188            directoryService = DSAnnotationProcessor.getDirectoryService( createDs );
189            DSAnnotationProcessor.applyLdifs( classAnnotation, classAnnotation.getClass().getName(), directoryService );
190            DSAnnotationProcessor.applyLdifs( methodAnnotation, methodAnnotation.getClass().getName(), directoryService );
191
192            setDirectoryService( context, METHOD_DS, directoryService );
193        }
194        else
195        {
196            // We don't have a local DS, so use the global one
197            directoryService = getDirectoryService( context, CLASS_DS );
198            
199            DSAnnotationProcessor.applyLdifs( methodAnnotation, methodAnnotation.getClass().getName(), directoryService );
200        }
201    }
202    
203
204    @Override
205    public void afterEach( ExtensionContext context ) throws Exception
206    {
207        LOG.trace( "Shutting down directory service" );
208        Class<?> testClass = context.getTestClass().get();
209        Field directoryServiceField = testClass.getField( METHOD_DS );
210        DirectoryService directoryService = ( DirectoryService ) directoryServiceField.get( testClass );
211        
212        if ( directoryService != null )
213        {
214            Method shutdownMethod = directoryService.getClass().getDeclaredMethod( "shutdown", new Class[]{} );
215            shutdownMethod.invoke( directoryService );
216            FileUtils.deleteDirectory( directoryService.getInstanceLayout().getInstanceDirectory() );
217            
218            setDirectoryService( context, METHOD_DS, null );
219        }
220    }
221}