View Javadoc
1   package org.djutils.event;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.Iterator;
7   import java.util.LinkedHashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djutils.event.ref.Reference;
13  import org.djutils.event.ref.ReferenceType;
14  import org.djutils.event.ref.StrongReference;
15  import org.djutils.event.ref.WeakReference;
16  import org.djutils.exceptions.Throw;
17  
18  /**
19   * The EventProducer forms the reference implementation of the EventProducerInterface. The storage of the listeners is done in a
20   * Map with the EventType as the key, and a List of References (weak or strong) to the Listeners.
21   * <p>
22   * Copyright (c) 2002-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
23   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
24   * distributed under a three-clause BSD-style license, which can be found at
25   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. This class was
26   * originally part of the DSOL project, see <a href="https://simulation.tudelft.nl/dsol/manual" target="_blank">
27   * https://simulation.tudelft.nl/dsol/manual</a>.
28   * </p>
29   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
30   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   */
32  public class EventProducerImpl implements EventProducerInterface, Serializable
33  {
34      /** The default serial version UID for serializable classes. */
35      private static final long serialVersionUID = 20200207;
36  
37      /** The collection of interested listeners. */
38      private EventListenerMaptListenerMap">EventListenerMap listeners = new EventListenerMap();
39  
40      /** The embedding event producer that uses this helper class. */
41      private final EventProducerInterface embeddingEventProducer;
42  
43      /**
44       * Construct the helper class to execute the work for registering listeners and firing events.
45       * @param embeddingEventProducer EventProducerInterface; the embedding event producer class
46       */
47      public EventProducerImpl(final EventProducerInterface embeddingEventProducer)
48      {
49          this.embeddingEventProducer = embeddingEventProducer;
50      }
51  
52      /** {@inheritDoc} */
53      @Override
54      public Serializable getSourceId() // without RemoteException
55      {
56          try
57          {
58              return this.embeddingEventProducer.getSourceId();
59          }
60          catch (RemoteException rme)
61          {
62              throw new RuntimeException(rme);
63          }
64      }
65  
66      /** {@inheritDoc} */
67      @Override
68      public final synchronized boolean addListener(final EventListenerInterface listener, final EventTypeInterface eventType)
69      {
70          return this.addListener(listener, eventType, EventProducerInterface.FIRST_POSITION);
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public final synchronized boolean addListener(final EventListenerInterface listener, final EventTypeInterface eventType,
76              final ReferenceType referenceType)
77      {
78          return this.addListener(listener, eventType, EventProducerInterface.FIRST_POSITION, referenceType);
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public final synchronized boolean addListener(final EventListenerInterface listener, final EventTypeInterface eventType,
84              final int position)
85      {
86          return this.addListener(listener, eventType, position, ReferenceType.STRONG);
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public final synchronized boolean addListener(final EventListenerInterface listener, final EventTypeInterface eventType,
92              final int position, final ReferenceType referenceType)
93      {
94          Throw.whenNull(listener, "listener cannot be null");
95          Throw.whenNull(eventType, "eventType cannot be null");
96          Throw.whenNull(referenceType, "referenceType cannot be null");
97          if (position < EventProducerInterface.LAST_POSITION)
98          {
99              return false;
100         }
101         Reference<EventListenerInterface> reference = null;
102         if (referenceType.isStrong())
103         {
104             reference = new StrongReference<EventListenerInterface>(listener);
105         }
106         else
107         {
108             reference = new WeakReference<EventListenerInterface>(listener);
109         }
110         if (this.listeners.containsKey(eventType))
111         {
112             for (Reference<EventListenerInterface> entry : this.listeners.get(eventType))
113             {
114                 if (listener.equals(entry.get()))
115                 {
116                     return false;
117                 }
118             }
119             List<Reference<EventListenerInterface>> entries = this.listeners.get(eventType);
120             if (position == EventProducerInterface.LAST_POSITION)
121             {
122                 entries.add(reference);
123             }
124             else
125             {
126                 entries.add(position, reference);
127             }
128         }
129         else
130         {
131             List<Reference<EventListenerInterface>> entries = new ArrayList<>();
132             entries.add(reference);
133             this.listeners.put(eventType, entries);
134         }
135         return true;
136     }
137 
138     /**
139      * Transmit an event to a listener. This method is a hook method. The default implementation simply invokes the notify on
140      * the listener. In specific cases (filtering, storing, queuing, this method can be overwritten.
141      * @param listener EventListenerInterface; the listener for this event
142      * @param event EventInterface; the event to fire
143      * @throws RemoteException on network failure
144      */
145     public void fireEvent(final EventListenerInterface listener, final EventInterface event) throws RemoteException
146     {
147         listener.notify(event);
148     }
149 
150     /**
151      * Transmit an event to all interested listeners.
152      * @param event EventInterface; the event
153      */
154     public synchronized void fireEvent(final EventInterface event)
155     {
156         Throw.whenNull(event, "event may not be null");
157         Throw.whenNull(event.getType(), "event type may not be null");
158         if (this.listeners.containsKey(event.getType()))
159         {
160             // make a safe copy because of possible removeListener() in notify() method during fireEvent
161             List<Reference<EventListenerInterface>> listenerList = new ArrayList<>(this.listeners.get(event.getType()));
162             for (Reference<EventListenerInterface> reference : listenerList)
163             {
164                 EventListenerInterface listener = reference.get();
165                 try
166                 {
167                     if (listener != null)
168                     {
169                         // The garbage collection has not cleaned the referent
170                         this.fireEvent(listener, event);
171                     }
172                     else
173                     {
174                         // The garbage collection cleaned the referent;
175                         // there is no need to keep the subscription
176                         this.removeListener(reference, event.getType());
177                     }
178                 }
179                 catch (RemoteException remoteException)
180                 {
181                     // A network failure prevented the delivery,
182                     // subscription is removed.
183                     this.removeListener(reference, event.getType());
184                 }
185             }
186         }
187     }
188 
189     /**
190      * Transmit a regular event to all interested listeners.
191      * @param event EventInterface; the event
192      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
193      */
194     public void fireEvent(final Event event, final boolean verifyMetaData)
195     {
196         fireEvent(event);
197     }
198 
199     /**
200      * Transmit a time-stamped event to all interested listeners.
201      * @param event EventInterface; the event
202      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
203      * @param <C> the comparable type to indicate the time when the event is fired
204      */
205     public <C extends Comparable<C> & Serializable> void fireTimedEvent(final TimedEvent<C> event, final boolean verifyMetaData)
206     {
207         fireEvent(event);
208     }
209 
210     /**
211      * Transmit an event with no payload object to all interested listeners.
212      * @param eventType EventType; the eventType of the event
213      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
214      */
215     public void fireEvent(final EventType eventType, final boolean verifyMetaData)
216     {
217         this.fireEvent(new Event(eventType, getSourceId(), null, verifyMetaData));
218     }
219 
220     /**
221      * Transmit a time-stamped event with a no payload object to all interested listeners.
222      * @param eventType EventType; the eventType of the event.
223      * @param time C; a time stamp for the event
224      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
225      * @param <C> the comparable type to indicate the time when the event is fired
226      */
227     public <C extends Comparable<C> & Serializable> void fireTimedEvent(final TimedEventType eventType, final C time,
228             final boolean verifyMetaData)
229     {
230         Throw.whenNull(time, "time may not be null");
231         this.fireEvent(new TimedEvent<C>(eventType, getSourceId(), null, time, verifyMetaData));
232     }
233 
234     /**
235      * Transmit an event with a serializable object as payload to all interested listeners.
236      * @param eventType EventType; the eventType of the event
237      * @param value Serializable; the object sent with the event
238      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
239      * @return Serializable; the payload
240      */
241     public Serializable fireEvent(final EventType eventType, final Serializable value, final boolean verifyMetaData)
242     {
243         this.fireEvent(new Event(eventType, getSourceId(), value, verifyMetaData));
244         return value;
245     }
246 
247     /**
248      * Transmit a time-stamped event with a Serializable object (payload) to all interested listeners.
249      * @param eventType EventType; the eventType of the event.
250      * @param value Serializable; the payload sent with the event
251      * @param time C; a time stamp for the event
252      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
253      * @return Serializable; the payload
254      * @param <C> the comparable type to indicate the time when the event is fired
255      */
256     public <C extends Comparable<C> & Serializable> Serializable fireTimedEvent(final TimedEventType eventType,
257             final Serializable value, final C time, final boolean verifyMetaData)
258     {
259         Throw.whenNull(time, "time may not be null");
260         this.fireEvent(new TimedEvent<C>(eventType, getSourceId(), value, time, verifyMetaData));
261         return value;
262     }
263 
264     /**
265      * Transmit an event with a one byte payload to all interested listeners.
266      * @param eventType EventType; the eventType of the event
267      * @param value byte; the payload
268      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
269      * @return byte; the payload
270      */
271     public byte fireEvent(final EventType eventType, final byte value, final boolean verifyMetaData)
272     {
273         this.fireEvent(eventType, Byte.valueOf(value), verifyMetaData);
274         return value;
275     }
276 
277     /**
278      * Transmit a time-stamped event with a one byte payload to all interested listeners.
279      * @param eventType EventType; the eventType of the event
280      * @param value byte; the payload
281      * @param time C; a time stamp for the event
282      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
283      * @param <C> the comparable type to indicate the time when the event is fired
284      * @return byte; the payload
285      */
286     public <C extends Comparable<C> & Serializable> byte fireTimedEvent(final TimedEventType eventType, final byte value,
287             final C time, final boolean verifyMetaData)
288     {
289         this.fireTimedEvent(eventType, Byte.valueOf(value), time, verifyMetaData);
290         return value;
291     }
292 
293     /**
294      * Transmit an event with a one char payload to all interested listeners.
295      * @param eventType EventType; the eventType of the event
296      * @param value char; the payload
297      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
298      * @return char; the payload
299      */
300     public char fireEvent(final EventType eventType, final char value, final boolean verifyMetaData)
301     {
302         this.fireEvent(eventType, Character.valueOf(value), verifyMetaData);
303         return value;
304     }
305 
306     /**
307      * Transmit a time-stamped event with a one char payload to all interested listeners.
308      * @param eventType EventType; the eventType of the event
309      * @param value char; the payload
310      * @param time C; a time stamp for the event
311      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
312      * @param <C> the comparable type to indicate the time when the event is fired
313      * @return char; the payload
314      */
315     public <C extends Comparable<C> & Serializable> char fireTimedEvent(final TimedEventType eventType, final char value,
316             final C time, final boolean verifyMetaData)
317     {
318         this.fireTimedEvent(eventType, Character.valueOf(value), time, verifyMetaData);
319         return value;
320     }
321 
322     /**
323      * Transmit an event with a boolean payload to all interested listeners.
324      * @param eventType EventType; the eventType of the event
325      * @param value boolean; the payload
326      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
327      * @return boolean; the payload
328      */
329     public boolean fireEvent(final EventType eventType, final boolean value, final boolean verifyMetaData)
330     {
331         this.fireEvent(eventType, Boolean.valueOf(value), verifyMetaData);
332         return value;
333     }
334 
335     /**
336      * Transmit a time-stamped event with a boolean payload to all interested listeners.
337      * @param eventType EventType; the eventType of the event
338      * @param value boolean; the payload
339      * @param time C; a time stamp for the event
340      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
341      * @param <C> the comparable type to indicate the time when the event is fired
342      * @return boolean; the payload
343      */
344     public <C extends Comparable<C> & Serializable> boolean fireTimedEvent(final TimedEventType eventType, final boolean value,
345             final C time, final boolean verifyMetaData)
346     {
347         fireTimedEvent(eventType, Boolean.valueOf(value), time, verifyMetaData);
348         return value;
349     }
350 
351     /**
352      * Transmit an event with a double value payload to all interested listeners.
353      * @param eventType EventType; the eventType of the event
354      * @param value double; the payload
355      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
356      * @return double; the payload
357      */
358     public double fireEvent(final EventType eventType, final double value, final boolean verifyMetaData)
359     {
360         this.fireEvent(eventType, Double.valueOf(value), verifyMetaData);
361         return value;
362     }
363 
364     /**
365      * Transmit a time-stamped event with a double value payload to interested listeners.
366      * @param eventType EventType; the eventType of the event
367      * @param value double; the payload
368      * @param time C; a time stamp for the event
369      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
370      * @param <C> the comparable type to indicate the time when the event is fired
371      * @return double; the payload
372      */
373     public <C extends Comparable<C> & Serializable> double fireTimedEvent(final TimedEventType eventType, final double value,
374             final C time, final boolean verifyMetaData)
375     {
376         this.fireTimedEvent(eventType, Double.valueOf(value), time, verifyMetaData);
377         return value;
378     }
379 
380     /**
381      * Transmit an event with an integer payload to all interested listeners.
382      * @param eventType EventType; the eventType of the event
383      * @param value int; the payload
384      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
385      * @return int; the payload
386      */
387     public int fireEvent(final EventType eventType, final int value, final boolean verifyMetaData)
388     {
389         this.fireEvent(eventType, Integer.valueOf(value), verifyMetaData);
390         return value;
391     }
392 
393     /**
394      * Transmit a time-stamped event with an integer payload to all interested listeners.
395      * @param eventType EventType; the eventType of the event
396      * @param value int; the payload
397      * @param time C; a time stamp for the event
398      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
399      * @param <C> the comparable type to indicate the time when the event is fired
400      * @return int; the payload
401      */
402     public <C extends Comparable<C> & Serializable> int fireTimedEvent(final TimedEventType eventType, final int value,
403             final C time, final boolean verifyMetaData)
404     {
405         this.fireTimedEvent(eventType, Integer.valueOf(value), time, verifyMetaData);
406         return value;
407     }
408 
409     /**
410      * Transmit an event with a long payload to all interested listeners.
411      * @param eventType EventType; the eventType of the event
412      * @param value long; the payload
413      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
414      * @return long; the payload
415      */
416     public long fireEvent(final EventType eventType, final long value, final boolean verifyMetaData)
417     {
418         this.fireEvent(eventType, Long.valueOf(value), verifyMetaData);
419         return value;
420     }
421 
422     /**
423      * Transmit a time-stamped event with a long payload to all interested listeners.
424      * @param eventType EventType; the eventType of the event
425      * @param value long; the payload
426      * @param time C; a time stamp for the event
427      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
428      * @param <C> the comparable type to indicate the time when the event is fired
429      * @return long; the payload
430      */
431     public <C extends Comparable<C> & Serializable> long fireTimedEvent(final TimedEventType eventType, final long value,
432             final C time, final boolean verifyMetaData)
433     {
434         this.fireTimedEvent(eventType, Long.valueOf(value), time, verifyMetaData);
435         return value;
436     }
437 
438     /**
439      * Transmit an event with a short payload to all interested listeners.
440      * @param eventType EventType; the eventType of the event
441      * @param value short; the payload
442      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
443      * @return short; the payload
444      */
445     public short fireEvent(final EventType eventType, final short value, final boolean verifyMetaData)
446     {
447         this.fireEvent(eventType, Short.valueOf(value), verifyMetaData);
448         return value;
449     }
450 
451     /**
452      * Transmit a time-stamped event with a short payload to all interested listeners.
453      * @param eventType EventType; the eventType of the event
454      * @param value short; the payload
455      * @param time C; a time stamp for the event
456      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
457      * @param <C> the comparable type to indicate the time when the event is fired
458      * @return short; the payload
459      */
460     public <C extends Comparable<C> & Serializable> short fireTimedEvent(final TimedEventType eventType, final short value,
461             final C time, final boolean verifyMetaData)
462     {
463         this.fireTimedEvent(eventType, Short.valueOf(value), time, verifyMetaData);
464         return value;
465     }
466 
467     /**
468      * Transmit an event with a float value payload to all interested listeners.
469      * @param eventType EventType; the eventType of the event
470      * @param value float; the payload
471      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
472      * @return float; the payload
473      */
474     public float fireEvent(final EventType eventType, final float value, final boolean verifyMetaData)
475     {
476         this.fireEvent(eventType, Float.valueOf(value), verifyMetaData);
477         return value;
478     }
479 
480     /**
481      * Transmit a time-stamped event with a float value payload to interested listeners.
482      * @param eventType EventType; the eventType of the event
483      * @param value float; the payload
484      * @param time C; a time stamp for the event
485      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
486      * @param <C> the comparable type to indicate the time when the event is fired
487      * @return float; the payload
488      */
489     public <C extends Comparable<C> & Serializable> float fireTimedEvent(final TimedEventType eventType, final float value,
490             final C time, final boolean verifyMetaData)
491     {
492         this.fireTimedEvent(eventType, Float.valueOf(value), time, verifyMetaData);
493         return value;
494     }
495 
496     /**
497      * Remove all the listeners from this event producer.
498      * @return int; the number of removed event types
499      */
500     public synchronized int removeAllListeners()
501     {
502         int result = this.listeners.size();
503         this.listeners = null;
504         this.listeners = new EventListenerMap();
505         return result;
506     }
507 
508     /**
509      * Removes all the listeners of a class from this event producer.
510      * @param ofClass Class&lt;?&gt;; the class or superclass
511      * @return int; the number of removed listeners
512      */
513     public synchronized int removeAllListeners(final Class<?> ofClass)
514     {
515         Throw.whenNull(ofClass, "ofClass may not be null");
516         int result = 0;
517         Map<EventTypeInterface, Reference<EventListenerInterface>> removeMap = new LinkedHashMap<>();
518         for (EventTypeInterface type : this.listeners.keySet())
519         {
520             for (Iterator<Reference<EventListenerInterface>> ii = this.listeners.get(type).iterator(); ii.hasNext();)
521             {
522                 Reference<EventListenerInterface> listener = ii.next();
523                 if (listener.get().getClass().isAssignableFrom(ofClass))
524                 {
525                     removeMap.put(type, listener);
526                     result++;
527                 }
528             }
529         }
530         for (EventTypeInterface type : removeMap.keySet())
531         {
532             removeListener(removeMap.get(type).get(), type);
533         }
534         return result;
535     }
536 
537     /** {@inheritDoc} */
538     @Override
539     public final synchronized boolean removeListener(final EventListenerInterface listener, final EventTypeInterface eventType)
540     {
541         Throw.whenNull(listener, "listener may not be null");
542         Throw.whenNull(eventType, "eventType may not be null");
543         if (!this.listeners.containsKey(eventType))
544         {
545             return false;
546         }
547         boolean result = false;
548         for (Iterator<Reference<EventListenerInterface>> i = this.listeners.get(eventType).iterator(); i.hasNext();)
549         {
550             Reference<EventListenerInterface> reference = i.next();
551             EventListenerInterface entry = reference.get();
552             if (entry == null)
553             {
554                 i.remove();
555             }
556             else
557             {
558                 if (listener.equals(entry))
559                 {
560                     i.remove();
561                     result = true;
562                 }
563             }
564             if (this.listeners.get(eventType).size() == 0)
565             {
566                 this.listeners.remove(eventType);
567             }
568         }
569         return result;
570     }
571 
572     /**
573      * Remove one reference from the subscription list.
574      * @param reference Reference&lt;EventListenerInterface&gt;; the (strong or weak) reference to remove
575      * @param eventType EventType; the eventType for which reference must be removed
576      * @return boolean; true if the reference was removed; otherwise false
577      */
578     private synchronized boolean removeListener(final Reference<EventListenerInterface> reference,
579             final EventTypeInterface eventType)
580     {
581         Throw.whenNull(reference, "reference may not be null");
582         Throw.whenNull(eventType, "eventType may not be null");
583         boolean success = false;
584         for (Iterator<Reference<EventListenerInterface>> i = this.listeners.get(eventType).iterator(); i.hasNext();)
585         {
586             if (i.next().equals(reference))
587             {
588                 i.remove();
589                 success = true;
590             }
591         }
592         if (this.listeners.get(eventType).size() == 0)
593         {
594             this.listeners.remove(eventType);
595         }
596         return success;
597     }
598 
599     /** {@inheritDoc} */
600     @Override
601     public boolean hasListeners()
602     {
603         return !this.listeners.isEmpty();
604     }
605 
606     /** {@inheritDoc} */
607     @Override
608     public synchronized int numberOfListeners(final EventTypeInterface eventType)
609     {
610         if (this.listeners.containsKey(eventType))
611         {
612             return this.listeners.get(eventType).size();
613         }
614         return 0;
615     }
616 
617     /**
618      * Return a safe copy of the list of (strong or weak) references to the registered listeners for the provided event type, or
619      * an empty list when nothing is registered for this event type. The method never returns a null pointer, so it is safe to
620      * use the result directly in an iterator. The references to the listeners are the original references, so not safe copies.
621      * @param eventType EventType; the event type to look up the listeners for
622      * @return List&lt;Reference&lt;EventListenerInterface&gt;&gt;; the list of references to the listeners for this event type,
623      *         or an empty list when the event type is not registered
624      */
625     public List<Reference<EventListenerInterface>> getListenerReferences(final EventTypeInterface eventType)
626     {
627         List<Reference<EventListenerInterface>> result = new ArrayList<>();
628         if (this.listeners.get(eventType) != null)
629         {
630             result.addAll(this.listeners.get(eventType));
631         }
632         return result;
633     }
634 
635     /** {@inheritDoc} */
636     @Override
637     public synchronized Set<EventTypeInterface> getEventTypesWithListeners()
638     {
639         return this.listeners.keySet(); // is already a safe copy
640     }
641 
642 }