View Javadoc

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