View Javadoc
1   package org.djutils.event;
2   
3   import java.io.Serializable;
4   import java.rmi.Remote;
5   import java.rmi.RemoteException;
6   import java.util.ArrayList;
7   import java.util.Iterator;
8   import java.util.LinkedHashMap;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import org.djutils.event.reference.Reference;
14  import org.djutils.event.reference.ReferenceType;
15  import org.djutils.event.reference.StrongReference;
16  import org.djutils.event.reference.WeakReference;
17  import org.djutils.exceptions.Throw;
18  
19  /**
20   * EventProducer is the interface that exposes a few of the methods of the implementation of an EventProducer to the outside
21   * world: the ability to add and remove listeners.
22   * <p>
23   * Copyright (c) 2022-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
24   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
25   * distributed under a three-clause BSD-style license, which can be found at
26   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
27   * </p>
28   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29   */
30  public interface EventProducer extends Serializable, Remote
31  {
32      /** The FIRST_POSITION in the queue. */
33      int FIRST_POSITION = 0;
34  
35      /** The LAST_POSITION in the queue. */
36      int LAST_POSITION = -1;
37  
38      /**
39       * Add a listener to the specified position of a queue of listeners.
40       * @param listener EventListenerInterface; which is interested at certain events
41       * @param eventType EventType; the events of interest
42       * @param position int; the position of the listener in the queue
43       * @param referenceType ReferenceType; whether the listener is added as a strong or as a weak reference
44       * @return the success of adding the listener. If a listener was already added or an illegal position is provided false is
45       *         returned
46       * @throws RemoteException on network error
47       * @see org.djutils.event.reference.WeakReference
48       */
49      default boolean addListener(final EventListener listener, final EventType eventType, final int position,
50              final ReferenceType referenceType) throws RemoteException
51      {
52          Throw.whenNull(listener, "listener cannot be null");
53          Throw.whenNull(eventType, "eventType cannot be null");
54          Throw.whenNull(referenceType, "referenceType cannot be null");
55          if (position < LAST_POSITION)
56          {
57              return false;
58          }
59          Reference<EventListener> reference = null;
60          if (referenceType.isStrong())
61          {
62              reference = new StrongReference<EventListener>(listener);
63          }
64          else
65          {
66              reference = new WeakReference<EventListener>(listener);
67          }
68          EventListenerMap eventListenerMap = getEventListenerMap();
69          if (eventListenerMap.containsKey(eventType))
70          {
71              for (Reference<EventListener> entry : eventListenerMap.get(eventType))
72              {
73                  if (listener.equals(entry.get()))
74                  {
75                      return false;
76                  }
77              }
78              List<Reference<EventListener>> entries = eventListenerMap.get(eventType);
79              if (position == LAST_POSITION)
80              {
81                  entries.add(reference);
82              }
83              else
84              {
85                  entries.add(position, reference);
86              }
87          }
88          else
89          {
90              List<Reference<EventListener>> entries = new ArrayList<>();
91              entries.add(reference);
92              eventListenerMap.put(eventType, entries);
93          }
94          return true;
95      }
96  
97      /**
98       * Add a listener as strong reference to the BEGINNING of a queue of listeners.
99       * @param listener EventListenerInterface; the listener which is interested at events of eventType
100      * @param eventType EventType; the events of interest
101      * @return the success of adding the listener. If a listener was already added false is returned
102      * @throws RemoteException on network error
103      */
104     default boolean addListener(final EventListener listener, final EventType eventType) throws RemoteException
105     {
106         return addListener(listener, eventType, FIRST_POSITION);
107     }
108 
109     /**
110      * Add a listener to the BEGINNING of a queue of listeners.
111      * @param listener EventListenerInterface; the listener which is interested at events of eventType
112      * @param eventType EventType; the events of interest
113      * @param referenceType ReferenceType; whether the listener is added as a strong or as a weak reference
114      * @return the success of adding the listener. If a listener was already added false is returned
115      * @throws RemoteException on network error
116      * @see org.djutils.event.reference.WeakReference
117      */
118     default boolean addListener(final EventListener listener, final EventType eventType, final ReferenceType referenceType)
119             throws RemoteException
120     {
121         return addListener(listener, eventType, FIRST_POSITION, referenceType);
122     }
123 
124     /**
125      * Add a listener as strong reference to the specified position of a queue of listeners.
126      * @param listener EventListenerInterface; the listener which is interested at events of eventType
127      * @param eventType EventType; the events of interest
128      * @param position int; the position of the listener in the queue
129      * @return the success of adding the listener. If a listener was already added, or an illegal position is provided false is
130      *         returned
131      * @throws RemoteException on network error
132      */
133     default boolean addListener(final EventListener listener, final EventType eventType, final int position)
134             throws RemoteException
135     {
136         return addListener(listener, eventType, position, ReferenceType.STRONG);
137     }
138 
139     /**
140      * Return the map with the EventListener entries and the reference types.
141      * @return EventListenerMap; the map with the EventListener entries and the reference types
142      * @throws RemoteException on network error
143      */
144     EventListenerMap getEventListenerMap() throws RemoteException;
145 
146     /**
147      * Remove all the listeners from this event producer.
148      * @return int; the number of removed event types for which listeners existed
149      * @throws RemoteException on network error
150      */
151     default int removeAllListeners() throws RemoteException
152     {
153         int result = getEventListenerMap().size();
154         getEventListenerMap().clear();
155         return result;
156     }
157 
158     /**
159      * Removes all the listeners of a class from this event producer.
160      * @param ofClass Class&lt;?&gt;; the class or superclass
161      * @return int; the number of removed listeners
162      * @throws RemoteException on network error
163      */
164     default int removeAllListeners(final Class<?> ofClass) throws RemoteException
165     {
166         Throw.whenNull(ofClass, "ofClass may not be null");
167         int result = 0;
168         Map<EventType, Reference<EventListener>> removeMap = new LinkedHashMap<>();
169         for (EventType type : getEventListenerMap().keySet())
170         {
171             for (Iterator<Reference<EventListener>> ii = getEventListenerMap().get(type).iterator(); ii.hasNext();)
172             {
173                 Reference<EventListener> listener = ii.next();
174                 if (listener.get().getClass().isAssignableFrom(ofClass))
175                 {
176                     removeMap.put(type, listener);
177                     result++;
178                 }
179             }
180         }
181         for (EventType type : removeMap.keySet())
182         {
183             removeListener(removeMap.get(type).get(), type);
184         }
185         return result;
186     }
187 
188     /**
189      * Remove the subscription of a listener for a specific event.
190      * @param listener EventListenerInterface; which is no longer interested
191      * @param eventType EventType; the event which is of no interest any more
192      * @return the success of removing the listener. If a listener was not subscribed false is returned
193      * @throws RemoteException on network error
194      */
195     default boolean removeListener(final EventListener listener, final EventType eventType) throws RemoteException
196     {
197         Throw.whenNull(listener, "listener may not be null");
198         Throw.whenNull(eventType, "eventType may not be null");
199         EventListenerMap eventListenerMap = getEventListenerMap();
200         if (!eventListenerMap.containsKey(eventType))
201         {
202             return false;
203         }
204         boolean result = false;
205         for (Iterator<Reference<EventListener>> i = eventListenerMap.get(eventType).iterator(); i.hasNext();)
206         {
207             Reference<EventListener> reference = i.next();
208             EventListener entry = reference.get();
209             if (entry == null)
210             {
211                 i.remove();
212             }
213             else
214             {
215                 if (listener.equals(entry))
216                 {
217                     i.remove();
218                     result = true;
219                 }
220             }
221             if (eventListenerMap.get(eventType).size() == 0)
222             {
223                 eventListenerMap.remove(eventType);
224             }
225         }
226         return result;
227     }
228 
229     /**
230      * Return whether the EventProducer has listeners.
231      * @return boolean; whether the EventProducer has listeners or not
232      * @throws RemoteException on network error
233      */
234     default boolean hasListeners() throws RemoteException
235     {
236         return !getEventListenerMap().isEmpty();
237     }
238 
239     /**
240      * Return the number of listeners for the provided EventType.
241      * @param eventType EventType; the event type to return the number of listeners for
242      * @return boolean; whether the EventProducer has listeners or not
243      * @throws RemoteException on network error
244      */
245     default int numberOfListeners(final EventType eventType) throws RemoteException
246     {
247         if (getEventListenerMap().containsKey(eventType))
248         {
249             return getEventListenerMap().get(eventType).size();
250         }
251         return 0;
252     }
253 
254     /**
255      * Return a safe copy of the list of (strong or weak) references to the registered listeners for the provided event type, or
256      * an empty list when nothing is registered for this event type. The method never returns a null pointer, so it is safe to
257      * use the result directly in an iterator. The references to the listeners are the original references, so not safe copies.
258      * @param eventType EventType; the event type to look up the listeners for
259      * @return List&lt;Reference&lt;EventListenerInterface&gt;&gt;; the list of references to the listeners for this event type,
260      *         or an empty list when the event type is not registered
261      * @throws RemoteException on network error
262      */
263     default List<Reference<EventListener>> getListenerReferences(final EventType eventType) throws RemoteException
264     {
265         List<Reference<EventListener>> result = new ArrayList<>();
266         if (getEventListenerMap().get(eventType) != null)
267         {
268             result.addAll(getEventListenerMap().get(eventType));
269         }
270         return result;
271     }
272 
273     /**
274      * Return the EventTypes for which the EventProducer has listeners.
275      * @return Set&lt;EventType&gt;; the EventTypes for which the EventProducer has registered listeners
276      * @throws RemoteException on netowrk error
277      */
278     default Set<EventType> getEventTypesWithListeners() throws RemoteException
279     {
280         return getEventListenerMap().keySet(); // is already a safe copy
281     }
282 
283     /**
284      * Remove one reference from the subscription list.
285      * @param reference Reference&lt;EventListenerInterface&gt;; the (strong or weak) reference to remove
286      * @param eventType EventType; the eventType for which reference must be removed
287      * @return boolean; true if the reference was removed; otherwise false
288      * @throws RemoteException on network error
289      */
290     private boolean removeListener(final Reference<EventListener> reference, final EventType eventType) throws RemoteException
291     {
292         Throw.whenNull(reference, "reference may not be null");
293         Throw.whenNull(eventType, "eventType may not be null");
294         EventListenerMap eventListenerMap = getEventListenerMap();
295         boolean success = false;
296         for (Iterator<Reference<EventListener>> i = eventListenerMap.get(eventType).iterator(); i.hasNext();)
297         {
298             if (i.next().equals(reference))
299             {
300                 i.remove();
301                 success = true;
302             }
303         }
304         if (eventListenerMap.get(eventType).size() == 0)
305         {
306             eventListenerMap.remove(eventType);
307         }
308         return success;
309     }
310 
311     /**
312      * Transmit an event to all subscribed listeners.
313      * @param event Event; the event
314      * @throws RemoteException on network error
315      */
316     default void fireEvent(final Event event) throws RemoteException
317     {
318         Throw.whenNull(event, "event may not be null");
319         EventListenerMap eventListenerMap = getEventListenerMap();
320         if (eventListenerMap.containsKey(event.getType()))
321         {
322             // make a safe copy because of possible removeListener() in notify() method during fireEvent
323             List<Reference<EventListener>> listenerList = new ArrayList<>(eventListenerMap.get(event.getType()));
324             for (Reference<EventListener> reference : listenerList)
325             {
326                 EventListener listener = reference.get();
327                 try
328                 {
329                     if (listener != null)
330                     {
331                         // The garbage collection has not cleaned the referent
332                         fireEvent(listener, event);
333                     }
334                     else
335                     {
336                         // The garbage collection cleaned the referent;
337                         // there is no need to keep the subscription
338                         removeListener(reference, event.getType());
339                     }
340                 }
341                 catch (RemoteException remoteException)
342                 {
343                     // A network failure prevented the delivery,
344                     // subscription is removed.
345                     removeListener(reference, event.getType());
346                 }
347             }
348         }
349     }
350 
351     /**
352      * Transmit an event to a listener. This method is a hook method. The default implementation simply invokes the notify on
353      * the listener. In specific cases (filtering, storing, queueing, this method can be overwritten.
354      * @param listener EventListenerInterface; the listener for this event
355      * @param event Event; the event to fire
356      * @throws RemoteException on network failure
357      */
358     private void fireEvent(final EventListener listener, final Event event) throws RemoteException
359     {
360         listener.notify(event);
361     }
362 
363     /**
364      * Transmit a time-stamped event to all interested listeners.
365      * @param event TimedEvent&lt;C&gt;; the event
366      * @param <C> the comparable type to indicate the time when the event is fired
367      * @throws RemoteException on network failure
368      */
369     default <C extends Comparable<C> & Serializable> void fireTimedEvent(final TimedEvent<C> event) throws RemoteException
370     {
371         fireEvent(event);
372     }
373 
374     /**
375      * Transmit an event with no payload object to all interested listeners.
376      * @param eventType EventType; the eventType of the event
377      * @throws RemoteException on network failure
378      */
379     default void fireEvent(final EventType eventType) throws RemoteException
380     {
381         fireEvent(new Event(eventType, null, true));
382     }
383 
384     /**
385      * Transmit a time-stamped event with a no payload object to all interested listeners.
386      * @param eventType EventType; the eventType of the event.
387      * @param time C; a time stamp for the event
388      * @param <C> the comparable type to indicate the time when the event is fired
389      * @throws RemoteException on network failure
390      */
391     default <C extends Comparable<C> & Serializable> void fireTimedEvent(final EventType eventType, final C time)
392             throws RemoteException
393 
394     {
395         fireEvent(new TimedEvent<C>(eventType, null, time, true));
396     }
397 
398     /**
399      * Transmit an event with a serializable object as payload to all interested listeners.
400      * @param eventType EventType; the eventType of the event
401      * @param value Serializable; the object sent with the event
402      * @throws RemoteException on network failure
403      */
404     default void fireEvent(final EventType eventType, final Serializable value) throws RemoteException
405     {
406         fireEvent(new Event(eventType, value, true));
407     }
408 
409     /**
410      * Transmit a time-stamped event with a Serializable object (payload) to all interested listeners.
411      * @param eventType EventType; the eventType of the event.
412      * @param value Serializable; the payload sent with the event
413      * @param time C; a time stamp for the event
414      * @param <C> the comparable type to indicate the time when the event is fired
415      * @throws RemoteException on network failure
416      */
417     default <C extends Comparable<C> & Serializable> void fireTimedEvent(final EventType eventType, final Serializable value,
418             final C time) throws RemoteException
419     {
420         fireEvent(new TimedEvent<C>(eventType, value, time, true));
421     }
422 
423     /**
424      * Transmit an event with no payload object to all interested listeners.
425      * @param eventType EventType; the eventType of the event
426      * @throws RemoteException on network failure
427      */
428     default void fireUnverifiedEvent(final EventType eventType) throws RemoteException
429     {
430         fireEvent(new Event(eventType, null, false));
431     }
432 
433     /**
434      * Transmit a time-stamped event with a no payload object to all interested listeners.
435      * @param eventType EventType; the eventType of the event.
436      * @param time C; a time stamp for the event
437      * @param <C> the comparable type to indicate the time when the event is fired
438      * @throws RemoteException on network failure
439      */
440     default <C extends Comparable<C> & Serializable> void fireUnverifiedTimedEvent(final EventType eventType, final C time)
441             throws RemoteException
442     {
443         fireEvent(new TimedEvent<C>(eventType, null, time, false));
444     }
445 
446     /**
447      * Transmit an event with a serializable object as payload to all interested listeners.
448      * @param eventType EventType; the eventType of the event
449      * @param value Serializable; the object sent with the event
450      * @throws RemoteException on network failure
451      */
452     default void fireUnverifiedEvent(final EventType eventType, final Serializable value) throws RemoteException
453     {
454         fireEvent(new Event(eventType, value, false));
455     }
456 
457     /**
458      * Transmit a time-stamped event with a Serializable object (payload) to all interested listeners.
459      * @param eventType EventType; the eventType of the event.
460      * @param value Serializable; the payload sent with the event
461      * @param time C; a time stamp for the event
462      * @param <C> the comparable type to indicate the time when the event is fired
463      * @throws RemoteException on network failure
464      */
465     default <C extends Comparable<C> & Serializable> void fireUnverifiedTimedEvent(final EventType eventType,
466             final Serializable value, final C time) throws RemoteException
467     {
468         fireEvent(new TimedEvent<C>(eventType, value, time, false));
469     }
470 
471 }