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