View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport;
8   
9   
10  import org.mule.api.MuleException;
11  import org.mule.api.endpoint.InboundEndpoint;
12  import org.mule.api.endpoint.OutboundEndpoint;
13  import org.mule.api.lifecycle.LifecycleException;
14  import org.mule.api.service.Service;
15  import org.mule.api.source.CompositeMessageSource;
16  import org.mule.api.transport.MessageDispatcher;
17  import org.mule.api.transport.MessageRequester;
18  import org.mule.tck.junit4.AbstractMuleContextTestCase;
19  import org.mule.tck.testmodels.mule.TestConnector;
20  
21  import javax.resource.spi.work.Work;
22  import javax.resource.spi.work.WorkException;
23  
24  import junit.framework.Assert;
25  import org.junit.Test;
26  
27  import static org.junit.Assert.assertEquals;
28  import static org.junit.Assert.assertFalse;
29  import static org.junit.Assert.assertNotNull;
30  import static org.junit.Assert.assertNull;
31  import static org.junit.Assert.assertTrue;
32  import static org.junit.Assert.fail;
33  
34  /**
35   * Tests that lifecycle methods on a connector are not processed more than once. (@see MULE-3062)
36   * Also test lifecycle of a connector dispatchers, receivers, workManagers and scheduler.
37   */
38  public class ConnectorLifecycleTestCase extends AbstractMuleContextTestCase
39  {
40      private TestConnector connector;
41  
42      @Override
43      public void doSetUp() throws Exception
44      {
45          connector = new TestConnector(muleContext);
46          connector.initialise();
47      }
48  
49      @Override
50      public void doTearDown() throws Exception
51      {
52          if(!connector.isDisposed()) connector.dispose();
53          connector = null;
54      }
55  
56      /**
57       * This test ensures that the connector is only initialised once even on a
58       * direct initialisation (not through Mule).
59       *
60       * @throws Exception if things go pear-shaped
61       */
62      @Test
63      public void testDoubleInitialiseConnector() throws Exception
64      {
65          // Note: the connector was already initialized once during doSetUp()
66  
67          // Initialising the connector should leave it disconnected.
68          assertEquals(1, connector.getInitialiseCount());
69          assertEquals(0, connector.getConnectCount());
70          assertEquals(0, connector.getStartCount());
71          assertEquals(0, connector.getStopCount());
72          assertEquals(0, connector.getDisconnectCount());
73          assertEquals(0, connector.getDisposeCount());
74  
75          // Initialising the connector again should not throw an exception.
76          try
77          {
78              connector.initialise();
79              Assert.fail("Expected IllegalStateException not thrown.");
80          }
81          catch (IllegalStateException ex)
82          {
83              // ignore since expected
84          }
85      }
86  
87      /**
88       * This test ensures that the connector is only started once even on a
89       * direct restart (not through Mule).
90       *
91       * @throws Exception if things go pear-shaped
92       */
93      @Test
94      public void testDoubleStartConnector() throws Exception
95      {
96          // Starting the connector should leave it uninitialised,
97          // but connected and started.
98          connector.start();
99          assertEquals(1, connector.getInitialiseCount());
100         assertEquals(1, connector.getConnectCount());
101         assertEquals(1, connector.getStartCount());
102         assertEquals(0, connector.getStopCount());
103         assertEquals(0, connector.getDisconnectCount());
104         assertEquals(0, connector.getDisposeCount());
105 
106         // Starting the connector again
107         try
108         {
109             connector.start();
110             fail("cannot start the connector twice");
111         }
112         catch (IllegalStateException e)
113         {
114             //expected
115         }
116         assertEquals(1, connector.getInitialiseCount());
117         assertEquals(1, connector.getConnectCount());
118         assertEquals(1, connector.getStartCount());
119         assertEquals(0, connector.getStopCount());
120         assertEquals(0, connector.getDisconnectCount());
121         assertEquals(0, connector.getDisposeCount());
122     }
123 
124     /**
125      * This test ensures that the connector is only stopped once even on a
126      * direct restop (not through Mule).
127      *
128      * @throws Exception if things go pear-shaped
129      */
130     @Test
131     public void testDoubleStopConnector() throws Exception
132     {
133         // Starting the connector should leave it uninitialised,
134         // but connected and started.
135         connector.start();
136         assertEquals(1, connector.getInitialiseCount());
137         assertEquals(1, connector.getConnectCount());
138         assertEquals(1, connector.getStartCount());
139         assertEquals(0, connector.getStopCount());
140         assertEquals(0, connector.getDisconnectCount());
141         assertEquals(0, connector.getDisposeCount());
142 
143         assertTrue(connector.isStarted());
144 
145         // Stopping the connector should stop and disconnect it.
146         connector.stop();
147         assertEquals(1, connector.getInitialiseCount());
148         assertEquals(1, connector.getConnectCount());
149         assertEquals(1, connector.getStartCount());
150         assertEquals(1, connector.getStopCount());
151         assertEquals(1, connector.getDisconnectCount());
152         assertEquals(0, connector.getDisposeCount());
153 
154 
155         try
156         {
157             connector.stop();
158             fail("cannot stop the connector twice");
159         }
160         catch (IllegalStateException e)
161         {
162             //expected
163         }
164         assertEquals(1, connector.getInitialiseCount());
165         assertEquals(1, connector.getConnectCount());
166         assertEquals(1, connector.getStartCount());
167         assertEquals(1, connector.getStopCount());
168         assertEquals(1, connector.getDisconnectCount());
169         assertEquals(0, connector.getDisposeCount());
170     }
171 
172     /**
173      * This test ensures that the connector is only disposed once even on a
174      * direct disposal (not through Mule).
175      *
176      * @throws Exception if things go pear-shaped
177      */
178     @Test
179     public void testDoubleDisposeConnectorStartStop() throws Exception
180     {
181         connector.start();
182         assertTrue(connector.isStarted());
183 
184         connector.stop();
185         assertFalse(connector.isStarted());
186 
187         // Disposing the connector should leave it uninitialised.
188         connector.dispose();
189         assertEquals(1, connector.getInitialiseCount());
190         assertEquals(1, connector.getConnectCount());
191         assertEquals(1, connector.getStartCount());
192         assertEquals(1, connector.getStopCount());
193         assertEquals(1, connector.getDisconnectCount());
194         assertEquals(1, connector.getDisposeCount());
195 
196         try
197         {
198             connector.dispose();
199             fail("cannot dispose the connector twice");
200         }
201         catch (IllegalStateException e)
202         {
203             //expected
204         }
205         assertEquals(1, connector.getInitialiseCount());
206         assertEquals(1, connector.getConnectCount());
207         assertEquals(1, connector.getStartCount());
208         assertEquals(1, connector.getStopCount());
209         assertEquals(1, connector.getDisconnectCount());
210         assertEquals(1, connector.getDisposeCount());
211     }
212 
213     /**
214      * This test ensures that the connector is only disposed once even on a
215      * direct disposal (not through Mule).
216      *
217      * @throws Exception if things go pear-shaped
218      */
219     @Test
220     public void testDoubleDisposeConnectorStartOnly() throws Exception
221     {
222         connector.start();
223         assertTrue(connector.isStarted());
224 
225         // Disposing the connector should leave it uninitialised.
226         connector.dispose();
227         assertEquals(1, connector.getInitialiseCount());
228         assertEquals(1, connector.getConnectCount());
229         assertEquals(1, connector.getStartCount());
230         // dispose() implicitly calls stop()
231         assertEquals(1, connector.getStopCount());
232         assertEquals(1, connector.getDisconnectCount());
233         assertEquals(1, connector.getDisposeCount());
234 
235         try
236         {
237             connector.dispose();
238             fail("cannot dispose the connector twice");
239         }
240         catch (IllegalStateException e)
241         {
242             //expected
243         }
244         assertEquals(1, connector.getInitialiseCount());
245         assertEquals(1, connector.getConnectCount());
246         assertEquals(1, connector.getStartCount());
247         // dispose() implicitly calls stop()
248         assertEquals(1, connector.getStopCount());
249         assertEquals(1, connector.getDisconnectCount());
250         assertEquals(1, connector.getDisposeCount());
251     }
252 
253     /**
254      * This test ensures that the connector is only disposed once even on a
255      * direct disposal (not through Mule).
256      *
257      * @throws Exception if things go pear-shaped
258      */
259     @Test
260     public void testDoubleDisposeConnector() throws Exception
261     {
262         // Disposing the connector should leave it uninitialised.
263         connector.dispose();
264         assertEquals(1, connector.getInitialiseCount());
265         assertEquals(0, connector.getConnectCount());
266         assertEquals(0, connector.getStartCount());
267         assertEquals(0, connector.getStopCount());
268         assertEquals(0, connector.getDisconnectCount());
269         assertEquals(1, connector.getDisposeCount());
270 
271         try
272         {
273             connector.dispose();
274             fail("cannot dispose the connector twice");
275         }
276         catch (IllegalStateException e)
277         {
278             //expected
279         }
280         assertEquals(1, connector.getInitialiseCount());
281         assertEquals(0, connector.getConnectCount());
282         assertEquals(0, connector.getStartCount());
283         assertEquals(0, connector.getStopCount());
284         assertEquals(0, connector.getDisconnectCount());
285         assertEquals(1, connector.getDisposeCount());
286     }
287 
288     @Test
289     public void testReceiversLifecycle() throws Exception
290     {
291         Service service=getTestService();
292         service.start();
293         connector.registerListener(getTestInboundEndpoint("in", "test://in"), getSensingNullMessageProcessor(), service);
294 
295         assertEquals(1, connector.receivers.size());
296         assertFalse(( connector.receivers.get("in")).isConnected());
297         assertFalse(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
298 
299         connector.start();
300         assertTrue(( connector.receivers.get("in")).isConnected());
301         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
302 
303         connector.registerListener(getTestInboundEndpoint("in2", "test://in2"), getSensingNullMessageProcessor(), service);
304 
305         assertEquals(2, connector.receivers.size());
306         assertTrue(( connector.receivers.get("in")).isConnected());
307         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
308 
309         assertTrue((connector.receivers.get("in2")).isConnected());
310         assertTrue(((AbstractMessageReceiver)connector.receivers.get("in2")).isStarted());
311 
312         connector.stop();
313         assertEquals(2, connector.receivers.size());
314         assertFalse(( connector.receivers.get("in")).isConnected());
315         assertFalse(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
316         assertFalse(( connector.receivers.get("in2")).isConnected());
317         assertFalse(((AbstractMessageReceiver) connector.receivers.get("in2")).isStarted());
318 
319         connector.start();
320         assertEquals(2, connector.receivers.size());
321         assertTrue((connector.receivers.get("in")).isConnected());
322         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
323         assertTrue((connector.receivers.get("in2")).isConnected());
324         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in2")).isStarted());
325 
326         connector.dispose();
327         assertEquals(0, connector.receivers.size());
328 
329     }
330 
331     @Test
332     public void testReceiversServiceLifecycle() throws Exception
333     {
334         Service service = getTestService();
335         InboundEndpoint endpoint = getTestInboundEndpoint("in", "test://in");
336         ((CompositeMessageSource) service.getMessageSource()).addSource(endpoint);
337         connector = (TestConnector) endpoint.getConnector();
338 
339         assertEquals(0, connector.receivers.size());
340 
341         connector.start();
342         assertEquals(0, connector.receivers.size());
343 
344         service.start();
345         assertEquals(1, connector.receivers.size());
346         assertTrue(( connector.receivers.get("in")).isConnected());
347         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
348 
349         connector.stop();
350         assertEquals(1, connector.receivers.size());
351         assertFalse(( connector.receivers.get("in")).isConnected());
352         assertFalse(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
353 
354         connector.start();
355         assertEquals(1, connector.receivers.size());
356         assertTrue(( connector.receivers.get("in")).isConnected());
357         assertTrue(((AbstractMessageReceiver) connector.receivers.get("in")).isStarted());
358 
359         service.stop();
360         assertEquals(0, connector.receivers.size());
361 
362         connector.stop();
363         assertEquals(0, connector.receivers.size());
364     }
365 
366     @Test
367     public void testDispatchersLifecycle() throws Exception
368     {
369         //using sync endpoint so that any calls to 'process()' will be blocking and avoid timing issues
370         OutboundEndpoint out = getTestOutboundEndpoint("out", 
371             "test://out?exchangePattern=request-response", null, null, null, connector);
372 
373         // attempts to send/dispatch/request are made on a stopped/stopping connector
374         // This should fail because the connector is not started!
375         try
376         {
377             out.process(getTestEvent("data"));
378             fail("cannot send on a connector that is not started");
379         }
380         catch (LifecycleException e)
381         {
382             // expected
383         }
384 
385         assertEquals(0, connector.dispatchers.getNumIdle());
386 
387         // Dispatcher is not started or connected
388         assertDispatcherStartedConnected(out, false, false);
389 
390         connector.start();
391         //This causes the first instance out dispatcher to be created
392         assertDispatcherStartedConnected(out, true, true);
393 
394         OutboundEndpoint out2 = getTestOutboundEndpoint("out2", 
395             "test://out2?exchangePattern=request-response", null, null, null, connector);
396         //This causes the first instance out2 dispatcher to be created
397         out2.process(getTestEvent("data", out2));
398 
399         //At this point there should be two idle, but the build server reports one, I suspect its a timing issues
400         assertEquals(2, connector.dispatchers.getNumIdle());
401         assertDispatcherStartedConnected(out, true, true);
402         assertDispatcherStartedConnected(out2, true, true);
403 
404         connector.stop();
405 
406         // Pool is cleared because of implementation of workaround for MULE-4553
407         assertEquals(0, connector.dispatchers.getNumActive() + connector.dispatchers.getNumIdle());
408         assertDispatcherStartedConnected(out, false, false);
409         assertDispatcherStartedConnected(out2, false, false);
410 
411         connector.start();
412 
413         assertEquals(2, connector.dispatchers.getNumActive() + connector.dispatchers.getNumIdle());
414         assertDispatcherStartedConnected(out, true, true);
415         assertDispatcherStartedConnected(out2, true, true);
416 
417         out.process(getTestEvent("data", out));
418         assertEquals(2, connector.dispatchers.getNumIdle());
419         assertDispatcherStartedConnected(out, true, true);
420 
421         connector.dispose();
422         assertEquals(0, connector.dispatchers.getNumActive() + connector.dispatchers.getNumIdle());
423 
424     }
425 
426     @Test
427     public void testDispatcherFullLifecycle() throws Exception
428     {
429         OutboundEndpoint out = getTestOutboundEndpoint("out", "test://out", null, null, null, connector);
430 
431         MessageDispatcher dispatcher = connector.getDispatcherFactory().create(out);
432         dispatcher.initialise();
433         
434         assertTrue(dispatcher.getLifecycleState().isInitialised());
435         dispatcher.connect();
436         assertTrue(dispatcher.isConnected());
437 
438         dispatcher.start();
439         assertTrue(dispatcher.getLifecycleState().isStarted());
440 
441         dispatcher.stop();
442         assertTrue(dispatcher.getLifecycleState().isStopped());
443 
444         dispatcher.disconnect();
445         assertFalse(dispatcher.isConnected());
446 
447         dispatcher.dispose();
448         assertTrue(dispatcher.getLifecycleState().isDisposed());
449 
450     }
451 
452     @Test
453     public void testRequestersLifecycle() throws Exception
454     {
455         InboundEndpoint in = getTestInboundEndpoint("in", "test://in", null, null, null, connector);
456 
457         // attempts to send/dispatch/request are made on a stopped/stopping connector
458         // This should fail because the connector is not started!
459         try
460         {
461             in.request(1000L);
462             fail("cannot sent on a connector that is not started");
463         }
464         catch (LifecycleException e)
465         {
466             //Expected
467         }
468 
469 
470         assertEquals(0, connector.requesters.getNumIdle());
471 
472         // Dispatcher is not started or connected
473         assertRequesterStartedConnected(in, false, false);
474 
475         connector.start();
476         assertRequesterStartedConnected(in, true, true);
477 
478         assertEquals(1, connector.requesters.getNumIdle());
479 
480         InboundEndpoint in2 = getTestInboundEndpoint("in2", "test://in2", null, null, null, connector);
481         in2.request(1000L);
482 
483 
484         assertEquals(2, connector.requesters.getNumIdle());
485         assertRequesterStartedConnected(in, true, true);
486         assertRequesterStartedConnected(in2, true, true);
487 
488         connector.stop();
489 
490         // Pool is cleared because of implementation of workaround for MULE-4553
491         assertEquals(0, connector.requesters.getNumActive() + connector.requesters.getNumIdle());
492         assertRequesterStartedConnected(in, false, false);
493         assertRequesterStartedConnected(in2, false, false);
494 
495         connector.start();
496         //Between Stop and start the requester pool maintains existing pooled objects
497         assertEquals(2, connector.requesters.getNumActive() + connector.requesters.getNumIdle());
498         assertRequesterStartedConnected(in, true, true);
499         assertRequesterStartedConnected(in2, true, true);
500 
501         in.request(1000L);
502         assertEquals(2, connector.requesters.getNumIdle());
503         assertRequesterStartedConnected(in, true, true);
504 
505         connector.dispose();
506         assertEquals(0, connector.requesters.getNumActive() + connector.requesters.getNumIdle());
507 
508     }
509 
510     @Test
511     public void testRequesterFullLifecycle() throws Exception
512     {
513         InboundEndpoint in = getTestInboundEndpoint("out", "test://out", null, null, null, connector);
514 
515         MessageRequester requester = connector.getRequesterFactory().create(in);
516 
517         requester.initialise();
518         assertTrue(requester.getLifecycleState().isInitialised());
519 
520         requester.connect();
521         assertTrue(requester.isConnected());
522 
523         requester.start();
524         assertTrue(requester.getLifecycleState().isStarted());
525 
526         requester.stop();
527         assertTrue(requester.getLifecycleState().isStopped());
528 
529         requester.disconnect();
530         assertFalse(requester.isConnected());
531 
532         requester.dispose();
533         assertTrue(requester.getLifecycleState().isDisposed());
534 
535     }
536 
537     @Test
538     public void testWorkManagerLifecycle() throws MuleException, WorkException
539     {
540         //ConnectorLifecycleTestCase These are now created in the "initialize" phase
541         //   assertNull(connector.getReceiverWorkManager());
542         //   assertNull(connector.getDispatcherWorkManager());
543         //   assertNull(connector.getRequesterWorkManager());
544 
545         connector.start();
546         assertNotNull(connector.getReceiverWorkManager());
547         assertNotNull(connector.getDispatcherWorkManager());
548         assertNotNull(connector.getRequesterWorkManager());
549         connector.getReceiverWorkManager().doWork(createSomeWork());
550         connector.getDispatcherWorkManager().doWork(createSomeWork());
551         connector.getRequesterWorkManager().doWork(createSomeWork());
552 
553         connector.stop();
554         assertNull(connector.getReceiverWorkManager());
555         assertNull(connector.getDispatcherWorkManager());
556         assertNull(connector.getRequesterWorkManager());
557 
558         connector.start();
559         assertNotNull(connector.getReceiverWorkManager());
560         assertNotNull(connector.getDispatcherWorkManager());
561         assertNotNull(connector.getRequesterWorkManager());
562         connector.getReceiverWorkManager().doWork(createSomeWork());
563         connector.getDispatcherWorkManager().doWork(createSomeWork());
564         connector.getRequesterWorkManager().doWork(createSomeWork());
565 
566         connector.dispose();
567         assertNull(connector.getReceiverWorkManager());
568         assertNull(connector.getDispatcherWorkManager());
569         assertNull(connector.getRequesterWorkManager());
570     }
571 
572     @Test
573     public void testSchedulerLifecycle() throws MuleException, WorkException
574     {
575         assertNull(connector.getScheduler());
576 
577         connector.start();
578         assertFalse(connector.getScheduler().isShutdown());
579         assertFalse(connector.getScheduler().isTerminated());
580 
581         connector.stop();
582         assertNull(connector.getScheduler());
583 
584         connector.start();
585         assertFalse(connector.getScheduler().isShutdown());
586         assertFalse(connector.getScheduler().isTerminated());
587 
588         connector.dispose();
589         assertNull(connector.getScheduler());
590     }
591 
592     protected Work createSomeWork()
593     {
594         return new Work()
595         {
596             public void run()
597             {
598                 System.out.println("I'm doing some work");
599             }
600 
601             public void release()
602             {
603                 // nothing to do
604             }
605         };
606     }
607 
608     private void assertDispatcherStartedConnected(OutboundEndpoint out, boolean started, boolean connected)
609             throws Exception
610     {
611         AbstractMessageDispatcher dispatcher = (AbstractMessageDispatcher) connector.dispatchers.borrowObject(out);
612         assertEquals("Dispatcher started", started, dispatcher.isStarted());
613         assertEquals("Dispatcher connected", connected, dispatcher.isConnected());
614         connector.dispatchers.returnObject(out, dispatcher);
615     }
616 
617     private void assertRequesterStartedConnected(InboundEndpoint in, boolean started, boolean connected)
618             throws Exception
619     {
620         AbstractMessageRequester requester = (AbstractMessageRequester) connector.requesters.borrowObject(in);
621         assertEquals("Requester started", started, requester.isStarted());
622         assertEquals("requester connected", connected, requester.isConnected());
623         connector.requesters.returnObject(in, requester);
624     }
625 }