View Javadoc

1   /*
2    * $Id: ServerNotificationManager.java 11844 2008-05-23 12:49:16Z 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.context.notification;
12  
13  import org.mule.api.context.WorkManager;
14  import org.mule.api.context.notification.BlockingServerEvent;
15  import org.mule.api.context.notification.ServerNotification;
16  import org.mule.api.context.notification.ServerNotificationHandler;
17  import org.mule.api.context.notification.ServerNotificationListener;
18  import org.mule.api.lifecycle.Disposable;
19  import org.mule.api.lifecycle.LifecycleException;
20  import org.mule.util.ClassUtils;
21  
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Map;
25  
26  import javax.resource.spi.work.Work;
27  import javax.resource.spi.work.WorkException;
28  import javax.resource.spi.work.WorkListener;
29  
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.atomic.AtomicBoolean;
33  
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
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  
73      public boolean isNotificationDynamic()
74      {
75          return dynamic;
76      }
77  
78      public void setNotificationDynamic(boolean dynamic)
79      {
80          this.dynamic = dynamic;
81      }
82  
83      public void start(WorkManager workManager, WorkListener workListener) throws LifecycleException
84      {
85          try
86          {
87              workManager.scheduleWork(this, WorkManager.INDEFINITE, null, workListener);
88          }
89          catch (WorkException e)
90          {
91              throw new LifecycleException(e, this);
92          }
93      }
94  
95      public void addInterfaceToType(Class iface, Class event)
96      {
97          configuration.addInterfaceToType(iface, event);
98      }
99  
100     public void setInterfaceToTypes(Map interfaceToEvents) throws ClassNotFoundException
101     {
102         configuration.addAllInterfaceToTypes(interfaceToEvents);
103     }
104 
105     public void addListenerSubscriptionPair(ListenerSubscriptionPair pair)
106     {
107         configuration.addListenerSubscriptionPair(pair);
108     }
109 
110     public void addListener(ServerNotificationListener listener)
111     {
112         configuration.addListenerSubscriptionPair(new ListenerSubscriptionPair(listener));
113     }
114 
115     public void addListenerSubscription(ServerNotificationListener listener, String subscription)
116     {
117         configuration.addListenerSubscriptionPair(new ListenerSubscriptionPair(listener, subscription));
118     }
119 
120     public void setAllListenerSubscriptionPairs(Collection pairs)
121     {
122         configuration.addAllListenerSubscriptionPairs(pairs);
123     }
124 
125     /**
126      * This removes *all* registrations that reference this listener
127      */
128     public void removeListener(ServerNotificationListener listener)
129     {
130         configuration.removeListener(listener);
131     }
132 
133     public void removeAllListeners(Collection listeners)
134     {
135         configuration.removeAllListeners(listeners);
136     }
137 
138     public void disableInterface(Class iface) throws ClassNotFoundException
139     {
140         configuration.disableInterface(iface);
141     }
142 
143     public void setDisabledInterfaces(Collection interfaces) throws ClassNotFoundException
144     {
145         configuration.disabledAllInterfaces(interfaces);
146     }
147 
148     public void disableType(Class type) throws ClassNotFoundException
149     {
150         configuration.disableType(type);
151     }
152 
153     public void setDisabledTypes(Collection types) throws ClassNotFoundException
154     {
155         configuration.disableAllTypes(types);
156     }
157 
158     public void fireNotification(ServerNotification notification)
159     {
160         if (!disposed.get())
161         {
162             if (notification instanceof BlockingServerEvent)
163             {
164                 notifyListeners(notification);
165             }
166             else
167             {
168                 try
169                 {
170                     eventQueue.put(notification);
171                 }
172                 catch (InterruptedException e)
173                 {
174                     if (!disposed.get())
175                     {
176                         logger.error("Failed to queue notification: " + notification, e);
177                     }
178                 }
179             }
180         }
181         else
182         {
183             logger.warn("Notification not enqueued after ServerNotificationManager disposal: " + notification);
184         }
185     }
186 
187     public boolean isNotificationEnabled(Class type)
188     {
189         return configuration.getPolicy().isNotificationEnabled(type);
190     }
191 
192     public void dispose()
193     {
194         disposed.set(true);
195         configuration = null;
196     }
197 
198     protected void notifyListeners(ServerNotification notification)
199     {
200         if (!disposed.get())
201         {
202             configuration.getPolicy().dispatch(notification);
203         }
204         else
205         {
206             logger.warn("Notification not delivered after ServerNotificationManager disposal: " + notification);
207         }
208     }
209 
210     public void release()
211     {
212         dispose();
213     }
214 
215     public void run()
216     {
217         while (!disposed.get())
218         {
219             try
220             {
221                 ServerNotification notification = (ServerNotification) eventQueue.take();
222                 notifyListeners(notification);
223             }
224             catch (InterruptedException e)
225             {
226                 // ignore - we just loop round
227             }
228         }
229     }
230 
231     /**
232      * Support string or class parameters
233      */
234     public static Class toClass(Object value) throws ClassNotFoundException
235     {
236         Class clazz;
237         if (value instanceof String)
238         {
239             clazz = ClassUtils.loadClass(value.toString(), value.getClass());
240         }
241         else if(value instanceof Class)
242         {
243             clazz = (Class)value;
244         }
245         else
246         {
247            throw new IllegalArgumentException("Notification types and listeners must be a Class with fully qualified class name. Value is: " + value);
248         }
249         return clazz;
250     }
251 
252     // for tests -------------------------------------------------------
253 
254     Policy getPolicy()
255     {
256         return configuration.getPolicy();
257     }
258 
259     public Map getInterfaceToTypes()
260     {
261         return Collections.unmodifiableMap(configuration.getInterfaceToTypes());
262     }
263 
264     public Collection getListeners()
265     {
266         return Collections.unmodifiableCollection(configuration.getListeners());
267     }
268 
269 }