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