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.module.spring.events;
8   
9   import org.mule.api.MuleEventContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.MuleMessage;
12  import org.mule.module.client.MuleClient;
13  import org.mule.tck.functional.EventCallback;
14  import org.mule.tck.junit4.FunctionalTestCase;
15  import org.mule.transformer.AbstractMessageTransformer;
16  import org.mule.util.ExceptionUtils;
17  import org.mule.util.concurrent.Latch;
18  
19  import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
20  import edu.emory.mathcs.backport.java.util.concurrent.Executors;
21  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
22  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
23  import org.junit.Test;
24  import org.springframework.context.ApplicationContext;
25  import org.springframework.context.ApplicationEvent;
26  import org.springframework.context.event.ContextRefreshedEvent;
27  import org.springframework.context.support.AbstractApplicationContext;
28  
29  import static org.junit.Assert.assertEquals;
30  import static org.junit.Assert.assertNotNull;
31  import static org.junit.Assert.assertNull;
32  import static org.junit.Assert.assertTrue;
33  import static org.junit.Assert.fail;
34  
35  public class SpringEventsTestCase extends FunctionalTestCase
36  {
37  
38      protected static final int DEFAULT_LATCH_TIMEOUT = 10000;
39  
40      private static final int NUMBER_OF_MESSAGES = 10;
41      volatile AtomicInteger eventCounter1;
42      volatile AtomicInteger eventCounter2;
43  
44      @Override
45      protected void doSetUp() throws Exception
46      {
47          super.doSetUp();
48          eventCounter1 = new AtomicInteger(0);
49          eventCounter2 = new AtomicInteger(0);
50      }
51  
52      @Override
53      protected String getConfigResources()
54      {
55          return "mule-events-app-context.xml";
56      }
57  
58      @Test
59      public void testManagerIsInstanciated() throws Exception
60      {
61          assertTrue(muleContext.isInitialised());
62          assertTrue(muleContext.isStarted());
63          assertNotNull(muleContext.getRegistry().lookupObject(
64              AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME));
65      }
66  
67      @Test
68      public void testRemovingListeners() throws Exception
69      {
70          TestSubscriptionEventBean subscriptionBean = (TestSubscriptionEventBean) muleContext.getRegistry()
71              .lookupObject("testSubscribingEventBean1");
72          assertNotNull(subscriptionBean);
73          MuleEventMulticaster multicaster = (MuleEventMulticaster) muleContext.getRegistry().lookupObject(
74              AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME);
75          assertNotNull(multicaster);
76  
77          Latch whenFinished = new Latch();
78          subscriptionBean.setEventCallback(new CountingEventCallback(eventCounter1, 1, whenFinished));
79  
80          multicaster.removeApplicationListener(subscriptionBean);
81          MuleClient client = new MuleClient(muleContext);
82          client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
83  
84          assertEquals(0, eventCounter1.get());
85  
86          multicaster.addApplicationListener(subscriptionBean);
87          client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
88  
89          assertTrue(whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
90          assertEquals(1, eventCounter1.get());
91          eventCounter1.set(0);
92  
93          multicaster.removeAllListeners();
94          client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
95  
96          assertEquals(0, eventCounter1.get());
97          multicaster.addApplicationListener(subscriptionBean);
98          // context.refresh();
99          subscriptionBean.setEventCallback(null);
100     }
101 
102     @Test
103     public void testReceivingANonSubscriptionMuleEvent() throws Exception
104     {
105         TestMuleEventBean bean = (TestMuleEventBean) muleContext.getRegistry().lookupObject(
106             "testNonSubscribingMuleEventBean");
107         assertNotNull(bean);
108 
109         // register a callback
110         Latch whenFinished = new Latch();
111         bean.setEventCallback(new CountingEventCallback(eventCounter1, 1, whenFinished));
112 
113         MuleClient client = new MuleClient(muleContext);
114         client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
115 
116         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
117         assertEquals(1, eventCounter1.get());
118     }
119 
120     @Test
121     public void testReceivingASpringEvent() throws Exception
122     {
123         TestApplicationEventBean bean = (TestApplicationEventBean) muleContext.getRegistry().lookupObject(
124             "testEventSpringBean");
125         assertNotNull(bean);
126 
127         final Latch whenFinished = new Latch();
128         EventCallback callback = new EventCallback()
129         {
130             public void eventReceived(MuleEventContext context, Object o) throws Exception
131             {
132                 assertNull(context);
133                 if (o instanceof TestApplicationEvent)
134                 {
135                     if (eventCounter1.incrementAndGet() == 1)
136                     {
137                         whenFinished.countDown();
138                     }
139                 }
140             }
141         };
142 
143         bean.setEventCallback(callback);
144 
145         ApplicationContext context = ((MuleEventMulticaster) muleContext.getRegistry().lookupObject(
146             "applicationEventMulticaster")).applicationContext;
147         context.publishEvent(new TestApplicationEvent(context));
148 
149         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
150         assertEquals(1, eventCounter1.get());
151     }
152 
153     @Test
154     public void testReceivingAllEvents() throws Exception
155     {
156         TestAllEventBean bean = (TestAllEventBean) muleContext.getRegistry().lookupObject("testAllEventBean");
157         assertNotNull(bean);
158 
159         Latch whenFinished = new Latch();
160         bean.setEventCallback(new CountingEventCallback(eventCounter1, 2, whenFinished));
161 
162         MuleClient client = new MuleClient(muleContext);
163         client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
164         ApplicationContext context = ((MuleEventMulticaster) muleContext.getRegistry().lookupObject(
165             "applicationEventMulticaster")).applicationContext;
166         context.publishEvent(new TestApplicationEvent(context));
167 
168         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
169         assertEquals(2, eventCounter1.get());
170     }
171 
172     @Test
173     public void testReceivingASubscriptionEvent() throws Exception
174     {
175         TestSubscriptionEventBean subscriptionBean = (TestSubscriptionEventBean) muleContext.getRegistry()
176             .lookupObject("testSubscribingEventBean1");
177         assertNotNull(subscriptionBean);
178 
179         Latch whenFinished = new Latch();
180         subscriptionBean.setEventCallback(new CountingEventCallback(eventCounter1, 1, whenFinished));
181 
182         MuleClient client = new MuleClient(muleContext);
183         client.send("vm://event.multicaster", "Test Spring MuleEvent", null);
184 
185         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
186         assertEquals(1, eventCounter1.get());
187     }
188 
189     @Test
190     public void testReceiveAndPublishEvent() throws Exception
191     {
192         TestSubscriptionEventBean bean1 = (TestSubscriptionEventBean) muleContext.getRegistry().lookupObject(
193             "testSubscribingEventBean1");
194         assertNotNull(bean1);
195 
196         final Latch whenFinished1 = new Latch();
197         EventCallback callback = new EventCallback()
198         {
199             public void eventReceived(MuleEventContext context, Object o) throws Exception
200             {
201                 MuleApplicationEvent returnEvent = new MuleApplicationEvent("MuleEvent from a spring bean",
202                     "vm://testBean2");
203                 MuleApplicationEvent e = (MuleApplicationEvent) o;
204                 e.getApplicationContext().publishEvent(returnEvent);
205                 if (eventCounter1.incrementAndGet() == NUMBER_OF_MESSAGES)
206                 {
207                     whenFinished1.countDown();
208                 }
209             }
210         };
211         bean1.setEventCallback(callback);
212 
213         TestSubscriptionEventBean bean2 = (TestSubscriptionEventBean) muleContext.getRegistry().lookupObject(
214             "testSubscribingEventBean2");
215         assertNotNull(bean2);
216 
217         Latch whenFinished2 = new Latch();
218         bean2.setEventCallback(new CountingEventCallback(eventCounter2, NUMBER_OF_MESSAGES, whenFinished2));
219 
220         // send asynchronously
221         this.doSend("vm://event.multicaster", "Test Spring MuleEvent", NUMBER_OF_MESSAGES);
222 
223         whenFinished1.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
224         whenFinished2.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
225         assertEquals(NUMBER_OF_MESSAGES, eventCounter1.get());
226         assertEquals(NUMBER_OF_MESSAGES, eventCounter2.get());
227     }
228 
229     @Test
230     public void testPublishOnly() throws Exception
231     {
232         final MuleApplicationEvent event = new MuleApplicationEvent("MuleEvent from a spring bean",
233             "vm://testBean2");
234 
235         TestSubscriptionEventBean bean2 = (TestSubscriptionEventBean) muleContext.getRegistry().lookupObject(
236             "testSubscribingEventBean2");
237         assertNotNull(bean2);
238 
239         Latch whenFinished = new Latch();
240         bean2.setEventCallback(new CountingEventCallback(eventCounter1, NUMBER_OF_MESSAGES, whenFinished));
241 
242         // publish asynchronously
243         this.doPublish(event, NUMBER_OF_MESSAGES);
244 
245         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
246         assertEquals(NUMBER_OF_MESSAGES, eventCounter1.get());
247     }
248 
249     @Test
250     public void testPublishWithEventAwareTransformer() throws Exception
251     {
252         CountDownLatch transformerLatch = new CountDownLatch(1);
253 
254         TestEventAwareTransformer trans = new TestEventAwareTransformer();
255         trans.setLatch(transformerLatch);
256         muleContext.getRegistry().registerTransformer(trans);
257 
258         MuleApplicationEvent event = new MuleApplicationEvent("MuleEvent from a spring bean",
259             "vm://testBean2?transformers=dummyTransformer");
260 
261         TestSubscriptionEventBean bean2 = (TestSubscriptionEventBean) muleContext.getRegistry().lookupObject(
262             "testSubscribingEventBean2");
263         assertNotNull(bean2);
264 
265         Latch whenFinished = new Latch();
266         bean2.setEventCallback(new CountingEventCallback(eventCounter1, 1, whenFinished));
267 
268         // publish asynchronously
269         this.doPublish(event, 1);
270 
271         whenFinished.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
272         assertTrue(transformerLatch.await(DEFAULT_LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
273         assertEquals(1, eventCounter1.get());
274     }
275 
276     // asynchronously publish the given event to the ApplicationContext for 'count' number of times
277     protected void doPublish(final ApplicationEvent event, final int count)
278     {
279         Runnable publisher = new Runnable()
280         {
281             public void run()
282             {
283                 for (int i = 0; i < count; i++)
284                 {
285                     ApplicationContext context = null;
286                     try
287                     {
288                         context = ((MuleEventMulticaster) muleContext.getRegistry().lookupObject(
289                             "applicationEventMulticaster")).applicationContext;
290                         context.publishEvent(event);
291                     }
292                     catch (IllegalArgumentException e)
293                     {
294                         // TODO Auto-generated catch block
295                         e.printStackTrace();
296                     }
297                     catch (SecurityException e)
298                     {
299                         // TODO Auto-generated catch block
300                         e.printStackTrace();
301                     }
302                 }
303             }
304         };
305 
306         Executors.newSingleThreadExecutor().execute(publisher);
307     }
308 
309     // asynchronously send the payload to the given Mule URL for 'count' number of times
310     protected void doSend(final String url, final Object payload, final int count)
311     {
312         Runnable sender = new Runnable()
313         {
314             public void run()
315             {
316                 try
317                 {
318                     MuleClient client = new MuleClient(muleContext);
319                     for (int i = 0; i < count; i++)
320                     {
321                         client.send(url, payload, null);
322                     }
323                 }
324                 catch (MuleException ex)
325                 {
326                     fail(ExceptionUtils.getStackTrace(ex));
327                 }
328             }
329         };
330 
331         // execute in background
332         Executors.newSingleThreadExecutor().execute(sender);
333     }
334 
335     /*
336      * This callback counts how many times an MuleEvent was received. If a maximum
337      * number has been reached, the given CountDownLatch is counted down. When
338      * passing in a Latch (CountDownLatch(1)) this acts just like a sempahore for the
339      * caller.
340      */
341     public static class CountingEventCallback implements EventCallback
342     {
343         private final AtomicInteger counter;
344         private final int maxCount;
345         private final CountDownLatch finished;
346 
347         public CountingEventCallback(AtomicInteger counter, int maxCount, CountDownLatch whenFinished)
348         {
349             super();
350             this.counter = counter;
351             this.maxCount = maxCount;
352             this.finished = whenFinished;
353         }
354 
355         public void eventReceived(MuleEventContext context, Object o) throws Exception
356         {
357             // apparently beans get an extra ContextRefreshedEvent during
358             // startup;
359             // this messes up our event counts.
360             if (!(o instanceof ContextRefreshedEvent))
361             {
362                 if (counter.incrementAndGet() == maxCount && finished != null)
363                 {
364                     finished.countDown();
365                 }
366             }
367         }
368     }
369 
370     /*
371      * A simple Transformer that counts down a Latch to indicate that it has been
372      * called.
373      */
374     public static class TestEventAwareTransformer extends AbstractMessageTransformer
375     {
376         private CountDownLatch latch;
377 
378         public TestEventAwareTransformer()
379         {
380             this.setName("dummyTransformer");
381         }
382 
383         @Override
384         public Object clone() throws CloneNotSupportedException
385         {
386             TestEventAwareTransformer clone = (TestEventAwareTransformer) super.clone();
387             // we MUST share the latch for this test since we obviously want to
388             // wait
389             // for it.
390             clone.setLatch(latch);
391             return clone;
392         }
393 
394         public CountDownLatch getLatch()
395         {
396             return latch;
397         }
398 
399         public void setLatch(CountDownLatch latch)
400         {
401             this.latch = latch;
402         }
403 
404         @Override
405         public Object transformMessage(MuleMessage message, String outputEncoding)
406         {
407             assertNotNull(message);
408 
409             if (latch != null)
410             {
411                 latch.countDown();
412             }
413 
414             return message;
415         }
416     }
417 
418     /*
419      * A simple custom ApplicationEvent for sending
420      */
421     public static class TestApplicationEvent extends ApplicationEvent
422     {
423         private static final long serialVersionUID = 1L;
424 
425         public TestApplicationEvent(Object source)
426         {
427             super(source);
428         }
429     }
430 
431 }