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-2021 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 EventInterface event, final boolean verifyMetaData)
195     {
196         fireEvent(event);
197     }
198 
199     /**
200      * Transmit a time-stamped event to all interested listeners.
201      * @param event TimedEventInterface&lt;C&gt;; 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 TimedEventInterface<C> event,
206             final boolean verifyMetaData)
207     {
208         fireEvent(event);
209     }
210 
211     /**
212      * Transmit an event with no payload object to all interested listeners.
213      * @param eventType EventTypeInterface; the eventType of the event
214      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
215      */
216     public void fireEvent(final EventTypeInterface eventType, final boolean verifyMetaData)
217     {
218         this.fireEvent(new Event(eventType, getSourceId(), null, verifyMetaData));
219     }
220 
221     /**
222      * Transmit a time-stamped event with a no payload object to all interested listeners.
223      * @param eventType TimedEventTypeInterface; the eventType of the event.
224      * @param time C; a time stamp for the event
225      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
226      * @param <C> the comparable type to indicate the time when the event is fired
227      */
228     public <C extends Comparable<C> & Serializable> void fireTimedEvent(final TimedEventTypeInterface eventType, final C time,
229             final boolean verifyMetaData)
230     {
231         Throw.whenNull(time, "time may not be null");
232         this.fireEvent(new TimedEvent<C>(eventType, getSourceId(), null, time, verifyMetaData));
233     }
234 
235     /**
236      * Transmit an event with a serializable object as payload to all interested listeners.
237      * @param eventType EventTypeInterface; the eventType of the event
238      * @param value Serializable; the object sent with the event
239      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
240      * @return Serializable; the payload
241      */
242     public Serializable fireEvent(final EventTypeInterface eventType, final Serializable value, final boolean verifyMetaData)
243     {
244         this.fireEvent(new Event(eventType, getSourceId(), value, verifyMetaData));
245         return value;
246     }
247 
248     /**
249      * Transmit a time-stamped event with a Serializable object (payload) to all interested listeners.
250      * @param eventType TimedEventTypeInterface; the eventType of the event.
251      * @param value Serializable; the payload sent with the event
252      * @param time C; a time stamp for the event
253      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
254      * @return Serializable; the payload
255      * @param <C> the comparable type to indicate the time when the event is fired
256      */
257     public <C extends Comparable<C> & Serializable> Serializable fireTimedEvent(final TimedEventTypeInterface eventType,
258             final Serializable value, final C time, final boolean verifyMetaData)
259     {
260         Throw.whenNull(time, "time may not be null");
261         this.fireEvent(new TimedEvent<C>(eventType, getSourceId(), value, time, verifyMetaData));
262         return value;
263     }
264 
265     /**
266      * Transmit an event with a one byte payload to all interested listeners.
267      * @param eventType EventTypeInterface; the eventType of the event
268      * @param value byte; the payload
269      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
270      * @return byte; the payload
271      */
272     public byte fireEvent(final EventTypeInterface eventType, final byte value, final boolean verifyMetaData)
273     {
274         this.fireEvent(eventType, Byte.valueOf(value), verifyMetaData);
275         return value;
276     }
277 
278     /**
279      * Transmit a time-stamped event with a one byte payload to all interested listeners.
280      * @param eventType TimedEventTypeInterface; the eventType of the event
281      * @param value byte; the payload
282      * @param time C; a time stamp for the event
283      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
284      * @param <C> the comparable type to indicate the time when the event is fired
285      * @return byte; the payload
286      */
287     public <C extends Comparable<C> & Serializable> byte fireTimedEvent(final TimedEventTypeInterface eventType,
288             final byte value, final C time, final boolean verifyMetaData)
289     {
290         this.fireTimedEvent(eventType, Byte.valueOf(value), time, verifyMetaData);
291         return value;
292     }
293 
294     /**
295      * Transmit an event with a one char payload to all interested listeners.
296      * @param eventType EventTypeInterface; the eventType of the event
297      * @param value char; the payload
298      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
299      * @return char; the payload
300      */
301     public char fireEvent(final EventTypeInterface eventType, final char value, final boolean verifyMetaData)
302     {
303         this.fireEvent(eventType, Character.valueOf(value), verifyMetaData);
304         return value;
305     }
306 
307     /**
308      * Transmit a time-stamped event with a one char payload to all interested listeners.
309      * @param eventType TimedEventTypeInterface; the eventType of the event
310      * @param value char; the payload
311      * @param time C; a time stamp for the event
312      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
313      * @param <C> the comparable type to indicate the time when the event is fired
314      * @return char; the payload
315      */
316     public <C extends Comparable<C> & Serializable> char fireTimedEvent(final TimedEventTypeInterface eventType,
317             final char value, final C time, final boolean verifyMetaData)
318     {
319         this.fireTimedEvent(eventType, Character.valueOf(value), time, verifyMetaData);
320         return value;
321     }
322 
323     /**
324      * Transmit an event with a boolean payload to all interested listeners.
325      * @param eventType EventTypeInterface; the eventType of the event
326      * @param value boolean; the payload
327      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
328      * @return boolean; the payload
329      */
330     public boolean fireEvent(final EventTypeInterface eventType, final boolean value, final boolean verifyMetaData)
331     {
332         this.fireEvent(eventType, Boolean.valueOf(value), verifyMetaData);
333         return value;
334     }
335 
336     /**
337      * Transmit a time-stamped event with a boolean payload to all interested listeners.
338      * @param eventType TimedEventTypeInterface; the eventType of the event
339      * @param value boolean; the payload
340      * @param time C; a time stamp for the event
341      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
342      * @param <C> the comparable type to indicate the time when the event is fired
343      * @return boolean; the payload
344      */
345     public <C extends Comparable<C> & Serializable> boolean fireTimedEvent(final TimedEventTypeInterface eventType,
346             final boolean value, final C time, final boolean verifyMetaData)
347     {
348         fireTimedEvent(eventType, Boolean.valueOf(value), time, verifyMetaData);
349         return value;
350     }
351 
352     /**
353      * Transmit an event with a double value payload to all interested listeners.
354      * @param eventType EventTypeInterface; the eventType of the event
355      * @param value double; the payload
356      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
357      * @return double; the payload
358      */
359     public double fireEvent(final EventTypeInterface eventType, final double value, final boolean verifyMetaData)
360     {
361         this.fireEvent(eventType, Double.valueOf(value), verifyMetaData);
362         return value;
363     }
364 
365     /**
366      * Transmit a time-stamped event with a double value payload to interested listeners.
367      * @param eventType TimedEventTypeInterface; the eventType of the event
368      * @param value double; the payload
369      * @param time C; a time stamp for the event
370      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
371      * @param <C> the comparable type to indicate the time when the event is fired
372      * @return double; the payload
373      */
374     public <C extends Comparable<C> & Serializable> double fireTimedEvent(final TimedEventTypeInterface eventType,
375             final double value, final C time, final boolean verifyMetaData)
376     {
377         this.fireTimedEvent(eventType, Double.valueOf(value), time, verifyMetaData);
378         return value;
379     }
380 
381     /**
382      * Transmit an event with an integer payload to all interested listeners.
383      * @param eventType EventTypeInterface; the eventType of the event
384      * @param value int; the payload
385      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
386      * @return int; the payload
387      */
388     public int fireEvent(final EventTypeInterface eventType, final int value, final boolean verifyMetaData)
389     {
390         this.fireEvent(eventType, Integer.valueOf(value), verifyMetaData);
391         return value;
392     }
393 
394     /**
395      * Transmit a time-stamped event with an integer payload to all interested listeners.
396      * @param eventType TimedEventTypeInterface; the eventType of the event
397      * @param value int; the payload
398      * @param time C; a time stamp for the event
399      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
400      * @param <C> the comparable type to indicate the time when the event is fired
401      * @return int; the payload
402      */
403     public <C extends Comparable<C> & Serializable> int fireTimedEvent(final TimedEventTypeInterface eventType, final int value,
404             final C time, final boolean verifyMetaData)
405     {
406         this.fireTimedEvent(eventType, Integer.valueOf(value), time, verifyMetaData);
407         return value;
408     }
409 
410     /**
411      * Transmit an event with a long payload to all interested listeners.
412      * @param eventType EventTypeInterface; the eventType of the event
413      * @param value long; the payload
414      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
415      * @return long; the payload
416      */
417     public long fireEvent(final EventTypeInterface eventType, final long value, final boolean verifyMetaData)
418     {
419         this.fireEvent(eventType, Long.valueOf(value), verifyMetaData);
420         return value;
421     }
422 
423     /**
424      * Transmit a time-stamped event with a long payload to all interested listeners.
425      * @param eventType TimedEventTypeInterface; the eventType of the event
426      * @param value long; the payload
427      * @param time C; a time stamp for the event
428      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
429      * @param <C> the comparable type to indicate the time when the event is fired
430      * @return long; the payload
431      */
432     public <C extends Comparable<C> & Serializable> long fireTimedEvent(final TimedEventTypeInterface eventType,
433             final long value, final C time, final boolean verifyMetaData)
434     {
435         this.fireTimedEvent(eventType, Long.valueOf(value), time, verifyMetaData);
436         return value;
437     }
438 
439     /**
440      * Transmit an event with a short payload to all interested listeners.
441      * @param eventType EventTypeInterface; the eventType of the event
442      * @param value short; the payload
443      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
444      * @return short; the payload
445      */
446     public short fireEvent(final EventTypeInterface eventType, final short value, final boolean verifyMetaData)
447     {
448         this.fireEvent(eventType, Short.valueOf(value), verifyMetaData);
449         return value;
450     }
451 
452     /**
453      * Transmit a time-stamped event with a short payload to all interested listeners.
454      * @param eventType TimedEventTypeInterface; the eventType of the event
455      * @param value short; the payload
456      * @param time C; a time stamp for the event
457      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
458      * @param <C> the comparable type to indicate the time when the event is fired
459      * @return short; the payload
460      */
461     public <C extends Comparable<C> & Serializable> short fireTimedEvent(final TimedEventTypeInterface eventType,
462             final short value, final C time, final boolean verifyMetaData)
463     {
464         this.fireTimedEvent(eventType, Short.valueOf(value), time, verifyMetaData);
465         return value;
466     }
467 
468     /**
469      * Transmit an event with a float value payload to all interested listeners.
470      * @param eventType EventTypeInterface; the eventType of the event
471      * @param value float; the payload
472      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
473      * @return float; the payload
474      */
475     public float fireEvent(final EventTypeInterface eventType, final float value, final boolean verifyMetaData)
476     {
477         this.fireEvent(eventType, Float.valueOf(value), verifyMetaData);
478         return value;
479     }
480 
481     /**
482      * Transmit a time-stamped event with a float value payload to interested listeners.
483      * @param eventType TimedEventTypeInterface; the eventType of the event
484      * @param value float; the payload
485      * @param time C; a time stamp for the event
486      * @param verifyMetaData boolean; whether to verify the compliance with metadata or not
487      * @param <C> the comparable type to indicate the time when the event is fired
488      * @return float; the payload
489      */
490     public <C extends Comparable<C> & Serializable> float fireTimedEvent(final TimedEventTypeInterface eventType,
491             final float value, final C time, final boolean verifyMetaData)
492     {
493         this.fireTimedEvent(eventType, Float.valueOf(value), time, verifyMetaData);
494         return value;
495     }
496 
497     /**
498      * Remove all the listeners from this event producer.
499      * @return int; the number of removed event types
500      */
501     public synchronized int removeAllListeners()
502     {
503         int result = this.listeners.size();
504         this.listeners = null;
505         this.listeners = new EventListenerMap();
506         return result;
507     }
508 
509     /**
510      * Removes all the listeners of a class from this event producer.
511      * @param ofClass Class&lt;?&gt;; the class or superclass
512      * @return int; the number of removed listeners
513      */
514     public synchronized int removeAllListeners(final Class<?> ofClass)
515     {
516         Throw.whenNull(ofClass, "ofClass may not be null");
517         int result = 0;
518         Map<EventTypeInterface, Reference<EventListenerInterface>> removeMap = new LinkedHashMap<>();
519         for (EventTypeInterface type : this.listeners.keySet())
520         {
521             for (Iterator<Reference<EventListenerInterface>> ii = this.listeners.get(type).iterator(); ii.hasNext();)
522             {
523                 Reference<EventListenerInterface> listener = ii.next();
524                 if (listener.get().getClass().isAssignableFrom(ofClass))
525                 {
526                     removeMap.put(type, listener);
527                     result++;
528                 }
529             }
530         }
531         for (EventTypeInterface type : removeMap.keySet())
532         {
533             removeListener(removeMap.get(type).get(), type);
534         }
535         return result;
536     }
537 
538     /** {@inheritDoc} */
539     @Override
540     public final synchronized boolean removeListener(final EventListenerInterface listener, final EventTypeInterface eventType)
541     {
542         Throw.whenNull(listener, "listener may not be null");
543         Throw.whenNull(eventType, "eventType may not be null");
544         if (!this.listeners.containsKey(eventType))
545         {
546             return false;
547         }
548         boolean result = false;
549         for (Iterator<Reference<EventListenerInterface>> i = this.listeners.get(eventType).iterator(); i.hasNext();)
550         {
551             Reference<EventListenerInterface> reference = i.next();
552             EventListenerInterface entry = reference.get();
553             if (entry == null)
554             {
555                 i.remove();
556             }
557             else
558             {
559                 if (listener.equals(entry))
560                 {
561                     i.remove();
562                     result = true;
563                 }
564             }
565             if (this.listeners.get(eventType).size() == 0)
566             {
567                 this.listeners.remove(eventType);
568             }
569         }
570         return result;
571     }
572 
573     /**
574      * Remove one reference from the subscription list.
575      * @param reference Reference&lt;EventListenerInterface&gt;; the (strong or weak) reference to remove
576      * @param eventType EventTypeInterface; the eventType for which reference must be removed
577      * @return boolean; true if the reference was removed; otherwise false
578      */
579     private synchronized boolean removeListener(final Reference<EventListenerInterface> reference,
580             final EventTypeInterface eventType)
581     {
582         Throw.whenNull(reference, "reference may not be null");
583         Throw.whenNull(eventType, "eventType may not be null");
584         boolean success = false;
585         for (Iterator<Reference<EventListenerInterface>> i = this.listeners.get(eventType).iterator(); i.hasNext();)
586         {
587             if (i.next().equals(reference))
588             {
589                 i.remove();
590                 success = true;
591             }
592         }
593         if (this.listeners.get(eventType).size() == 0)
594         {
595             this.listeners.remove(eventType);
596         }
597         return success;
598     }
599 
600     /** {@inheritDoc} */
601     @Override
602     public boolean hasListeners()
603     {
604         return !this.listeners.isEmpty();
605     }
606 
607     /** {@inheritDoc} */
608     @Override
609     public synchronized int numberOfListeners(final EventTypeInterface eventType)
610     {
611         if (this.listeners.containsKey(eventType))
612         {
613             return this.listeners.get(eventType).size();
614         }
615         return 0;
616     }
617 
618     /**
619      * Return a safe copy of the list of (strong or weak) references to the registered listeners for the provided event type, or
620      * an empty list when nothing is registered for this event type. The method never returns a null pointer, so it is safe to
621      * use the result directly in an iterator. The references to the listeners are the original references, so not safe copies.
622      * @param eventType EventTypeInterface; the event type to look up the listeners for
623      * @return List&lt;Reference&lt;EventListenerInterface&gt;&gt;; the list of references to the listeners for this event type,
624      *         or an empty list when the event type is not registered
625      */
626     public List<Reference<EventListenerInterface>> getListenerReferences(final EventTypeInterface eventType)
627     {
628         List<Reference<EventListenerInterface>> result = new ArrayList<>();
629         if (this.listeners.get(eventType) != null)
630         {
631             result.addAll(this.listeners.get(eventType));
632         }
633         return result;
634     }
635 
636     /** {@inheritDoc} */
637     @Override
638     public synchronized Set<EventTypeInterface> getEventTypesWithListeners()
639     {
640         return this.listeners.keySet(); // is already a safe copy
641     }
642 
643 }