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.integ.state;
020    
021    
022    import java.io.IOException;
023    import java.lang.reflect.Field;
024    import java.lang.reflect.InvocationTargetException;
025    
026    import javax.naming.NamingException;
027    
028    import org.apache.directory.server.integ.InheritableServerSettings;
029    import org.apache.directory.server.ldap.LdapServer;
030    import org.junit.runner.Description;
031    import org.junit.runner.notification.Failure;
032    import org.junit.runner.notification.RunNotifier;
033    import org.junit.runners.model.Statement;
034    import org.junit.runners.model.TestClass;
035    import org.slf4j.Logger;
036    import org.slf4j.LoggerFactory;
037    
038    
039    /**
040     * The context for managing the state of an integration test service.
041     * Each thread of execution driving tests manages it's own service context.
042     * Hence parallelism can be achieved while running integration tests.
043     *
044     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045     * @version $Rev$, $Date$
046     */
047    public class TestServerContext
048    {
049        /** The logger */
050        private static final Logger LOG = LoggerFactory.getLogger( TestServerContext.class );
051        
052        /** The ThreadLocal containing the contexts */
053        private static final ThreadLocal<TestServerContext> CONTEXTS = new ThreadLocal<TestServerContext>();
054    
055        /** The NonExistant state instance */
056        private final TestServerState nonExistentState = new NonExistentState( this );
057    
058        /** The StartedPristine state instance */
059        private final TestServerState startedPristineState = new StartedPristineState( this );
060        
061        /** The StartedNormal state instance */
062        private final TestServerState startedNormalState = new StartedNormalState( this );
063    
064    
065        /** current service state with respect to the testing life cycle */
066        private TestServerState state = nonExistentState;
067    
068        /** the ldap server managed by this context */
069        private LdapServer ldapServer;
070    
071    
072        /**
073         * A private constructor, the class contains only static methods, 
074         * no need to construct an instance.
075         */
076        private TestServerContext()
077        {
078            // Do nothing
079        }
080    
081    
082        /**
083         * Gets the TestServerContext associated with the current thread of
084         * execution.  If one does not yet exist it will be created.
085         *
086         * @return the context associated with the calling thread
087         */
088        public static TestServerContext getServerContext()
089        {
090            TestServerContext context = CONTEXTS.get();
091    
092            if ( context == null )
093            {
094                context = new TestServerContext();
095                CONTEXTS.set( context );
096            }
097    
098            return context;
099        }
100        
101    
102        /**
103         * Sets the TestServerContext for this current thread
104         *
105         * @param context the context associated with the calling thread
106         */
107        public static void set( TestServerContext context )
108        {
109            CONTEXTS.set( context );
110        }
111    
112    
113        /**
114         * Action where an attempt is made to create the service.  Service
115         * creation in this system is the combined instantiation and
116         * configuration which takes place when the factory is used to get
117         * a new instance of the service.
118         *
119         * @param settings the settings for this test
120         * @throws NamingException if we can't create the service
121         */
122        public static void create( InheritableServerSettings settings ) throws NamingException
123        {
124            TestServerState state = getServerContext().getState();
125            state.create( settings );
126        }
127    
128    
129        /**
130         * Action where an attempt is made to destroy the service.  This
131         * entails nulling out reference to it and triggering garbage
132         * collection.
133         */
134        public static void destroy()
135        {
136            TestServerState state = getServerContext().getState();
137            state.destroy();
138        }
139    
140    
141        /**
142         * Action where an attempt is made to erase the contents of the
143         * working directory used by the service for various files including
144         * partition database files.
145         *
146         * @throws IOException on errors while deleting the working directory
147         */
148        public static void cleanup() throws IOException
149        {
150            TestServerState state = getServerContext().getState();
151            state.cleanup();
152        }
153    
154    
155        /**
156         * Action where an attempt is made to start up the service.
157         *
158         * @throws Exception on failures to start the core directory service
159         */
160        public static void startup() throws Exception
161        {
162            TestServerState state = getServerContext().getState();
163            state.startup();
164        }
165    
166    
167        /**
168         * Action where an attempt is made to shutdown the service.
169         *
170         * @throws Exception on failures to stop the core directory service
171         */
172        public static void shutdown() throws Exception
173        {
174            TestServerState state = getServerContext().getState();
175            state.shutdown();
176        }
177    
178    
179        /**
180         * Action where an attempt is made to run a test against the service.
181         *
182         * @param testClass the class whose test method is to be run
183         * @param statement the test method which is to be run
184         * @param notifier a notifier to report failures to
185         * @param settings the inherited settings and annotations associated with
186         * the test method
187         */
188        public static void test( TestClass testClass, Statement statement, RunNotifier notifier,
189                                 InheritableServerSettings settings )
190        {
191            LOG.debug( "calling test(): {}", settings.getDescription().getDisplayName() );
192            TestServerState state = getServerContext().getState();
193            state.test( testClass, statement, notifier, settings );
194        }
195    
196    
197        /**
198         * Action where an attempt is made to revert the service to it's
199         * initial start up state by using a previous snapshot.
200         *
201         * @throws Exception on failures to revert the state of the core
202         * directory service
203         */
204        public static void revert() throws Exception
205        {
206            TestServerState state = getServerContext().getState();
207            state.revert();
208        }
209    
210    
211        static void invokeTest( TestClass testClass, Statement statement, RunNotifier notifier, Description description )
212        {
213            try
214            {
215                Field field = testClass.getJavaClass().getDeclaredField( "ldapServer" );
216                field.set( testClass.getJavaClass(), getServerContext().getLdapServer() );
217    
218                notifier.fireTestStarted( description );
219                statement.evaluate();
220                notifier.fireTestFinished( description );
221            }
222            catch ( InvocationTargetException e )
223            {
224                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), e.getCause() );
225                testAborted( notifier, description, e.getCause() );
226                return;
227            }
228            catch ( InstantiationException ie )
229            {
230                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), ie );
231                testAborted( notifier, description, ie );
232                return;
233            }
234            catch ( IllegalAccessException iae )
235            {
236                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), iae );
237                testAborted( notifier, description, iae );
238                return;
239            }
240            catch ( NoSuchMethodException nsme )
241            {
242                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsme );
243                testAborted( notifier, description, nsme );
244                return;
245            }
246            catch ( NoSuchFieldException nsfe )
247            {
248                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsfe );
249                testAborted( notifier, description, nsfe );
250                return;
251            }
252            catch ( Throwable t )
253            {
254                LOG.error( "Failed to invoke test method: " + description.getDisplayName(), t );
255                testAborted( notifier, description, t );
256                return;
257            }
258        }
259    
260    
261        // -----------------------------------------------------------------------
262        // Package Friendly Instance Methods
263        // -----------------------------------------------------------------------
264    
265    
266        void setState( TestServerState state )
267        {
268            this.state = state;
269        }
270    
271    
272        TestServerState getState()
273        {
274            return state;
275        }
276    
277    
278        TestServerState getNonExistentState()
279        {
280            return nonExistentState;
281        }
282    
283    
284        TestServerState getStartedPristineState()
285        {
286            return startedPristineState;
287        }
288    
289    
290        TestServerState getStartedNormalState()
291        {
292            return startedNormalState;
293        }
294    
295    
296        LdapServer getLdapServer()
297        {
298            return ldapServer;
299        }
300    
301    
302        void setLdapServer( LdapServer ldapServer )
303        {
304            this.ldapServer = ldapServer;
305        }
306    
307    
308        private static void testAborted( RunNotifier notifier, Description description, Throwable cause )
309        {
310            notifier.fireTestStarted( description );
311            notifier.fireTestFailure( new Failure( description, cause ) );
312            notifier.fireTestFinished( description );
313        }
314    }