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 }