View Javadoc

1   /*
2    * $Id: AbstractMuleContextTestCase.java 23129 2011-10-07 01:45:53Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.tck.junit4;
12  
13  import org.mule.MessageExchangePattern;
14  import org.mule.api.MuleContext;
15  import org.mule.api.MuleEvent;
16  import org.mule.api.MuleEventContext;
17  import org.mule.api.MuleException;
18  import org.mule.api.MuleSession;
19  import org.mule.api.config.ConfigurationBuilder;
20  import org.mule.api.construct.FlowConstruct;
21  import org.mule.api.context.MuleContextBuilder;
22  import org.mule.api.context.MuleContextFactory;
23  import org.mule.api.context.notification.MuleContextNotificationListener;
24  import org.mule.api.endpoint.InboundEndpoint;
25  import org.mule.api.endpoint.OutboundEndpoint;
26  import org.mule.api.processor.MessageProcessor;
27  import org.mule.api.registry.RegistrationException;
28  import org.mule.api.routing.filter.Filter;
29  import org.mule.api.service.Service;
30  import org.mule.api.transformer.Transformer;
31  import org.mule.api.transport.Connector;
32  import org.mule.config.DefaultMuleConfiguration;
33  import org.mule.config.builders.DefaultsConfigurationBuilder;
34  import org.mule.config.builders.SimpleConfigurationBuilder;
35  import org.mule.context.DefaultMuleContextBuilder;
36  import org.mule.context.DefaultMuleContextFactory;
37  import org.mule.context.notification.MuleContextNotification;
38  import org.mule.tck.MuleTestUtils;
39  import org.mule.tck.SensingNullMessageProcessor;
40  import org.mule.tck.TestingWorkListener;
41  import org.mule.tck.TriggerableMessageSource;
42  import org.mule.tck.testmodels.mule.TestConnector;
43  import org.mule.util.ClassUtils;
44  import org.mule.util.FileUtils;
45  import org.mule.util.StringUtils;
46  import org.mule.util.concurrent.Latch;
47  
48  import java.util.ArrayList;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.Properties;
52  import java.util.concurrent.TimeUnit;
53  import java.util.concurrent.atomic.AtomicReference;
54  
55  
56  import org.junit.After;
57  import org.junit.AfterClass;
58  import org.junit.Before;
59  
60  /**
61   * Extends {@link AbstractMuleTestCase} providing access to a {@link MuleContext}
62   * instance and tools for manage it.
63   */
64  public abstract class AbstractMuleContextTestCase extends AbstractMuleTestCase
65  {
66  
67      /**
68       * Top-level directories under <code>.mule</code> which are not deleted on each
69       * test case recycle. This is required, e.g. to play nice with transaction manager
70       * recovery service object store.
71       */
72      public static final String[] IGNORED_DOT_MULE_DIRS = new String[]{"transaction-log"};
73  
74      /**
75       * If the annotations module is on the classpath, also enable annotations config builder
76       */
77      public static final String CLASSNAME_ANNOTATIONS_CONFIG_BUILDER = "org.mule.config.AnnotationsConfigurationBuilder";
78  
79      /**
80       * The context used to run this test. Context will be created per class
81       * or per method depending on  {@link #disposeContextPerClass}.
82       * The context will be started only when {@link #startContext} is true.
83       */
84      protected static MuleContext muleContext;
85  
86      /**
87       * Start the muleContext once it's configured (defaults to false for AbstractMuleTestCase, true for FunctionalTestCase).
88       */
89      private boolean startContext = false;
90  
91      /**
92       * Convenient test message for unit testing.
93       */
94      public static final String TEST_MESSAGE = "Test Message";
95  
96      /**
97       * Default timeout for multithreaded tests (using CountDownLatch, WaitableBoolean, etc.),
98       * in milliseconds.  The higher this value, the more reliable the test will be, so it
99       * should be set high for Continuous Integration.  However, this can waste time during
100      * day-to-day development cycles, so you may want to temporarily lower it while debugging.
101      */
102     public static final long LOCK_TIMEOUT = 30000;
103 
104     /**
105      * Default timeout for waiting for responses
106      */
107     public static final int RECEIVE_TIMEOUT = 5000;
108 
109     /**
110      * Use this as a semaphore to the unit test to indicate when a callback has successfully been called.
111      */
112     protected Latch callbackCalled;
113 
114     /**
115      * Indicates if the context should be instantiated per context. Default is
116      * false, which means that a context will be instantiated per test method.
117      */
118     private boolean disposeContextPerClass;
119 
120     protected boolean isDisposeContextPerClass()
121     {
122         return disposeContextPerClass;
123     }
124 
125     protected void setDisposeContextPerClass(boolean val)
126     {
127         disposeContextPerClass = val;
128     }
129 
130     @Before
131     public final void setUpMuleContext() throws Exception
132     {
133         muleContext = createMuleContext();
134 
135         if (isStartContext() && muleContext != null && !muleContext.isStarted())
136         {
137             startMuleContext();
138         }
139 
140         doSetUp();
141     }
142 
143     private void startMuleContext() throws MuleException, InterruptedException
144     {
145         final AtomicReference<Latch> contextStartedLatch = new AtomicReference<Latch>();
146 
147         contextStartedLatch.set(new Latch());
148         muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>()
149         {
150             @Override
151             public void onNotification(MuleContextNotification notification)
152             {
153                 if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
154                 {
155                     contextStartedLatch.get().countDown();
156                 }
157             }
158         });
159 
160         muleContext.start();
161 
162         contextStartedLatch.get().await(20, TimeUnit.SECONDS);
163     }
164 
165     /**
166      * Enables the adding of extra behavior on the set up stage of a test right
167      * after the creation of the mule context in {@link #setUpMuleContext}.
168      * <p>
169      * Under normal circumstances this method could be replaced by a
170      * <code>@Before</code> annotated method.
171      *
172      * @throws Exception if something fails that should halt the test case
173      */
174     protected void doSetUp() throws Exception
175     {
176         // template method
177     }
178 
179     protected MuleContext createMuleContext() throws Exception
180     {
181         // Should we set up the manager for every method?
182         MuleContext context;
183         if (isDisposeContextPerClass() && muleContext != null)
184         {
185             context = muleContext;
186         }
187         else
188         {
189             MuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
190             List<ConfigurationBuilder> builders = new ArrayList<ConfigurationBuilder>();
191             builders.add(new SimpleConfigurationBuilder(getStartUpProperties()));
192             //If the annotations module is on the classpath, add the annotations config builder to the list
193             //This will enable annotations config for this instance
194             if (ClassUtils.isClassOnPath(CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, getClass()))
195             {
196                 builders.add((ConfigurationBuilder) ClassUtils.instanciateClass(CLASSNAME_ANNOTATIONS_CONFIG_BUILDER,
197                                                                                 ClassUtils.NO_ARGS, getClass()));
198             }
199             builders.add(getBuilder());
200             addBuilders(builders);
201             MuleContextBuilder contextBuilder = new DefaultMuleContextBuilder();
202             configureMuleContext(contextBuilder);
203             context = muleContextFactory.createMuleContext(builders, contextBuilder);
204             if (!isGracefulShutdown())
205             {
206                 ((DefaultMuleConfiguration) context.getConfiguration()).setShutdownTimeout(0);
207             }
208         }
209         return context;
210     }
211 
212     //This sohuldn't be needed by Test cases but can be used by base testcases that wish to add further builders when
213     //creating the MuleContext.
214     protected void addBuilders(List<ConfigurationBuilder> builders)
215     {
216         //No op
217     }
218 
219     /**
220      * Override this method to set properties of the MuleContextBuilder before it is
221      * used to create the MuleContext.
222      */
223     protected void configureMuleContext(MuleContextBuilder contextBuilder)
224     {
225         contextBuilder.setWorkListener(new TestingWorkListener());
226     }
227 
228     protected ConfigurationBuilder getBuilder() throws Exception
229     {
230         return new DefaultsConfigurationBuilder();
231     }
232 
233     protected String getConfigurationResources()
234     {
235         return StringUtils.EMPTY;
236     }
237 
238     protected Properties getStartUpProperties()
239     {
240         return null;
241     }
242 
243     @After
244     public final void disposeContextPerTest() throws Exception
245     {
246         doTearDown();
247 
248         if (!isDisposeContextPerClass())
249         {
250             disposeContext();
251         }
252     }
253 
254     @AfterClass
255     public static void disposeContext()
256     {
257         try
258         {
259             if (muleContext != null && !(muleContext.isDisposed() || muleContext.isDisposing()))
260             {
261                 muleContext.dispose();
262 
263                 final String workingDir = muleContext.getConfiguration().getWorkingDirectory();
264                 // do not delete TM recovery object store, everything else is good to
265                 // go
266                 FileUtils.deleteTree(FileUtils.newFile(workingDir), IGNORED_DOT_MULE_DIRS);
267             }
268             FileUtils.deleteTree(FileUtils.newFile("./ActiveMQ"));
269         }
270         finally
271         {
272             muleContext = null;
273         }
274     }
275 
276     /**
277      * Enables the adding of extra behavior on the tear down stage of a test
278      * before the mule context is disposed in {@link #disposeContextPerTest}.
279      * <p>
280      * Under normal circumstances this method could be replace with a
281      * <code>@After</code> annotated method.
282      *
283      * @throws Exception if something fails that should halt the testcase
284      */
285     protected void doTearDown() throws Exception
286     {
287         // template method
288     }
289 
290     public static InboundEndpoint getTestInboundEndpoint(String name) throws Exception
291     {
292         return MuleTestUtils.getTestInboundEndpoint(name, muleContext);
293     }
294 
295     public static OutboundEndpoint getTestOutboundEndpoint(String name) throws Exception
296     {
297         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext);
298     }
299 
300     public static InboundEndpoint getTestInboundEndpoint(MessageExchangePattern mep) throws Exception
301     {
302         return MuleTestUtils.getTestInboundEndpoint(mep, muleContext);
303     }
304 
305     public static InboundEndpoint getTestTransactedInboundEndpoint(MessageExchangePattern mep) throws Exception
306     {
307         return MuleTestUtils.getTestTransactedInboundEndpoint(mep, muleContext);
308     }
309 
310     public static InboundEndpoint getTestInboundEndpoint(String name, String uri) throws Exception
311     {
312         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, uri, null, null, null, null);
313     }
314 
315     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri) throws Exception
316     {
317         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, null, null, null);
318     }
319 
320     public static InboundEndpoint getTestInboundEndpoint(String name, List<Transformer> transformers) throws Exception
321     {
322         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, null, transformers, null, null, null);
323     }
324 
325     public static OutboundEndpoint getTestOutboundEndpoint(String name, List<Transformer> transformers) throws Exception
326     {
327         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, null, transformers, null, null);
328     }
329 
330     public static InboundEndpoint getTestInboundEndpoint(String name, String uri,
331                                                          List<Transformer> transformers, Filter filter, Map<Object, Object> properties, Connector connector) throws Exception
332     {
333         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, uri, transformers, filter, properties, connector);
334     }
335 
336     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri,
337                                                            List<Transformer> transformers, Filter filter, Map<Object, Object> properties) throws Exception
338     {
339         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, transformers, filter, properties);
340     }
341 
342     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri,
343                                                            List<Transformer> transformers, Filter filter, Map<Object, Object> properties, Connector connector) throws Exception
344     {
345         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, transformers, filter, properties, connector);
346     }
347 
348     public static MuleEvent getTestEvent(Object data, FlowConstruct service) throws Exception
349     {
350         return MuleTestUtils.getTestEvent(data, service, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
351     }
352 
353     public static MuleEvent getTestEvent(Object data, FlowConstruct service, MessageExchangePattern mep) throws Exception
354     {
355         return MuleTestUtils.getTestEvent(data, service, mep, muleContext);
356     }
357 
358     public static MuleEvent getTestEvent(Object data) throws Exception
359     {
360         return MuleTestUtils.getTestEvent(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
361     }
362 
363     public static MuleEvent getTestEventUsingFlow(Object data) throws Exception
364     {
365         return MuleTestUtils.getTestEventUsingFlow(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
366     }
367 
368     public static MuleEvent getTestEvent(Object data, MessageExchangePattern mep) throws Exception
369     {
370         return MuleTestUtils.getTestEvent(data, mep, muleContext);
371     }
372 
373     public static MuleEvent getTestEvent(Object data, MuleSession session) throws Exception
374     {
375         return MuleTestUtils.getTestEvent(data, session, muleContext);
376     }
377 
378     public static MuleEventContext getTestEventContext(Object data) throws Exception
379     {
380         return MuleTestUtils.getTestEventContext(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
381     }
382 
383     public static MuleEventContext getTestEventContext(Object data, MessageExchangePattern mep) throws Exception
384     {
385         return MuleTestUtils.getTestEventContext(data, mep, muleContext);
386     }
387 
388     public static Transformer getTestTransformer() throws Exception
389     {
390         return MuleTestUtils.getTestTransformer();
391     }
392 
393     public static MuleEvent getTestEvent(Object data, InboundEndpoint endpoint) throws Exception
394     {
395         return MuleTestUtils.getTestEvent(data, endpoint, muleContext);
396     }
397 
398     public static MuleEvent getTestEvent(Object data, Service service, InboundEndpoint endpoint)
399             throws Exception
400     {
401         return MuleTestUtils.getTestEvent(data, service, endpoint, muleContext);
402     }
403 
404     public static MuleSession getTestSession(Service service, MuleContext context)
405     {
406         return MuleTestUtils.getTestSession(service, context);
407     }
408 
409     public static TestConnector getTestConnector() throws Exception
410     {
411         return MuleTestUtils.getTestConnector(muleContext);
412     }
413 
414     public static Service getTestService() throws Exception
415     {
416         return MuleTestUtils.getTestService(muleContext);
417     }
418 
419     public static Service getTestService(String name, Class<?> clazz) throws Exception
420     {
421         return MuleTestUtils.getTestService(name, clazz, muleContext);
422     }
423 
424     public static Service getTestService(String name, Class<?> clazz, Map<?, ?> props) throws Exception
425     {
426         return MuleTestUtils.getTestService(name, clazz, props, muleContext);
427     }
428 
429     protected boolean isStartContext()
430     {
431         return startContext;
432     }
433 
434     protected void setStartContext(boolean startContext)
435     {
436         this.startContext = startContext;
437     }
438 
439     /**
440      * Determines if the test case should perform graceful shutdown or not.
441      * Default is false so that tests run more quickly.
442      */
443     protected boolean isGracefulShutdown()
444     {
445         return false;
446     }
447 
448     /**
449      * Create an object of instance <code>clazz</code>. It will then register the object with the registry so that any
450      * dependencies are injected and then the object will be initialised.
451      * Note that if the object needs to be configured with additional state that cannot be passed into the constructor you should
452      * create an instance first set any additional data on the object then call {@link #initialiseObject(Object)}.
453      *
454      * @param clazz the class to create an instance of.
455      * @param <T>   Object of this type will be returned
456      * @return an initialised instance of <code>class</code>
457      * @throws Exception if there is a problem creating or initializing the object
458      */
459     protected <T extends Object> T createObject(Class<T> clazz) throws Exception
460     {
461         return createObject(clazz, ClassUtils.NO_ARGS);
462     }
463 
464     /**
465      * Create an object of instance <code>clazz</code>. It will then register the object with the registry so that any
466      * dependencies are injected and then the object will be initialised.
467      * Note that if the object needs to be configured with additional state that cannot be passed into the constructor you should
468      * create an instance first set any additional data on the object then call {@link #initialiseObject(Object)}.
469      *
470      * @param clazz the class to create an instance of.
471      * @param args  constructor parameters
472      * @param <T>   Object of this type will be returned
473      * @return an initialised instance of <code>class</code>
474      * @throws Exception if there is a problem creating or initializing the object
475      */
476     @SuppressWarnings("unchecked")
477     protected <T extends Object> T createObject(Class<T> clazz, Object... args) throws Exception
478     {
479         if (args == null)
480         {
481             args = ClassUtils.NO_ARGS;
482         }
483         Object o = ClassUtils.instanciateClass(clazz, args);
484         muleContext.getRegistry().registerObject(String.valueOf(o.hashCode()), o);
485         return (T) o;
486     }
487 
488     /**
489      * A convenience method that will register an object in the registry using its hashcode as the key.  This will cause the object
490      * to have any objects injected and lifecycle methods called.  Note that the object lifecycle will be called to the same current
491      * lifecycle as the MuleContext
492      *
493      * @param o the object to register and initialise it
494      * @throws org.mule.api.registry.RegistrationException
495      */
496     protected void initialiseObject(Object o) throws RegistrationException
497     {
498         muleContext.getRegistry().registerObject(String.valueOf(o.hashCode()), o);
499     }
500 
501     public SensingNullMessageProcessor getSensingNullMessageProcessor()
502     {
503         return new SensingNullMessageProcessor();
504     }
505 
506     public TriggerableMessageSource getTriggerableMessageSource(MessageProcessor listener)
507     {
508         return new TriggerableMessageSource(listener);
509     }
510 
511     public TriggerableMessageSource getTriggerableMessageSource()
512     {
513         return new TriggerableMessageSource();
514     }
515 }