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-2023 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 netowrk 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<?>; 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<Reference<EventListenerInterface>>; 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<EventType>; 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<EventListenerInterface>; 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<C>; 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 }