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.context.notification;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.context.MuleContextAware;
11  import org.mule.api.context.WorkManager;
12  import org.mule.api.context.notification.BlockingServerEvent;
13  import org.mule.api.context.notification.ServerNotification;
14  import org.mule.api.context.notification.ServerNotificationHandler;
15  import org.mule.api.context.notification.ServerNotificationListener;
16  import org.mule.api.lifecycle.Disposable;
17  import org.mule.api.lifecycle.LifecycleException;
18  import org.mule.util.ClassUtils;
19  
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import javax.resource.spi.work.Work;
26  import javax.resource.spi.work.WorkException;
27  import javax.resource.spi.work.WorkListener;
28  
29  import edu.emory.mathcs.backport.java.util.Queue;
30  import edu.emory.mathcs.backport.java.util.concurrent.BlockingDeque;
31  import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingDeque;
32  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
33  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  /**
38   * A reworking of the event manager that allows efficient behaviour without global on/off
39   * switches in the config.
40   *
41   * <p>The configuration and resulting policy are separate; the policy
42   * is a summary of the configuration that contains information to decide whether a particular
43   * message can be handled, and which updates that with experience gained handling messages.
44   * When the configuration is changed the policy is rebuilt.  In this way we get a fairly
45   * efficient system without needing controls elsewhere.
46   *
47   * <p>However, measurements showed that there was still a small impact on speed in some
48   * cases.  To improve behaviour further the
49   * {@link org.mule.context.notification.OptimisedNotificationHandler} was
50   * added.  This allows a service that generates notifications to cache locally a handler
51   * optimised for a particular class.
52   *
53   * <p>The dynamic flag stops this caching from occurring.  This reduces efficiency slightly
54   * (about 15% cost on simple VM messages, less on other transports)</p>
55   *
56   * <p>Note that, because of subclass relationships, we need to be very careful about exactly
57   * what is enabled and disabled:
58   * <ul>
59   * <li>Disabling an event or interface disables all uses of that class or any subclass.</li>
60   * <li>Enquiring whether an event is enabled returns true if any subclass is enabled.</li>
61   * </ul>
62   */
63  public class ServerNotificationManager implements Work, Disposable, ServerNotificationHandler, MuleContextAware
64  {
65  
66      public static final String NULL_SUBSCRIPTION = "NULL";
67      protected Log logger = LogFactory.getLog(getClass());
68      private boolean dynamic = false;
69      private Configuration configuration = new Configuration();
70      private AtomicBoolean disposed = new AtomicBoolean(false);
71      private BlockingDeque eventQueue = new LinkedBlockingDeque();
72      private MuleContext muleContext;
73  
74      public boolean isNotificationDynamic()
75      {
76          return dynamic;
77      }
78  
79      public void setMuleContext(MuleContext context)
80      {
81          muleContext = context;
82      }
83  
84      public void setNotificationDynamic(boolean dynamic)
85      {
86          this.dynamic = dynamic;
87      }
88  
89      public void start(WorkManager workManager, WorkListener workListener) throws LifecycleException
90      {
91          try
92          {
93              workManager.scheduleWork(this, WorkManager.INDEFINITE, null, workListener);
94          }
95          catch (WorkException e)
96          {
97              throw new LifecycleException(e, this);
98          }
99      }
100 
101     public void addInterfaceToType(Class<? extends ServerNotificationListener> iface, Class<? extends ServerNotification> event)
102     {
103         configuration.addInterfaceToType(iface, event);
104     }
105 
106     public void setInterfaceToTypes(Map<Class<? extends ServerNotificationListener>, Set<Class<? extends ServerNotification>>> interfaceToEvents) throws ClassNotFoundException
107     {
108         configuration.addAllInterfaceToTypes(interfaceToEvents);
109     }
110 
111     public void addListenerSubscriptionPair(ListenerSubscriptionPair pair)
112     {
113         configuration.addListenerSubscriptionPair(pair);
114     }
115 
116     public void addListener(ServerNotificationListener<?> listener)
117     {
118         configuration.addListenerSubscriptionPair(new ListenerSubscriptionPair(listener));
119     }
120 
121     public void addListenerSubscription(ServerNotificationListener<?> listener, String subscription)
122     {
123         configuration.addListenerSubscriptionPair(new ListenerSubscriptionPair(listener, subscription));
124     }
125 
126     public void addAllListenerSubscriptionPairs(Collection<?> pairs)
127     {
128         configuration.addAllListenerSubscriptionPairs(pairs);
129     }
130     
131     /**
132      * @deprecated Use addAllListenerSubscriptionPairs which better describes the "add" operation that occurs.
133      * @param pairs
134      */
135     @Deprecated
136     public void setAllListenerSubscriptionPairs(Collection<?> pairs)
137     {
138         configuration.addAllListenerSubscriptionPairs(pairs);
139     }
140 
141     /**
142      * This removes *all* registrations that reference this listener
143      */
144     public void removeListener(ServerNotificationListener<?> listener)
145     {
146         configuration.removeListener(listener);
147     }
148 
149     public void removeAllListeners(Collection<ServerNotificationListener> listeners)
150     {
151         configuration.removeAllListeners(listeners);
152     }
153 
154     public void disableInterface(Class<? extends ServerNotificationListener> iface) throws ClassNotFoundException
155     {
156         configuration.disableInterface(iface);
157     }
158 
159     public void setDisabledInterfaces(Collection<Class<? extends ServerNotificationListener>> interfaces) throws ClassNotFoundException
160     {
161         configuration.disabledAllInterfaces(interfaces);
162     }
163 
164     public void disableType(Class<? extends ServerNotification> type) throws ClassNotFoundException
165     {
166         configuration.disableType(type);
167     }
168 
169     public void setDisabledTypes(Collection<Class<? extends ServerNotificationListener>> types) throws ClassNotFoundException
170     {
171         configuration.disableAllTypes(types);
172     }
173 
174     public boolean isListenerRegistered(ServerNotificationListener listener)
175     {
176         for (ListenerSubscriptionPair pair : configuration.getListeners())
177         {
178             if (pair.getListener().equals(listener))
179             {
180                 return true;
181             }
182         }
183         return false;
184     }
185 
186     public void fireNotification(ServerNotification notification)
187     {
188         if (!disposed.get())
189         {
190             notification.setMuleContext(muleContext);
191             if (notification instanceof BlockingServerEvent)
192             {
193                 notifyListeners(notification);
194             }
195             else
196             {
197                 try
198                 {
199                     eventQueue.put(notification);
200                 }
201                 catch (InterruptedException e)
202                 {
203                     if (!disposed.get())
204                     {
205                         logger.error("Failed to queue notification: " + notification, e);
206                     }
207                 }
208             }
209         }
210         else
211         {
212             logger.warn("Notification not enqueued after ServerNotificationManager disposal: " + notification);
213         }
214     }
215 
216     public boolean isNotificationEnabled(Class<? extends ServerNotification> type)
217     {
218         boolean enabled = false;
219         if (configuration != null)
220         {
221             Policy policy = configuration.getPolicy();
222             if (policy != null)
223             {
224                 enabled = policy.isNotificationEnabled(type);
225             }
226         }
227         return enabled;
228     }
229 
230     public void dispose()
231     {
232         disposed.set(true);
233         configuration = null;
234     }
235 
236     protected void notifyListeners(ServerNotification notification)
237     {
238         if (!disposed.get())
239         {
240             configuration.getPolicy().dispatch(notification);
241         }
242         else
243         {
244             logger.warn("Notification not delivered after ServerNotificationManager disposal: " + notification);
245         }
246     }
247 
248     public void release()
249     {
250         dispose();
251     }
252 
253     public void run()
254     {
255         while (!disposed.get())
256         {
257             try
258             {
259                 ServerNotification notification = (ServerNotification) eventQueue.poll(
260                     muleContext.getConfiguration().getDefaultQueueTimeout(),
261                     TimeUnit.MILLISECONDS);
262                 if (notification != null)
263                 {
264                     notifyListeners(notification);
265                 }
266             }
267             catch (InterruptedException e)
268             {
269                 Thread.currentThread().interrupt();
270             }
271         }
272     }
273 
274     /**
275      * @return DIRECT reference to an event queue
276      */
277     public Queue getEventQueue() {
278         return eventQueue;
279     }
280 
281     /**
282      * Support string or class parameters
283      */
284     static Class toClass(Object value) throws ClassNotFoundException
285     {
286         Class clazz;
287         if (value instanceof String)
288         {
289             clazz = ClassUtils.loadClass(value.toString(), value.getClass());
290         }
291         else if(value instanceof Class)
292         {
293             clazz = (Class)value;
294         }
295         else
296         {
297            throw new IllegalArgumentException("Notification types and listeners must be a Class with fully qualified class name. Value is: " + value);
298         }
299         return clazz;
300     }
301 
302     // for tests -------------------------------------------------------
303 
304     Policy getPolicy()
305     {
306         return configuration.getPolicy();
307     }
308 
309     public Map<Class<? extends ServerNotificationListener>, Set<Class<? extends ServerNotification>>> getInterfaceToTypes()
310     {
311         return Collections.unmodifiableMap(configuration.getInterfaceToTypes());
312     }
313 
314     public Set<ListenerSubscriptionPair> getListeners()
315     {
316         return Collections.unmodifiableSet(configuration.getListeners());
317     }
318 
319 }