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-2022 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 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<C>; 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<?>; 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<EventListenerInterface>; 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<Reference<EventListenerInterface>>; 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 }