View Javadoc

1   /*
2    * $Id: DispatcherThreadingProfileTestCase.java 22387 2011-07-12 03:53:36Z 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.transport;
12  
13  import org.mule.MessageExchangePattern;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.config.ThreadingProfile;
17  import org.mule.api.endpoint.OutboundEndpoint;
18  import org.mule.api.transport.DispatchException;
19  import org.mule.api.transport.MessageDispatcher;
20  import org.mule.config.ImmutableThreadingProfile;
21  import org.mule.tck.junit4.AbstractMuleContextTestCase;
22  import org.mule.tck.testmodels.mule.TestConnector;
23  import org.mule.tck.testmodels.mule.TestMessageDispatcher;
24  import org.mule.tck.testmodels.mule.TestMessageDispatcherFactory;
25  
26  import java.util.concurrent.CountDownLatch;
27  import java.util.concurrent.TimeUnit;
28  import java.util.concurrent.atomic.AtomicInteger;
29  import org.junit.Test;
30  
31  import static org.junit.Assert.assertEquals;
32  import static org.junit.Assert.assertTrue;
33  
34  /**
35   * This test case tests the both dispatcher threading profile and it's rejection
36   * handlers and AbstractConnector dispatch logic by dispatch events using
37   * TestConnector with varying threading profile configurations and asserting the
38   * correct outcome. See: MULE-4752
39   */
40  public class DispatcherThreadingProfileTestCase extends AbstractMuleContextTestCase
41  {
42  
43      public static int DELAY_TIME = 500;
44      public static int WAIT_TIME = DELAY_TIME + DELAY_TIME / 4;
45      public static int SERIAL_WAIT_TIME = (DELAY_TIME * 2) + DELAY_TIME / 4;
46      public static int LONGER_WAIT_TIME = DELAY_TIME * 5;
47      private CountDownLatch latch;
48      private AtomicInteger counter = new AtomicInteger();
49  
50      public DispatcherThreadingProfileTestCase()
51      {
52          setStartContext(true);
53      }
54  
55      @Override
56      protected void doTearDown() throws Exception
57      {
58          super.doTearDown();
59          counter.set(0);
60      }
61  
62      @Test
63      public void testDefaultThreadingProfileConfiguration() throws MuleException
64      {
65          TestConnector connector = new TestConnector(muleContext);
66          muleContext.getRegistry().registerConnector(connector);
67          assertEquals(ThreadingProfile.DEFAULT_MAX_THREADS_ACTIVE, connector.getDispatcherThreadingProfile()
68              .getMaxThreadsActive());
69          assertEquals(ThreadingProfile.DEFAULT_MAX_THREADS_IDLE, connector.getDispatcherThreadingProfile()
70              .getMaxThreadsIdle());
71          assertEquals(ThreadingProfile.WHEN_EXHAUSTED_RUN, connector.getDispatcherThreadingProfile()
72              .getPoolExhaustedAction());
73          assertEquals(ThreadingProfile.DEFAULT_MAX_BUFFER_SIZE, connector.getDispatcherThreadingProfile()
74              .getMaxBufferSize());
75          assertEquals(ThreadingProfile.DEFAULT_MAX_THREAD_TTL, connector.getDispatcherThreadingProfile()
76              .getThreadTTL());
77          assertEquals(ThreadingProfile.DEFAULT_THREAD_WAIT_TIMEOUT, connector.getDispatcherThreadingProfile()
78              .getThreadWaitTimeout());
79      }
80  
81      @Test
82      public void testDefaultRunExhaustedAction() throws Exception
83      {
84          // Default is RUN.
85          // To concurrent dispatch operations are possible even with
86          // maxActiveThreads=1.
87          // Note: This also tests the fact with RUN here, the dispatcher pool needs to
88          // GROW on demand.
89          latch = new CountDownLatch(2);
90  
91          createTestConnectorWithSingleDispatcherThread(ThreadingProfile.WHEN_EXHAUSTED_RUN);
92          dispatchTwoAsyncEvents();
93  
94          // Both execute complete at the same time and finish shortly after DELAY_TIME
95          assertTrue(latch.await(WAIT_TIME, TimeUnit.MILLISECONDS));
96      }
97  
98      @Test
99      public void testWaitExhaustedAction() throws Exception
100     {
101         // Second job waits in workQueue for first job to complete.
102         latch = new CountDownLatch(2);
103 
104         createTestConnectorWithSingleDispatcherThread(1, ThreadingProfile.WHEN_EXHAUSTED_WAIT,
105             ThreadingProfile.DEFAULT_THREAD_WAIT_TIMEOUT, ThreadingProfile.DEFAULT_MAX_BUFFER_SIZE);
106         dispatchTwoAsyncEvents();
107 
108         // Both execute in serial as the second job wait for the fist job to complete
109         assertTrue(latch.await(SERIAL_WAIT_TIME, TimeUnit.MILLISECONDS));
110     }
111 
112     @Test
113     public void testWaitTimeoutExhaustedAction() throws Exception
114     {
115         // Second job attempts to wait in workQueue but waiting job times out/
116         latch = new CountDownLatch(1);
117 
118         createTestConnectorWithSingleDispatcherThread(ThreadingProfile.WHEN_EXHAUSTED_WAIT);
119         dispatchTwoAsyncEvents();
120 
121         // The job that executes finishes shortly after DELAY_TIME
122         assertTrue(latch.await(WAIT_TIME, TimeUnit.MILLISECONDS));
123 
124         // Wait even longer and ensure the other message isn't executed.
125         Thread.sleep(LONGER_WAIT_TIME);
126         assertEquals(1, counter.get());
127     }
128 
129     @Test
130     public void testAbortExhaustedAction() throws Exception
131     {
132         // Second job is aborted
133         latch = new CountDownLatch(1);
134 
135         createTestConnectorWithSingleDispatcherThread(ThreadingProfile.WHEN_EXHAUSTED_ABORT);
136         dispatchTwoAsyncEvents();
137 
138         // The job that executes finishes shortly after DELAY_TIME
139         assertTrue(latch.await(WAIT_TIME, TimeUnit.MILLISECONDS));
140 
141         // Wait even longer and ensure the other message isn't executed.
142         Thread.sleep(LONGER_WAIT_TIME);
143         assertEquals(1, counter.get());
144     }
145 
146     @Test
147     public void testDiscardExhaustedAction() throws Exception
148     {
149         // Second job is discarded
150         latch = new CountDownLatch(1);
151 
152         createTestConnectorWithSingleDispatcherThread(ThreadingProfile.WHEN_EXHAUSTED_DISCARD);
153         dispatchTwoAsyncEvents();
154 
155         // The job that executes finishes shortly after DELAY_TIME
156         assertTrue(latch.await(WAIT_TIME, TimeUnit.MILLISECONDS));
157 
158         // Wait even longer and ensure the other message isn't executed.
159         Thread.sleep(LONGER_WAIT_TIME);
160         assertEquals(1, counter.get());
161     }
162 
163     @Test
164     public void testDiscardOldestExhaustedAction() throws Exception
165     {
166         // The third job is discarded when the fourth job is submitted
167         // The fourth job (now third) is discarded when the fifth job is submitted.
168         // The fifth job (now third) is discarded when the sixth job is submitted.
169         // Therefore the first, second and sixth jobs are run
170         latch = new CountDownLatch(3);
171 
172         // In order for a LinkedBlockingDeque to be used rather than a
173         // SynchronousQueue there need to be
174         // i) 2+ maxActiveThreads ii) maxBufferSize>0
175         createTestConnectorWithSingleDispatcherThread(2, ThreadingProfile.WHEN_EXHAUSTED_DISCARD_OLDEST,
176             ThreadingProfile.DEFAULT_THREAD_WAIT_TIMEOUT, 1);
177 
178         dispatchTwoAsyncEvents();
179         dispatchTwoAsyncEvents();
180         dispatchTwoAsyncEvents();
181 
182         assertTrue(latch.await(SERIAL_WAIT_TIME, TimeUnit.MILLISECONDS));
183         Thread.sleep(LONGER_WAIT_TIME);
184         assertEquals(3, counter.get());
185     }
186 
187     protected void createTestConnectorWithSingleDispatcherThread(int exhaustedAction) throws MuleException
188     {
189         createTestConnectorWithSingleDispatcherThread(1, exhaustedAction, 1, 1);
190     }
191 
192     protected void createTestConnectorWithSingleDispatcherThread(int threads,
193                                                                  int exhaustedAction,
194                                                                  long waitTimeout,
195                                                                  int maxBufferSize) throws MuleException
196     {
197         TestConnector connector = new TestConnector(muleContext);
198         ThreadingProfile threadingProfile = new ImmutableThreadingProfile(threads, threads, maxBufferSize,
199             ThreadingProfile.DEFAULT_MAX_THREAD_TTL, waitTimeout, exhaustedAction, true, null, null);
200         threadingProfile.setMuleContext(muleContext);
201         connector.setDispatcherThreadingProfile(threadingProfile);
202         muleContext.getRegistry().registerConnector(connector);
203         connector.setDispatcherFactory(new DelayTestMessageDispatcherFactory());
204     }
205 
206     private void dispatchTwoAsyncEvents() throws DispatchException, Exception
207     {
208         OutboundEndpoint endpoint = muleContext.getEndpointFactory().getOutboundEndpoint(
209             "test://test");
210         endpoint.process(getTestEvent("data", getTestInboundEndpoint(MessageExchangePattern.ONE_WAY)));
211         endpoint.process(getTestEvent("data", getTestInboundEndpoint(MessageExchangePattern.ONE_WAY)));
212     }
213 
214     public class DelayTestMessageDispatcher extends TestMessageDispatcher
215     {
216         public DelayTestMessageDispatcher(OutboundEndpoint endpoint)
217         {
218             super(endpoint);
219         }
220 
221         @Override
222         protected void doDispatch(MuleEvent event) throws Exception
223         {
224             super.doDispatch(event);
225             Thread.sleep(DELAY_TIME);
226             counter.incrementAndGet();
227             latch.countDown();
228         }
229     }
230 
231     class DelayTestMessageDispatcherFactory extends TestMessageDispatcherFactory
232     {
233         @Override
234         public MessageDispatcher create(OutboundEndpoint endpoint) throws MuleException
235         {
236             return new DelayTestMessageDispatcher(endpoint);
237         }
238     }
239 
240 }