View Javadoc

1   /*
2    * $Id: AbstractMuleTestCase.java 20073 2010-11-04 19:42:34Z dirk.olmes $
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;
12  
13  import org.mule.MessageExchangePattern;
14  import org.mule.RequestContext;
15  import org.mule.api.MuleContext;
16  import org.mule.api.MuleEvent;
17  import org.mule.api.MuleEventContext;
18  import org.mule.api.MuleSession;
19  import org.mule.api.config.ConfigurationBuilder;
20  import org.mule.api.context.MuleContextBuilder;
21  import org.mule.api.context.MuleContextFactory;
22  import org.mule.api.context.notification.MuleContextNotificationListener;
23  import org.mule.api.endpoint.ImmutableEndpoint;
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.testmodels.mule.TestConnector;
39  import org.mule.util.ClassUtils;
40  import org.mule.util.FileUtils;
41  import org.mule.util.IOUtils;
42  import org.mule.util.MuleUrlStreamHandlerFactory;
43  import org.mule.util.StringMessageUtils;
44  import org.mule.util.StringUtils;
45  import org.mule.util.SystemUtils;
46  import org.mule.util.concurrent.Latch;
47  
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.net.ServerSocket;
51  import java.net.URL;
52  import java.net.URLClassLoader;
53  import java.util.ArrayList;
54  import java.util.Collections;
55  import java.util.HashMap;
56  import java.util.HashSet;
57  import java.util.Iterator;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.Properties;
61  import java.util.Set;
62  import java.util.concurrent.atomic.AtomicReference;
63  
64  import junit.framework.TestCase;
65  import junit.framework.TestResult;
66  
67  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
68  
69  import org.apache.commons.collections.IteratorUtils;
70  import org.apache.commons.collections.Predicate;
71  import org.apache.commons.logging.Log;
72  import org.apache.commons.logging.LogFactory;
73  
74  /**
75   * <code>AbstractMuleTestCase</code> is a base class for Mule testcases. This
76   * implementation provides services to test code for creating mock and test objects.
77   */
78  public abstract class AbstractMuleTestCase extends TestCase implements TestCaseWatchdogTimeoutHandler
79  {
80  
81      /**
82       * Top-level directories under <code>.mule</code> which are not deleted on each
83       * test case recycle. This is required, e.g. to play nice with transaction manager
84       * recovery service object store.
85       */
86      public static final String[] IGNORED_DOT_MULE_DIRS = new String[]{"transaction-log"};
87  
88      /**
89       * Name of a property to override the default test watchdog timeout.
90       *
91       * @see #DEFAULT_MULE_TEST_TIMEOUT_SECS
92       */
93      public static final String PROPERTY_MULE_TEST_TIMEOUT = "mule.test.timeoutSecs";
94  
95      /**
96       * Default test watchdog timeout in seconds.
97       */
98      public static final int DEFAULT_MULE_TEST_TIMEOUT_SECS = 60;
99  
100     /**
101      * If the annotations module is on the classpath, also enable annotations config builder
102      */
103     public static final String CLASSNAME_ANNOTATIONS_CONFIG_BUILDER = "org.mule.config.AnnotationsConfigurationBuilder";
104 
105 
106     protected static MuleContext muleContext;
107 
108     /**
109      * This flag controls whether the text boxes will be logged when starting each test case.
110      */
111     private static final boolean verbose;
112 
113     // A Map of test case extension objects. JUnit creates a new TestCase instance for
114     // every method, so we need to record metainfo outside the test.
115     private static final Map<String, TestInfo> testInfos = Collections.synchronizedMap(new HashMap<String, TestInfo>());
116 
117     // A logger that should be suitable for most test cases.
118     protected final transient Log logger = LogFactory.getLog(this.getClass());
119 
120     /**
121      * Start the muleContext once it's configured (defaults to false for AbstractMuleTestCase, true for FunctionalTestCase).
122      */
123     private boolean startContext = false;
124 
125     // Should be set to a string message describing any prerequisites not met.
126     private boolean offline = "true".equalsIgnoreCase(System.getProperty("org.mule.offline"));
127 
128     // Barks if the test exceeds its time limit
129     private TestCaseWatchdog watchdog;
130 
131     protected int numPorts = 0;
132     
133     public List<Integer> ports = null;    
134 
135     final static private int MIN_PORT = 5000;
136     final static private int MAX_PORT = 6000;    
137     
138     static
139     {
140         String muleOpts = SystemUtils.getenv("MULE_TEST_OPTS");
141         if (StringUtils.isNotBlank(muleOpts))
142         {
143             Map<String, String> parsedOpts = SystemUtils.parsePropertyDefinitions(muleOpts);
144             String optVerbose = parsedOpts.get("mule.verbose");
145             verbose = Boolean.valueOf(optVerbose);
146         }
147         else
148         {
149             // per default, revert to the old behaviour
150             verbose = true;
151         }
152 
153         // register the custom UrlStreamHandlerFactory.
154         MuleUrlStreamHandlerFactory.installUrlStreamHandlerFactory();
155     }
156 
157     /**
158      * Convenient test message for unit testing.
159      */
160     public static final String TEST_MESSAGE = "Test Message";
161 
162     /**
163      * Default timeout for multithreaded tests (using CountDownLatch, WaitableBoolean, etc.),
164      * in milliseconds.  The higher this value, the more reliable the test will be, so it
165      * should be set high for Continuous Integration.  However, this can waste time during
166      * day-to-day development cycles, so you may want to temporarily lower it while debugging.
167      */
168     public static final long LOCK_TIMEOUT = 30000;
169 
170     /**
171      * Default timeout for waiting for responses
172      */
173     public static final int RECEIVE_TIMEOUT = 5000;
174 
175     /**
176      * Use this as a semaphore to the unit test to indicate when a callback has successfully been called.
177      */
178     protected Latch callbackCalled;
179 
180     /**
181      * Timeout used for the test watchdog
182      */
183     protected int testTimeoutSecs = DEFAULT_MULE_TEST_TIMEOUT_SECS;
184 
185     /**
186      * When a test case depends on a 3rd-party resource such as a public web service,
187      * it may be desirable to not fail the test upon timeout but rather to simply log
188      * a warning.
189      */
190     private boolean failOnTimeout = true;
191 
192     /**
193      * This is just a reference to the main thread running the current thread. It is
194      * set in the {@link #setUp()} method.
195      */
196     private volatile Thread currentTestRunningThread;
197 
198     public AbstractMuleTestCase()
199     {
200         super();
201 
202         TestInfo info = getTestInfo();
203         if (info == null)
204         {
205             info = this.createTestInfo();
206             testInfos.put(getClass().getName(), info);
207         }
208         registerTestMethod();
209         initTestTimeoutSecs();
210     }
211 
212     protected void registerTestMethod()
213     {
214         if (this.getName() != null)
215         {
216             this.getTestInfo().incTestCount(getName());
217         }
218     }
219 
220     protected void initTestTimeoutSecs()
221     {
222         String timeoutString = System.getProperty(PROPERTY_MULE_TEST_TIMEOUT, null);
223         if (timeoutString == null)
224         {
225             // unix style: MULE_TEST_TIMEOUTSECS
226             String variableName = PROPERTY_MULE_TEST_TIMEOUT.toUpperCase().replace(".", "_");
227             timeoutString = System.getenv(variableName);
228         }
229 
230         if (timeoutString != null)
231         {
232             try
233             {
234                 testTimeoutSecs = Integer.parseInt(timeoutString);
235             }
236             catch (NumberFormatException nfe)
237             {
238                 // the default still applies
239             }
240         }
241     }
242 
243     @Override
244     public void setName(String name)
245     {
246         super.setName(name);
247         registerTestMethod();
248     }
249 
250     protected TestInfo createTestInfo()
251     {
252         return new TestInfo(this);
253     }
254 
255     protected TestInfo getTestInfo()
256     {
257         return testInfos.get(this.getClass().getName());
258     }
259 
260     private void clearInfo()
261     {
262         testInfos.remove(this.getClass().getName());
263     }
264 
265     @Override
266     public void run(TestResult result)
267     {
268         if (this.isExcluded())
269         {
270             if (verbose)
271             {
272                 logger.info(this.getClass().getName() + " excluded");
273             }
274             return;
275         }
276 
277         if (this.isDisabledInThisEnvironment())
278         {
279             if (verbose)
280             {
281                 logger.info(this.getClass().getName() + " disabled");
282             }
283             return;
284         }
285 
286         super.run(result);
287     }
288 
289     /**
290      * Shamelessly copy from Spring's ConditionalTestCase so in MULE-2.0 we can extend
291      * this class from ConditionalTestCase.
292      * <p/>
293      * Subclasses can override <code>isDisabledInThisEnvironment</code> to skip a single test.
294      */
295     @Override
296     public void runBare() throws Throwable
297     {
298         // getName will return the name of the method being run. Use the real JUnit implementation,
299         // this class has a different implementation
300         if (this.isDisabledInThisEnvironment(super.getName()))
301         {
302             logger.warn(this.getClass().getName() + "." + super.getName() + " disabled in this environment");
303             return;
304         }
305 
306         // Let JUnit handle execution
307         super.runBare();
308     }
309 
310     /**
311      * Subclasses can override this method to skip the execution of the entire test class.
312      *
313      * @return <code>true</code> if the test class should not be run.
314      */
315     protected boolean isDisabledInThisEnvironment()
316     {
317         return false;
318     }
319 
320     /**
321      * Indicates whether this test has been explicitly disabled through the configuration
322      * file loaded by TestInfo.
323      *
324      * @return whether the test has been explicitly disabled
325      */
326     protected boolean isExcluded()
327     {
328         return getTestInfo().isExcluded();
329     }
330 
331     /**
332      * Should this test run?
333      *
334      * @param testMethodName name of the test method
335      * @return whether the test should execute in the current envionment
336      */
337     protected boolean isDisabledInThisEnvironment(String testMethodName)
338     {
339         return false;
340     }
341 
342     public boolean isOffline(String method)
343     {
344         if (offline)
345         {
346             logger.warn(StringMessageUtils.getBoilerPlate(
347                     "Working offline cannot run test: " + method, '=', 80));
348         }
349         return offline;
350     }
351 
352     protected boolean isDisposeManagerPerSuite()
353     {
354         return getTestInfo().isDisposeManagerPerSuite();
355     }
356 
357     protected void setDisposeManagerPerSuite(boolean val)
358     {
359         getTestInfo().setDisposeManagerPerSuite(val);
360     }
361 
362     public int getTestTimeoutSecs()
363     {
364         return testTimeoutSecs;
365     }
366 
367     protected TestCaseWatchdog createWatchdog()
368     {
369         return new TestCaseWatchdog(testTimeoutSecs, TimeUnit.SECONDS, this);
370     }
371 
372     public void handleTimeout(long timeout, TimeUnit unit)
373     {
374         String msg = "Timeout of " + unit.toMillis(timeout) + "ms exceeded (modify via -Dmule.test.timeoutSecs=XX)";
375 
376         if (failOnTimeout)
377         {
378             logger.fatal(msg + " - Attempting to interrupt thread for test " + this.getName());
379             if (currentTestRunningThread != null)
380             {
381                 currentTestRunningThread.interrupt();
382             }
383             giveTheTestSomeTimeToCleanUpAndThenKillIt("Interrupting didn't work. Killing the VM!. Test "
384                                                       + this.getName() + " did not finish correctly.");
385         }
386         else
387         {
388             logger.warn(msg);
389         }
390     }
391 
392     protected void giveTheTestSomeTimeToCleanUpAndThenKillIt(String messageIfNeedToKill)
393     {
394         try
395         {
396             Thread.sleep(5000);
397             logger.fatal(messageIfNeedToKill);
398             Runtime.getRuntime().halt(1);
399         }
400         catch (InterruptedException e)
401         {
402             logger.info(
403                 "Test thread has been interrupted, probably bt the call to watchdog.cancel() in teardown method.",
404                 e);
405         }
406     }
407 
408     /**
409      * Normal JUnit method
410      *
411      * @throws Exception
412      * @see #doSetUp()
413      */
414     @Override
415     protected final void setUp() throws Exception
416     {
417         // start a watchdog thread
418         watchdog = createWatchdog();
419         watchdog.start();
420        
421         // set up the free ports
422         if (numPorts > 0)
423         {
424             //find some free ports
425             ports = findFreePorts(numPorts);
426 
427             //set the port properties
428             setPortProperties();
429         }                
430         
431         currentTestRunningThread = Thread.currentThread();
432 
433         if (verbose)
434         {
435             System.out.println(StringMessageUtils.getBoilerPlate("Testing: " + getName(), '=', 80));
436         }
437 
438         try
439         {
440             if (getTestInfo().getRunCount() == 0)
441             {
442                 if (getTestInfo().isDisposeManagerPerSuite())
443                 {
444                     // We dispose here jut in case
445                     disposeManager();
446                 }
447                 suitePreSetUp();
448             }
449             if (!getTestInfo().isDisposeManagerPerSuite())
450             {
451                 // We dispose here just in case
452                 disposeManager();
453             }
454             
455             muleContext = createMuleContext();
456 
457             // wait for Mule to fully start when requested (default)
458 
459             // latch ref needs to be final for listener use, wrap in atomic ref
460             final AtomicReference<Latch> contextStartedLatch = new AtomicReference<Latch>();
461             if (isStartContext() && null != muleContext && muleContext.isStarted() == false)
462             {
463                 contextStartedLatch.set(new Latch());
464                 muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>()
465                 {
466                     public void onNotification(MuleContextNotification notification)
467                     {
468                         if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
469                         {
470                             contextStartedLatch.get().countDown();
471                         }
472                     }
473                 });
474                 muleContext.start();
475             }
476 
477             // if it's null, than
478             if (contextStartedLatch.get() != null)
479             {
480                 // wait no more than 20 secs
481                 contextStartedLatch.get().await(20, TimeUnit.SECONDS);
482             }
483             doSetUp();
484         }
485         catch (Exception e)
486         {
487             getTestInfo().incRunCount();
488             throw e;
489         }
490     }
491 
492     protected MuleContext createMuleContext() throws Exception
493     {
494         // Should we set up the manager for every method?
495         MuleContext context;
496         if (getTestInfo().isDisposeManagerPerSuite() && muleContext != null)
497         {
498             context = muleContext;
499         }
500         else
501         {
502             MuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
503             List<ConfigurationBuilder> builders = new ArrayList<ConfigurationBuilder>();
504             builders.add(new SimpleConfigurationBuilder(getStartUpProperties()));
505             //If the annotations module is on the classpath, add the annotations config builder to the list
506             //This will enable annotations config for this instance
507             if (ClassUtils.isClassOnPath(CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, getClass()))
508             {
509                 builders.add((ConfigurationBuilder) ClassUtils.instanciateClass(CLASSNAME_ANNOTATIONS_CONFIG_BUILDER,
510                         ClassUtils.NO_ARGS, getClass()));
511             }
512             builders.add(getBuilder());
513             addBuilders(builders);
514             MuleContextBuilder contextBuilder = new DefaultMuleContextBuilder();
515             configureMuleContext(contextBuilder);
516             context = muleContextFactory.createMuleContext(builders, contextBuilder);
517             if (!isGracefulShutdown())
518             {
519                 ((DefaultMuleConfiguration) context.getConfiguration()).setShutdownTimeout(0);
520             }
521         }
522         return context;
523     }
524 
525     //This sohuldn't be needed by Test cases but can be used by base testcases that wish to add further builders when
526     //creating the MuleContext.
527 
528     protected void addBuilders(List<ConfigurationBuilder> builders)
529     {
530         //No op
531     }
532 
533     /**
534      * Override this method to set properties of the MuleContextBuilder before it is
535      * used to create the MuleContext.
536      */
537     protected void configureMuleContext(MuleContextBuilder contextBuilder)
538     {
539         contextBuilder.setWorkListener(new TestingWorkListener());
540     }
541 
542     protected ConfigurationBuilder getBuilder() throws Exception
543     {
544         return new DefaultsConfigurationBuilder();
545     }
546 
547     protected String getConfigurationResources()
548     {
549         return StringUtils.EMPTY;
550     }
551 
552     protected Properties getStartUpProperties()
553     {
554         return null;
555     }
556 
557     /**
558      * Run <strong>before</strong> any testcase setup.
559      * This is called once only before the test suite runs.
560      */
561     protected void suitePreSetUp() throws Exception
562     {
563         // nothing to do
564     }
565 
566     /**
567      * Run <strong>after</strong> all testcase teardowns.
568      * This is called once only after all the tests in the suite have run.
569      */
570     protected void suitePostTearDown() throws Exception
571     {
572         // nothing to do
573     }
574 
575     /**
576      * Normal JUnit method
577      *
578      * @throws Exception
579      * @see #doTearDown()
580      */
581     @Override
582     protected final void tearDown() throws Exception
583     {
584         try
585         {
586             doTearDown();
587 
588             if (!getTestInfo().isDisposeManagerPerSuite())
589             {
590                 disposeManager();
591             }
592         }
593         finally
594         {
595             try
596             {
597                 getTestInfo().incRunCount();
598                 if (getTestInfo().getRunCount() == getTestInfo().getTestCount())
599                 {
600                     try
601                     {
602                         suitePostTearDown();
603                     }
604                     finally
605                     {
606                         clearInfo();
607                         disposeManager();
608                     }
609                 }
610             }
611             finally
612             {
613                 // remove the watchdog thread in any case
614                 watchdog.cancel();
615             }
616         }
617     }
618 
619     protected void disposeManager()
620     {
621         try
622         {
623             if (muleContext != null && !(muleContext.isDisposed() || muleContext.isDisposing()))
624             {
625                 muleContext.dispose();
626 
627                 final String workingDir = muleContext.getConfiguration().getWorkingDirectory();
628                 // do not delete TM recovery object store, everything else is good to
629                 // go
630                 FileUtils.deleteTree(FileUtils.newFile(workingDir), IGNORED_DOT_MULE_DIRS);
631             }
632             FileUtils.deleteTree(FileUtils.newFile("./ActiveMQ"));
633         }
634         finally
635         {
636             muleContext = null;
637         }
638     }
639 
640     /**
641      * Exactly the same a {@link #setUp()} in normal JUnit test cases.  this is called <strong>before</strong> a test
642      * method has been called.
643      *
644      * @throws Exception if something fails that should halt the testcase
645      */
646     protected void doSetUp() throws Exception
647     {
648         // template method
649     }
650 
651     /**
652      * Exactly the same a {@link #tearDown()} in normal JUnit test cases.  this is called <strong>after</strong> a test
653      * method has been called.
654      *
655      * @throws Exception if something fails that should halt the testcase
656      */
657     protected void doTearDown() throws Exception
658     {
659         RequestContext.clear();
660     }
661 
662     public static InboundEndpoint getTestInboundEndpoint(String name) throws Exception
663     {
664         return MuleTestUtils.getTestInboundEndpoint(name, muleContext);
665     }
666 
667     public static OutboundEndpoint getTestOutboundEndpoint(String name) throws Exception
668     {
669         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext);
670     }
671 
672     public static InboundEndpoint getTestInboundEndpoint(MessageExchangePattern mep) throws Exception
673     {
674         return MuleTestUtils.getTestInboundEndpoint(mep, muleContext);
675     }
676 
677     public static InboundEndpoint getTestTransactedInboundEndpoint(MessageExchangePattern mep) throws Exception
678     {
679         return MuleTestUtils.getTestTransactedInboundEndpoint(mep, muleContext);
680     }
681 
682     public static InboundEndpoint getTestInboundEndpoint(String name, String uri) throws Exception
683     {
684         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, uri, null, null, null, null);
685     }
686 
687     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri) throws Exception
688     {
689         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, null, null, null);
690     }
691 
692     public static InboundEndpoint getTestInboundEndpoint(String name, List<Transformer> transformers) throws Exception
693     {
694         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, null, transformers, null, null, null);
695     }
696 
697     public static OutboundEndpoint getTestOutboundEndpoint(String name, List<Transformer> transformers) throws Exception
698     {
699         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, null, transformers, null, null);
700     }
701 
702     public static InboundEndpoint getTestInboundEndpoint(String name, String uri, 
703         List<Transformer> transformers, Filter filter, Map<Object, Object> properties, Connector connector) throws Exception
704     {
705         return MuleTestUtils.getTestInboundEndpoint(name, muleContext, uri, transformers, filter, properties, connector);
706     }
707 
708     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri, 
709         List<Transformer> transformers, Filter filter, Map<Object, Object> properties) throws Exception
710     {
711         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, transformers, filter, properties);
712     }
713 
714     public static OutboundEndpoint getTestOutboundEndpoint(String name, String uri, 
715         List<Transformer> transformers, Filter filter, Map<Object, Object> properties, Connector connector) throws Exception
716     {
717         return MuleTestUtils.getTestOutboundEndpoint(name, muleContext, uri, transformers, filter, properties, connector);
718     }
719 
720     public static MuleEvent getTestEvent(Object data, Service service) throws Exception
721     {
722         return MuleTestUtils.getTestEvent(data, service, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
723     }
724 
725     public static MuleEvent getTestEvent(Object data, Service service, MessageExchangePattern mep) throws Exception
726     {
727         return MuleTestUtils.getTestEvent(data, service, mep, muleContext);
728     }
729 
730     public static MuleEvent getTestEvent(Object data) throws Exception
731     {
732         return MuleTestUtils.getTestEvent(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
733     }
734 
735     public static MuleEvent getTestEvent(Object data, MessageExchangePattern mep) throws Exception
736     {
737         return MuleTestUtils.getTestEvent(data, mep, muleContext);
738     }
739 
740     public static MuleEvent getTestInboundEvent(Object data, MuleSession session) throws Exception
741     {
742         return MuleTestUtils.getTestInboundEvent(data, session, muleContext);
743     }
744 
745     public static MuleEvent getTestInboundEvent(Object data) throws Exception
746     {
747         return MuleTestUtils.getTestInboundEvent(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
748     }
749 
750     public static MuleEvent getTestInboundEvent(Object data, MessageExchangePattern mep) throws Exception
751     {
752         return MuleTestUtils.getTestInboundEvent(data, mep, muleContext);
753     }
754 
755     public static MuleEventContext getTestEventContext(Object data) throws Exception
756     {
757         return MuleTestUtils.getTestEventContext(data, MessageExchangePattern.REQUEST_RESPONSE, muleContext);
758     }
759 
760     public static MuleEventContext getTestEventContext(Object data, MessageExchangePattern mep) throws Exception
761     {
762         return MuleTestUtils.getTestEventContext(data, mep, muleContext);
763     }
764 
765     public static Transformer getTestTransformer() throws Exception
766     {
767         return MuleTestUtils.getTestTransformer();
768     }
769 
770     public static MuleEvent getTestEvent(Object data, ImmutableEndpoint endpoint) throws Exception
771     {
772         return MuleTestUtils.getTestEvent(data, endpoint, muleContext);
773     }
774 
775     public static MuleEvent getTestEvent(Object data, Service service, ImmutableEndpoint endpoint)
776             throws Exception
777     {
778         return MuleTestUtils.getTestEvent(data, service, endpoint, muleContext);
779     }
780 
781     public static MuleSession getTestSession(Service service, MuleContext context)
782     {
783         return MuleTestUtils.getTestSession(service, context);
784     }
785 
786     public static TestConnector getTestConnector() throws Exception
787     {
788         return MuleTestUtils.getTestConnector(muleContext);
789     }
790 
791     public static Service getTestService() throws Exception
792     {
793         return MuleTestUtils.getTestService(muleContext);
794     }
795 
796     public static Service getTestService(String name, Class<?> clazz) throws Exception
797     {
798         return MuleTestUtils.getTestService(name, clazz, muleContext);
799     }
800 
801     public static Service getTestService(String name, Class<?> clazz, Map<?, ?> props) throws Exception
802     {
803         return MuleTestUtils.getTestService(name, clazz, props, muleContext);
804     }
805 
806     public static class TestInfo
807     {
808         /**
809          * Whether to dispose the manager after every method or once all tests for
810          * the class have run
811          */
812         private final String name;
813         private boolean disposeManagerPerSuite = false;
814         private boolean excluded = false;
815         private volatile int testCount = 0;
816         private volatile int runCount = 0;
817         // @GuardedBy(this)
818         private Set<String> registeredTestMethod = new HashSet<String>();
819 
820         public TestInfo(TestCase test)
821         {
822             this.name = test.getClass().getName();
823 
824             // load test exclusions
825             try
826             {
827                 // We find the physical classpath root URL of the test class and
828                 // use that to find the correct resource. Works fine everywhere,
829                 // regardless of classloaders. See MULE-2414
830                 URL classUrl = ClassUtils.getClassPathRoot(test.getClass());
831                 URLClassLoader tempClassLoader = new URLClassLoader(new URL[]{classUrl});
832                 URL fileUrl = tempClassLoader.getResource("mule-test-exclusions.txt");
833                 if (fileUrl != null)
834                 {
835                     InputStream in = null;
836                     try
837                     {
838                         in = fileUrl.openStream();
839 
840                         // this iterates over all lines in the exclusion file
841                         Iterator<?> lines = IOUtils.lineIterator(in, "UTF-8");
842 
843                         // ..and this finds non-comments that match the test case name
844                         excluded = IteratorUtils.filteredIterator(lines, new Predicate()
845                         {
846                             public boolean evaluate(Object object)
847                             {
848                                 return StringUtils.equals(name, StringUtils.trimToEmpty((String) object));
849                             }
850                         }).hasNext();
851                     }
852                     finally
853                     {
854                         IOUtils.closeQuietly(in);
855                     }
856                 }
857             }
858             catch (IOException ioex)
859             {
860                 // ignore
861             }
862         }
863 
864         public int getTestCount()
865         {
866             return testCount;
867         }
868 
869         public synchronized void incTestCount(String testName)
870         {
871             if (!registeredTestMethod.contains(testName))
872             {
873                 testCount++;
874                 registeredTestMethod.add(testName);
875             }
876         }
877 
878         public int getRunCount()
879         {
880             return runCount;
881         }
882 
883         public void incRunCount()
884         {
885             runCount++;
886         }
887 
888         public String getName()
889         {
890             return name;
891         }
892 
893         public boolean isDisposeManagerPerSuite()
894         {
895             return disposeManagerPerSuite;
896         }
897 
898         public void setDisposeManagerPerSuite(boolean disposeManagerPerSuite)
899         {
900             this.disposeManagerPerSuite = disposeManagerPerSuite;
901         }
902 
903         public boolean isExcluded()
904         {
905             return excluded;
906         }
907 
908         @Override
909         public synchronized String toString()
910         {
911             StringBuffer buf = new StringBuffer();
912             return buf.append(name).append(", (").append(runCount).append(" / ").append(testCount).append(
913                     ") tests run, disposePerSuite=").append(disposeManagerPerSuite).toString();
914         }
915     }
916 
917     protected boolean isStartContext()
918     {
919         return startContext;
920     }
921 
922     protected void setStartContext(boolean startContext)
923     {
924         this.startContext = startContext;
925     }
926 
927     public void setFailOnTimeout(boolean failOnTimeout)
928     {
929         this.failOnTimeout = failOnTimeout;
930     }
931 
932     /**
933      * Determines if the test case should perform graceful shutdown or not.
934      * Default is false so that tests run more quickly.
935      *
936      * @return
937      */
938     protected boolean isGracefulShutdown()
939     {
940         return false;
941     }
942 
943     /**
944      * Create an object of instance <code>clazz</code>. It will then register the object with the registry so that any
945      * dependencies are injected and then the object will be initialised.
946      * Note that if the object needs to be configured with additional state that cannot be passed into the constructor you should
947      * create an instance first set any additional data on the object then call {@link #initialiseObject(Object)}.
948      *
949      * @param clazz the class to create an instance of.
950      * @param <T>   Object of this type will be returned
951      * @return an initialised instance of <code>class</code>
952      * @throws Exception if there is a problem creating or initializing the object
953      */
954     protected <T extends Object> T createObject(Class<T> clazz) throws Exception
955     {
956         return createObject(clazz, ClassUtils.NO_ARGS);
957     }
958 
959     /**
960      * Create an object of instance <code>clazz</code>. It will then register the object with the registry so that any
961      * dependencies are injected and then the object will be initialised.
962      * Note that if the object needs to be configured with additional state that cannot be passed into the constructor you should
963      * create an instance first set any additional data on the object then call {@link #initialiseObject(Object)}.
964      *
965      * @param clazz the class to create an instance of.
966      * @param args  constructor parameters
967      * @param <T>   Object of this type will be returned
968      * @return an initialised instance of <code>class</code>
969      * @throws Exception if there is a problem creating or initializing the object
970      */
971     @SuppressWarnings("unchecked")
972     protected <T extends Object> T createObject(Class<T> clazz, Object... args) throws Exception
973     {
974         if (args == null)
975         {
976             args = ClassUtils.NO_ARGS;
977         }
978         Object o = ClassUtils.instanciateClass(clazz, args);
979         muleContext.getRegistry().registerObject(String.valueOf(o.hashCode()), o);
980         return (T) o;
981     }
982 
983     /**
984      * A convenience method that will register an object in the registry using its hashcode as the key.  This will cause the object
985      * to have any objects injected and lifecycle methods called.  Note that the object lifecycle will be called to the same current
986      * lifecycle as the MuleContext
987      *
988      * @param o the object to register and initialise it
989      * @throws RegistrationException
990      */
991     protected void initialiseObject(Object o) throws RegistrationException
992     {
993         muleContext.getRegistry().registerObject(String.valueOf(o.hashCode()), o);
994     }
995     
996     public SensingNullMessageProcessor getSensingNullMessageProcessor()
997     {
998         return new SensingNullMessageProcessor();
999     }
1000     
1001     public TriggerableMessageSource getTriggerableMessageSource(MessageProcessor listener)
1002     {
1003         return new TriggerableMessageSource(listener);
1004     }
1005 
1006     public TriggerableMessageSource getTriggerableMessageSource()
1007     {
1008         return new TriggerableMessageSource();
1009     }
1010 
1011     /**
1012      * Define the ports as java system properties, starting with 'port1'
1013      */
1014     private void setPortProperties()
1015     {
1016         for (int i = 0; i < ports.size(); i++)
1017         {
1018             System.setProperty("port" + (i + 1), String.valueOf(ports.get(i)));
1019         }
1020     }
1021     
1022     /**
1023      * Find a given number of available ports
1024      * 
1025      * @param numberOfPorts The number of free ports to find
1026      * @return an ArrayList with the number of requested ports
1027      */
1028     public List<Integer> findFreePorts(int numberOfPorts)
1029     {
1030         ArrayList<Integer> freePorts = new ArrayList<Integer>();
1031         for (int port = MIN_PORT; freePorts.size() != numberOfPorts && port < MAX_PORT; ++port)
1032         {
1033             if (isPortFree(port))
1034             {
1035                 freePorts.add(port);
1036             }
1037         }
1038 
1039         if (freePorts.size() != numberOfPorts)
1040         {
1041             logger.info("requested " + numberOfPorts + " open ports, but returning " + freePorts.size());
1042         }
1043         return freePorts;
1044     }
1045     
1046     /**
1047      * Iterate through the ports and log whether each is available
1048      * @param failIfTaken If true, fails the current test if the port is not available
1049      */
1050     public void checkPorts(boolean failIfTaken, String prefix)
1051     {
1052         for (int i = 0; i < ports.size(); i++)
1053         {
1054             if (isPortFree(ports.get(i)))
1055             {
1056                 logger.info(prefix + " port is free : " + ports.get(i));
1057             }
1058             else
1059             {
1060                 logger.info(prefix + " port is not free : " + ports.get(i));
1061                 if (failIfTaken)
1062                 {
1063                     fail("failing test since port is not free : " + ports.get(i));
1064                 }
1065             }
1066         }
1067     }
1068     
1069 
1070     /**
1071      * Check and log is a given port is available
1072      * 
1073      * @param port the port number to check
1074      * @return true if the port is available, false otherwise
1075      */
1076     public boolean isPortFree(int port)
1077     {
1078         boolean portIsFree = true;
1079         
1080         ServerSocket server = null;
1081         try
1082         {
1083             server = new ServerSocket(port);
1084         }
1085         catch (IOException e)
1086         {
1087             portIsFree = false;
1088         }
1089         finally
1090         {
1091             if (server != null)
1092             {
1093                 try
1094                 {
1095                     server.close();
1096                 }
1097                 catch (IOException e)
1098                 {
1099                     // ignore
1100                 }
1101             }
1102         }
1103         
1104         return portIsFree;
1105     }
1106     
1107     public List<Integer> getPorts()
1108     {
1109         return ports;
1110     }
1111 }