View Javadoc

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