View Javadoc
1   package org.djutils.event;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertNull;
6   import static org.junit.Assert.assertTrue;
7   import static org.junit.Assert.fail;
8   
9   import java.io.ByteArrayInputStream;
10  import java.io.ByteArrayOutputStream;
11  import java.io.IOException;
12  import java.io.ObjectInputStream;
13  import java.io.ObjectOutputStream;
14  import java.io.Serializable;
15  import java.rmi.AlreadyBoundException;
16  import java.rmi.RemoteException;
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.HashSet;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.djutils.event.ref.Reference;
26  import org.djutils.event.ref.StrongReference;
27  import org.djutils.event.ref.WeakReference;
28  import org.djutils.event.remote.RemoteEventListener;
29  import org.djutils.metadata.MetaData;
30  import org.djutils.rmi.RMIUtils;
31  import org.junit.Test;
32  
33  /**
34   * Test the EventListenerMap.
35   * <p>
36   * Copyright (c) 2002-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
37   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
38   * distributed under a three-clause BSD-style license, which can be found at
39   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. This class was
40   * originally part of the DSOL project, see <a href="https://simulation.tudelft.nl/dsol/manual" target="_blank">
41   * https://simulation.tudelft.nl/dsol/manual</a>.
42   * </p>
43   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
44   */
45  public class EventListenerMapTest implements Serializable
46  {
47      /** */
48      private static final long serialVersionUID = 20191230L;
49  
50      /**
51       * Test the EventListenerMap.
52       */
53      @SuppressWarnings("checkstyle:methodlength")
54      @Test
55      public void testEventListenerMap()
56      {
57          EventType eventType1 = new EventType("EVENT_TYPE1", MetaData.NO_META_DATA);
58          EventType eventType2 = new EventType("EVENT_TYPE2", MetaData.NO_META_DATA);
59          EventListenerInterface el1 = new TestEventListener();
60          Reference<EventListenerInterface> sref1 = new StrongReference<>(el1);
61          EventListenerInterface el2 = new TestEventListener();
62          Reference<EventListenerInterface> sref2 = new StrongReference<>(el2);
63          EventListenerInterface el3 = new TestEventListener();
64          Reference<EventListenerInterface> wref3 = new WeakReference<>(el3);
65          EventListenerInterface el4 = new TestEventListener();
66          Reference<EventListenerInterface> wref4 = new WeakReference<>(el4);
67          Reference<EventListenerInterface> sref4 = new StrongReference<>(el4);
68  
69          // test size(), isEmpty(), put()
70          EventListenerMap elm = new EventListenerMap();
71          assertEquals(0, elm.size());
72          assertTrue(elm.isEmpty());
73          assertFalse(elm.containsKey(eventType1));
74          List<Reference<EventListenerInterface>> list1 = new ArrayList<>();
75          list1.add(sref1);
76          List<Reference<EventListenerInterface>> putResult = elm.put(eventType1, list1);
77          assertNull(putResult);
78          assertEquals(1, elm.size());
79          assertFalse(elm.isEmpty());
80          putResult = elm.put(eventType1, list1);
81          assertEquals(1, elm.size());
82          assertEquals(putResult, list1);
83          List<Reference<EventListenerInterface>> list2 = new ArrayList<>();
84          list2.add(sref2);
85          list2.add(wref3);
86          elm.put(eventType2, list2);
87          assertEquals(2, elm.size());
88  
89          // test keySet()
90          Set<EventTypeInterface> keySet = elm.keySet();
91          assertEquals(2, keySet.size());
92          assertTrue(keySet.contains(eventType1));
93          assertTrue(keySet.contains(eventType2));
94          assertFalse(keySet.contains(new EventType("EVENT_TYPE3", MetaData.NO_META_DATA)));
95  
96          // test containsKey()
97          assertTrue(elm.containsKey(eventType1));
98          assertTrue(elm.containsKey(eventType2));
99          assertFalse(elm.containsKey(new EventType("EVENT_TYPE3", MetaData.NO_META_DATA)));
100 
101         // test containsValue() for Reference and Listener
102         assertTrue(elm.containsValue(el1));
103         assertTrue(elm.containsValue(el2));
104         assertTrue(elm.containsValue(el3));
105         assertFalse(elm.containsValue(el4));
106         assertTrue(elm.containsValue(sref1));
107         assertTrue(elm.containsValue(sref2));
108         assertTrue(elm.containsValue(wref3));
109         assertFalse(elm.containsValue(sref4));
110         assertFalse(elm.containsValue(wref4));
111 
112         // test values()
113         Collection<List<Reference<EventListenerInterface>>> values = elm.values();
114         assertEquals(2, values.size());
115         Iterator<List<Reference<EventListenerInterface>>> vit = values.iterator();
116         List<Reference<EventListenerInterface>> v1 = vit.next();
117         List<Reference<EventListenerInterface>> v2 = vit.next();
118         assertTrue((v1.size() == 1 && v2.size() == 2) || (v1.size() == 2 && v2.size() == 1));
119 
120         // test entrySet()
121         Set<Map.Entry<EventTypeInterface, List<Reference<EventListenerInterface>>>> entrySet = elm.entrySet();
122         assertEquals(2, entrySet.size());
123 
124         // test putAll()
125         EventListenerMap elm2 = new EventListenerMap();
126         elm2.putAll(elm);
127         assertEquals(elm.size(), elm2.size());
128         assertEquals(elm.keySet(), elm2.keySet());
129 
130         // change something in the underlying list and see if the map remains unaffected
131         list1.remove(0);
132         assertTrue((v1.size() == 1 && v2.size() == 2) || (v1.size() == 2 && v2.size() == 1));
133         list1.add(sref1);
134 
135         // test readObject() and writeObject()
136         try
137         {
138             ByteArrayOutputStream baos = new ByteArrayOutputStream();
139             ObjectOutputStream oos = new ObjectOutputStream(baos);
140             oos.writeObject(elm);
141             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
142             ObjectInputStream ois = new ObjectInputStream(bais);
143             EventListenerMap elm3 = (EventListenerMap) ois.readObject();
144             assertEquals(elm.size(), elm3.size());
145             Set<String> names0 = new HashSet<>();
146             elm.keySet().forEach((e) -> names0.add(e.getName()));
147             Set<String> names3 = new HashSet<>();
148             elm3.keySet().forEach((e) -> names3.add(e.getName()));
149             assertEquals(names0, names3);
150             Collection<List<Reference<EventListenerInterface>>> values3 = elm3.values();
151             assertEquals(2, values3.size());
152             Iterator<List<Reference<EventListenerInterface>>> vit3 = values3.iterator();
153             List<Reference<EventListenerInterface>> v31 = vit3.next();
154             List<Reference<EventListenerInterface>> v32 = vit3.next();
155             assertTrue((v31.size() == 1 && v32.size() == 2) || (v31.size() == 2 && v32.size() == 1));
156             baos.close();
157             oos.close();
158             bais.close();
159             ois.close();
160         }
161         catch (IOException | ClassNotFoundException exception)
162         {
163             fail(exception.getMessage());
164         }
165 
166         // test readObject() and writeObject() with one RemoteEventListener (that should not be copied)
167         try
168         {
169             EventType remoteEventType = new EventType("REMOTE_EVENT_TYPE", MetaData.NO_META_DATA);
170             List<Reference<EventListenerInterface>> remoteList = new ArrayList<>();
171             TestRemoteEventListener remoteEventListener = new TestRemoteEventListener();
172             remoteList.add(new WeakReference<EventListenerInterface>(remoteEventListener));
173             elm.put(remoteEventType, remoteList);
174             assertEquals(3, elm.size());
175             ByteArrayOutputStream baos = new ByteArrayOutputStream();
176             ObjectOutputStream oos = new ObjectOutputStream(baos);
177             oos.writeObject(elm);
178             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
179             ObjectInputStream ois = new ObjectInputStream(bais);
180             EventListenerMap elm3 = (EventListenerMap) ois.readObject();
181             assertEquals(2, elm3.size());
182             assertEquals(3, elm.size());
183             elm.remove(remoteEventType);
184             Set<String> names0 = new HashSet<>();
185             elm.keySet().forEach((e) -> names0.add(e.getName()));
186             Set<String> names3 = new HashSet<>();
187             elm3.keySet().forEach((e) -> names3.add(e.getName()));
188             assertEquals(names0, names3);
189             Collection<List<Reference<EventListenerInterface>>> values3 = elm3.values();
190             assertEquals(2, values3.size());
191             Iterator<List<Reference<EventListenerInterface>>> vit3 = values3.iterator();
192             List<Reference<EventListenerInterface>> v31 = vit3.next();
193             List<Reference<EventListenerInterface>> v32 = vit3.next();
194             assertTrue((v31.size() == 1 && v32.size() == 2) || (v31.size() == 2 && v32.size() == 1));
195             baos.close();
196             oos.close();
197             bais.close();
198             ois.close();
199             RMIUtils.closeRegistry(remoteEventListener.getRegistry());
200         }
201         catch (IOException | ClassNotFoundException | AlreadyBoundException exception)
202         {
203             fail(exception.getMessage());
204         }
205 
206         // test get()
207         List<Reference<EventListenerInterface>> getList = elm.get(eventType2);
208         assertEquals(2, elm.size());
209         assertEquals(2, getList.size());
210         getList = elm.get(eventType1);
211         assertEquals(2, elm.size());
212         assertEquals(1, getList.size());
213         getList = elm.get(new EventType("EVENT_TYPE3", MetaData.NO_META_DATA));
214         assertNull(getList);
215 
216         // test remove() and see if the underlying and copied data structures remain unaffected
217         List<Reference<EventListenerInterface>> removedList = elm.remove(eventType2);
218         assertEquals(1, elm.size());
219         assertEquals(2, removedList.size());
220         assertEquals(list2, removedList);
221         assertEquals(2, elm2.size());
222         assertEquals(2, values.size());
223         assertEquals(2, keySet.size());
224         assertTrue(keySet.contains(eventType1));
225         assertTrue(keySet.contains(eventType2));
226         assertTrue(elm.keySet().contains(eventType1));
227         assertFalse(elm.keySet().contains(eventType2));
228         // the entrySet should be affected as it is the only infrastructure that is not a safe copy
229         assertEquals(1, entrySet.size());
230 
231         // test clear() and see if the underlying and copied data structures remain unaffected
232         elm.clear();
233         assertEquals(0, elm.size());
234         assertEquals(2, elm2.size());
235         assertEquals(2, values.size());
236         assertEquals(2, keySet.size());
237         assertTrue(keySet.contains(eventType1));
238         assertTrue(keySet.contains(eventType2));
239         assertFalse(elm.keySet().contains(eventType1));
240         assertFalse(elm.keySet().contains(eventType2));
241     }
242 
243     /** */
244     protected static class TestEventListener implements EventListenerInterface
245     {
246         /** */
247         private static final long serialVersionUID = 20191230L;
248 
249         /** expect notification or not. */
250         private boolean expectingNotification = true;
251 
252         /** expected object in notify. */
253         private Object expectedObject;
254 
255         /** received event. */
256         private EventInterface receivedEvent;
257 
258         /**
259          * @param expectingNotification set expectingNotification
260          */
261         public void setExpectingNotification(final boolean expectingNotification)
262         {
263             this.expectingNotification = expectingNotification;
264         }
265 
266         /**
267          * @param expectedObject set expectedObject
268          */
269         public void setExpectedObject(final Object expectedObject)
270         {
271             this.expectedObject = expectedObject;
272         }
273 
274         /**
275          * @return receivedEvent
276          */
277         public EventInterface getReceivedEvent()
278         {
279             return this.receivedEvent;
280         }
281 
282         /** {@inheritDoc} */
283         @Override
284         public void notify(final EventInterface event) throws RemoteException
285         {
286             if (!this.expectingNotification)
287             {
288                 fail("Received event " + event + " unexpectedly");
289             }
290             this.receivedEvent = event;
291             assertEquals(this.expectedObject, event.getContent());
292         }
293 
294     }
295 
296     /** */
297     protected static class TestRemoteEventListener extends RemoteEventListener
298     {
299         /** */
300         private static final long serialVersionUID = 20191230L;
301 
302         /**
303          * @throws RemoteException on error
304          * @throws AlreadyBoundException on error
305          */
306         public TestRemoteEventListener() throws RemoteException, AlreadyBoundException
307         {
308             super("localhost", 1099, "testListener");
309         }
310 
311         /** {@inheritDoc} */
312         @Override
313         public void notify(final EventInterface event) throws RemoteException
314         {
315             // tagging method
316         }
317     }
318 
319 }