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.context.notification.ServerNotification;
10  import org.mule.api.context.notification.ServerNotificationListener;
11  
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.Map;
17  import java.util.Set;
18  
19  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
20  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
21  
22  /**
23   * For a particular configuration, this describes what events should be delivered where.
24   * It is read-only and a lazy instance is cached by the
25   * {@link Configuration}
26   */
27  class Policy
28  {
29  
30      // map from event to set of senders
31      private Map<Class<? extends ServerNotification>, Collection<Sender>> eventToSenders = new HashMap<Class<? extends ServerNotification>, Collection<Sender>>();
32  
33      // these are cumulative - set values should never change, they are just a cache of known info
34      // they are co and contra-variant wrt to exact event type (see code below).
35      private ConcurrentMap knownEventsExact = new ConcurrentHashMap();
36      private ConcurrentMap knownEventsSuper = new ConcurrentHashMap();
37  
38      /**
39       * For each listener, we check each interface and see what events can be delivered.
40       */
41      Policy(Map<Class<? extends ServerNotificationListener>, Set<Class<? extends ServerNotification>>> interfaceToEvents, 
42          Set<ListenerSubscriptionPair> listenerSubscriptionPairs, 
43          Set<Class<? extends ServerNotificationListener>> disabledInterfaces, 
44          Set<Class<? extends ServerNotification>> disabledEvents)
45      {
46          for (ListenerSubscriptionPair pair : listenerSubscriptionPairs)
47          {
48              ServerNotificationListener listener = pair.getListener();
49              for (Class<? extends ServerNotificationListener> iface : interfaceToEvents.keySet())
50              {
51                  if (notASubclassOfAnyClassInSet(disabledInterfaces, iface))
52                  {
53                      if (iface.isAssignableFrom(listener.getClass()))
54                      {
55                          Set<Class<? extends ServerNotification>> events = interfaceToEvents.get(iface);
56                          for (Class<? extends ServerNotification> event : events)
57                          {
58                              if (notASubclassOfAnyClassInSet(disabledEvents, event))
59                              {
60                                  knownEventsExact.put(event, Boolean.TRUE);
61                                  knownEventsSuper.put(event, Boolean.TRUE);
62                                  if (!eventToSenders.containsKey(event))
63                                  {
64                                      // use a collection with predictable iteration order
65                                      eventToSenders.put(event, new ArrayList<Sender>());
66                                  }
67                                  eventToSenders.get(event).add(new Sender(pair));
68                              }
69                          }
70                      }
71                  }
72              }
73          }
74      }
75  
76      protected static boolean notASubclassOfAnyClassInSet(Set set,  Class clazz)
77      {
78          for (Iterator iterator = set.iterator(); iterator.hasNext();)
79          {
80              Class disabled = (Class) iterator.next();
81              if (disabled.isAssignableFrom(clazz))
82              {
83                  return false;
84              }
85          }
86          return true;
87      }
88  
89      protected static boolean notASuperclassOfAnyClassInSet(Set set, Class clazz)
90      {
91          for (Iterator iterator = set.iterator(); iterator.hasNext();)
92          {
93              Class disabled = (Class) iterator.next();
94              if (clazz.isAssignableFrom(disabled))
95              {
96                  return false;
97              }
98          }
99          return true;
100     }
101 
102     void dispatch(ServerNotification notification)
103     {
104         if (null != notification)
105         {
106             Class notfnClass = notification.getClass();
107             // search if we don't know about this event, or if we do know it is used
108             if ((!knownEventsExact.containsKey(notfnClass))
109                     || ((Boolean) knownEventsExact.get(notfnClass)).booleanValue())
110             {
111                 boolean found = false;
112                 for (Class<? extends ServerNotification> event : eventToSenders.keySet())
113                 {
114                     if (event.isAssignableFrom(notfnClass))
115                     {
116                         found = true;
117                         for (Iterator senders = ((Collection) eventToSenders.get(event)).iterator(); senders.hasNext();)
118                         {
119                             ((Sender) senders.next()).dispatch(notification);
120                         }
121                     }
122                 }
123                 knownEventsExact.put(notfnClass, Boolean.valueOf(found));
124             }
125         }
126     }
127 
128     /**
129      * This returns a very "conservative" value - it is true if the notification or any subclass would be
130      * accepted.  So if it returns false then you can be sure that there is no need to send the
131      * notification.  On the other hand, if it returns true there is no guarantee that the notification
132      * "really" will be dispatched to any listener.
133      *
134      * @param notfnClass Either the notification class being generated or some superclass
135      * @return false if there is no need to dispatch the notification
136      */
137     boolean isNotificationEnabled(Class notfnClass)
138     {
139         if (!knownEventsSuper.containsKey(notfnClass))
140         {
141             boolean found = false;
142             // this is exhaustive because we initialise to include all events handled.
143             for (Iterator events = knownEventsSuper.keySet().iterator(); events.hasNext() && !found;)
144             {
145                 Class event = (Class) events.next();
146                 found = ((Boolean) knownEventsSuper.get(event)).booleanValue() && notfnClass.isAssignableFrom(event);
147             }
148             knownEventsSuper.put(notfnClass, Boolean.valueOf(found));
149         }
150         if (!knownEventsExact.containsKey(notfnClass))
151         {
152             boolean found = false;
153             for (Iterator events = eventToSenders.keySet().iterator(); events.hasNext() && !found;)
154             {
155                 Class event = (Class) events.next();
156                 found = event.isAssignableFrom(notfnClass);
157             }
158             knownEventsExact.put(notfnClass, Boolean.valueOf(found));
159 
160         }
161         return ((Boolean) knownEventsSuper.get(notfnClass)).booleanValue()
162                 || ((Boolean) knownEventsExact.get(notfnClass)).booleanValue();
163     }
164 
165 }