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.routing.outbound;
8   
9   import org.mule.MessageExchangePattern;
10  import org.mule.api.MessagingException;
11  import org.mule.api.MuleEvent;
12  import org.mule.api.MuleException;
13  import org.mule.api.MuleMessage;
14  import org.mule.api.processor.MessageProcessor;
15  import org.mule.api.routing.OutboundRouterCollection;
16  import org.mule.api.service.Service;
17  import org.mule.api.transformer.Transformer;
18  import org.mule.api.transformer.TransformerException;
19  import org.mule.api.transport.OutputHandler;
20  import org.mule.component.simple.PassThroughComponent;
21  import org.mule.model.seda.SedaModel;
22  import org.mule.model.seda.SedaService;
23  import org.mule.tck.junit4.AbstractMuleContextTestCase;
24  import org.mule.transformer.AbstractTransformer;
25  
26  import java.io.IOException;
27  import java.io.OutputStream;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
32  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
33  import org.junit.Test;
34  
35  import static org.junit.Assert.assertNotSame;
36  import static org.junit.Assert.assertSame;
37  import static org.junit.Assert.assertTrue;
38  import static org.junit.Assert.fail;
39  
40  public class DefaultOutboundRouterCollectionTestCase extends AbstractMuleContextTestCase
41  {
42      public DefaultOutboundRouterCollectionTestCase()
43      {
44          setStartContext(true);
45      }
46  
47      private static int LATCH_AWAIT_TIMEOUT_MS = 1000;
48      private Service testService;
49      private MuleEvent testEvent;
50      static MuleMessage originalMessage;
51      private TestOutboundRouterCollection outboundRouter;
52  
53      @Override
54      protected void doSetUp() throws Exception
55      {
56          super.doSetUp();
57          testEvent = getTestEvent("TEST_MESSAGE", getTestInboundEndpoint(MessageExchangePattern.REQUEST_RESPONSE));
58          testService = createService();
59          outboundRouter = new TestOutboundRouterCollection();
60          testService.setOutboundMessageProcessor(outboundRouter);
61          outboundRouter.setMuleContext(muleContext);
62          muleContext.getRegistry().registerService(testService);
63      }
64  
65      protected Service createService() throws MuleException
66      {
67          SedaModel model = new SedaModel();
68          muleContext.getRegistry().registerModel(model);
69          Service service = new SedaService(muleContext);
70          service.setName("test");
71          service.setComponent(new PassThroughComponent());
72          service.setModel(model);
73          return service;
74      }
75  
76      /**
77       * If there is just one outbound router we don't need to do any copying at all
78       * regardless of if matchAll is true or not or if the router mutates the message
79       * in isMatch or not . The outbound phase already has a new message copy.
80       * @throws Exception if the test fails!
81       */
82      @Test
83      public void testSingleDoesNotRequireCopyRouterMatchAllFalse() throws Exception
84      {
85          getOutboundRouterCollection().setMatchAll(false);
86          getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
87          TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(1);
88  
89          testService.sendEvent(testEvent);
90  
91          assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
92              TimeUnit.MILLISECONDS));
93      }
94  
95      /**
96       * If there is just one outbound router we don't need to do any copying at all
97       * regardless of if matchAll is true or not or if the router mutates the message
98       * in isMatch or not . The outbound phase already has a new message copy.
99       * @throws Exception if the test fails!
100      */
101     @Test
102     public void testSingleDoesNotRequireCopyRouterMatchAllTrue() throws Exception
103     {
104 
105         MuleEvent testEvent = getTestInboundEvent("TEST_MESSAGE");
106         getOutboundRouterCollection().setMatchAll(true);
107         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
108 
109         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(1);
110 
111         testService.sendEvent(testEvent);
112 
113         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
114             TimeUnit.MILLISECONDS));
115     }
116 
117     /**
118      * If there is just one outbound router we don't need to do any copying at all
119      * regardless of if matchAll is true or not or if the router mutates the message
120      * in isMatch or not . The outbound phase already has a new message copy.
121      * @throws Exception if the test fails!
122      */
123     @Test
124     public void testSingleRequiresCopyRouterMatchAllFalse() throws Exception
125     {
126         getOutboundRouterCollection().setMatchAll(false);
127         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
128 
129         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(1);
130 
131         testService.sendEvent(testEvent);
132 
133         assertTrue(TestRequiresNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
134             TimeUnit.MILLISECONDS));
135     }
136 
137     /**
138      * If there is just one outbound router we don't need to do any copying at all
139      * regardless of if matchAll is true or not or if the router mutates the message
140      * in isMatch or not . The outbound phase already has a new message copy.
141      * @throws Exception if the test fails!
142      */
143     @Test
144     public void testSingleRequiresCopyRouterMatchAllTrue() throws Exception
145     {
146         getOutboundRouterCollection().setMatchAll(true);
147         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
148 
149         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(1);
150 
151         testService.sendEvent(testEvent);
152 
153         assertTrue(TestRequiresNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
154             TimeUnit.MILLISECONDS));
155     }
156 
157     // MULTIPLE
158 
159     /**
160      * If there are multiple outbound routers but matchAll is false then we only need
161      * to copy message if the router might mutate it in isMatch, if not then no need
162      * to copy.
163      * @throws Exception if the test fails!
164      */
165     @Test
166     public void testMultipleDoesNotRequireCopyRouterMatchAllFalse() throws Exception
167     {
168         getOutboundRouterCollection().setMatchAll(false);
169         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
170         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
171         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
172 
173         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(3);
174 
175         testService.sendEvent(testEvent);
176 
177         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
178             TimeUnit.MILLISECONDS));
179     }
180 
181     /**
182      * If there are multiple outbound routers and matchAll is true then we need a new
183      * message copy for all but the *last* router independent of whether the routers
184      * may mutate the message in isMatch or not. See MULE- 4352.
185      * @throws Exception if the test fails!
186      */
187     @Test
188     public void testMultipleDoesNotRequireCopyRouterMatchAllTrue() throws Exception
189     {
190 
191         MuleEvent testEvent = getTestInboundEvent("TEST_MESSAGE");
192         getOutboundRouterCollection().setMatchAll(true);
193         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(true));
194         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(true));
195         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
196 
197         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(3);
198 
199         testService.sendEvent(testEvent);
200 
201         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
202             TimeUnit.MILLISECONDS));
203     }
204 
205     /**
206      * If there are multiple outbound routers and matchAll is false then we need a
207      * new message copy for all but the *last* router that may mutate the message in
208      * isMatch.
209      * @throws Exception if the test fails!
210      */
211     @Test
212     public void testMultipleRequiresCopyRouterMatchAllFalse() throws Exception
213     {
214         getOutboundRouterCollection().setMatchAll(false);
215         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
216         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
217         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
218 
219         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(3);
220 
221         testService.sendEvent(testEvent);
222 
223         assertTrue(TestRequiresNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
224             TimeUnit.MILLISECONDS));
225     }
226 
227     /**
228      * If there are multiple outbound routers and matchAll is true then we need a new
229      * message copy for all but the *last* router independent of whether the routers
230      * may mutate the message in isMatch or not. See MULE- 4352.
231      * @throws Exception if the test fails!
232      */
233     @Test
234     public void testMultipleRequiresCopyRouterMatchAllTrue() throws Exception
235     {
236         getOutboundRouterCollection().setMatchAll(true);
237         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
238         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
239         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
240 
241         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(3);
242 
243         testService.sendEvent(testEvent);
244 
245         assertTrue(TestRequiresNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
246             TimeUnit.MILLISECONDS));
247     }
248 
249     // MIX
250 
251     /**
252      * If matchAll is true then we need a new message copy for each and every router except the last one.
253      * @throws Exception if the test fails!
254      */
255     @Test
256     public void testMultipleMixMatchAllTrue() throws Exception
257     {
258         getOutboundRouterCollection().setMatchAll(true);
259         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
260         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(true));
261         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
262         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(true));
263         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
264 
265         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(3);
266         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(2);
267 
268         testService.sendEvent(testEvent);
269 
270         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
271             TimeUnit.MILLISECONDS));
272         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
273             TimeUnit.MILLISECONDS));
274     }
275 
276     /**
277      * If matchAll is false then we need a new message copy for each router that may
278      * mutate the message in isMatch unless it is the last router.
279      * @throws Exception if the test fails!
280      */
281     @Test
282     public void testMultipleMixMatchAllFalse() throws Exception
283     {
284         getOutboundRouterCollection().setMatchAll(false);
285         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
286         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
287         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
288         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(true));
289         getOutboundRouterCollection().addRoute(new TestDoesNotRequireNewMessageOutboundRouter(false));
290         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
291 
292         TestDoesNotRequireNewMessageOutboundRouter.latch = new CountDownLatch(3);
293         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(3);
294 
295         testService.sendEvent(testEvent);
296 
297         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
298             TimeUnit.MILLISECONDS));
299         assertTrue(TestDoesNotRequireNewMessageOutboundRouter.latch.await(LATCH_AWAIT_TIMEOUT_MS,
300             TimeUnit.MILLISECONDS));
301     }
302 
303     /**
304      * If the message is a stream and message copying is required due to any of the
305      * scenarios tested above then an exception should be thrown as the stream
306      * payload cannot be copied.
307      * @throws Exception if the test fails!
308      */
309     @Test
310     public void testStreamPayload() throws Exception
311     {
312         getOutboundRouterCollection().setMatchAll(true);
313         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
314         getOutboundRouterCollection().addRoute(new TestRequiresNewMessageOutboundRouter(false));
315 
316         TestRequiresNewMessageOutboundRouter.latch = new CountDownLatch(2);
317 
318         testEvent.getMessage().setPayload(new OutputHandler()
319         {
320             public void write(MuleEvent event, OutputStream out) throws IOException
321             {
322                 // do nothing
323             }
324         });
325         try
326         {
327             testService.sendEvent(testEvent);
328             fail("Exception was expected");
329         }
330         catch (MessagingException e)
331         {
332             // expected 
333         }
334     }
335 
336     private OutboundRouterCollection getOutboundRouterCollection()
337     {
338         return (OutboundRouterCollection) testService.getOutboundMessageProcessor();
339     }
340     
341     private static class TestRequiresNewMessageOutboundRouter extends OutboundPassThroughRouter
342     {
343         static CountDownLatch latch;
344         private boolean expectCopy;
345 
346         public TestRequiresNewMessageOutboundRouter(boolean expectCopy)
347         {
348             this.expectCopy = expectCopy;
349             List<Transformer> transformers = new ArrayList<Transformer>();
350             transformers.add(new AbstractTransformer() 
351             {
352                 @Override
353                 public Object doTransform(Object src, String encoding) throws TransformerException
354                 {
355                     return src;
356                 }
357             });
358             setTransformers(transformers);
359         }
360 
361         @Override
362         public List<MessageProcessor> getRoutes()
363         {
364             List<MessageProcessor> list = new ArrayList<MessageProcessor>();
365             try
366             {
367                 list.add(getTestOutboundEndpoint("out", "test://out"));
368             }
369             catch (Exception e)
370             {
371                 fail(e.getMessage());
372             }
373             return list;
374         }
375 
376         @Override
377         public boolean isMatch(MuleMessage message) throws MuleException
378         {
379             if (expectCopy)
380             {
381                 assertNotSame(originalMessage, message);
382             }
383             else
384             {
385                 assertSame(originalMessage, message);
386             }
387             latch.countDown();
388             return false;
389         }
390     }
391 
392     private static class TestDoesNotRequireNewMessageOutboundRouter extends OutboundPassThroughRouter
393     {
394         static CountDownLatch latch;
395         private boolean expectCopy;
396 
397         public TestDoesNotRequireNewMessageOutboundRouter(boolean expectCopy)
398         {
399             this.expectCopy = expectCopy;
400         }
401 
402         @Override
403         public List<MessageProcessor> getRoutes()
404         {
405             List<MessageProcessor> list = new ArrayList<MessageProcessor>();
406             try
407             {
408                 list.add(getTestOutboundEndpoint("out", "test://out"));
409             }
410             catch (Exception e)
411             {
412                 fail(e.getMessage());
413             }
414             return list;
415         }
416 
417         @Override
418         public boolean isMatch(MuleMessage message) throws MuleException
419         {
420             if (expectCopy)
421             {
422                 assertNotSame(originalMessage, message);
423             }
424             else
425             {
426                 assertSame(originalMessage, message);
427 
428             }
429             latch.countDown();
430             return false;
431         }
432     }
433 
434     private static class TestOutboundRouterCollection extends DefaultOutboundRouterCollection
435     {
436         public TestOutboundRouterCollection()
437         {
438             super();
439         }
440 
441         @Override
442         public MuleEvent process(MuleEvent event) throws MessagingException
443         {
444             originalMessage = event.getMessage();
445             return super.process(event);
446         }
447     }
448 }