1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.lang3.event;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertSame;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyVetoException;
26 import java.beans.VetoableChangeListener;
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.lang.reflect.InvocationTargetException;
33 import java.lang.reflect.Method;
34 import java.util.ArrayList;
35 import java.util.Date;
36 import java.util.List;
37 import java.util.function.Function;
38
39 import org.apache.commons.lang3.AbstractLangTest;
40 import org.easymock.EasyMock;
41 import org.junit.jupiter.api.Test;
42
43
44
45
46 public class EventListenerSupportTest extends AbstractLangTest {
47
48 private void addDeregisterListener(final EventListenerSupport<VetoableChangeListener> listenerSupport) {
49 listenerSupport.addListener(new VetoableChangeListener() {
50 @Override
51 public void vetoableChange(final PropertyChangeEvent e) {
52 listenerSupport.removeListener(this);
53 }
54 });
55 }
56
57 private VetoableChangeListener createListener(final List<VetoableChangeListener> calledListeners) {
58 return new VetoableChangeListener() {
59 @Override
60 public void vetoableChange(final PropertyChangeEvent e) {
61 calledListeners.add(this);
62 }
63 };
64 }
65
66 @Test
67 public void testAddListenerNoDuplicates() {
68 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
69
70 final VetoableChangeListener[] listeners = listenerSupport.getListeners();
71 assertEquals(0, listeners.length);
72 assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
73 final VetoableChangeListener[] empty = listeners;
74
75 assertSame(empty, listenerSupport.getListeners());
76
77 final VetoableChangeListener listener1 = EasyMock.createNiceMock(VetoableChangeListener.class);
78 listenerSupport.addListener(listener1);
79 assertEquals(1, listenerSupport.getListeners().length);
80 listenerSupport.addListener(listener1, false);
81 assertEquals(1, listenerSupport.getListeners().length);
82 listenerSupport.removeListener(listener1);
83 assertSame(empty, listenerSupport.getListeners());
84 }
85
86 @Test
87 public void testAddNullListener() {
88 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
89 assertThrows(NullPointerException.class, () -> listenerSupport.addListener(null));
90 }
91
92 @Test
93 public void testCreateWithNonInterfaceParameter() {
94 assertThrows(IllegalArgumentException.class, () -> EventListenerSupport.create(String.class));
95 }
96
97 @Test
98 public void testCreateWithNullParameter() {
99 assertThrows(NullPointerException.class, () -> EventListenerSupport.create(null));
100 }
101
102 @Test
103 public void testEventDispatchOrder() throws PropertyVetoException {
104 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
105 final List<VetoableChangeListener> calledListeners = new ArrayList<>();
106
107 final VetoableChangeListener listener1 = createListener(calledListeners);
108 final VetoableChangeListener listener2 = createListener(calledListeners);
109 listenerSupport.addListener(listener1);
110 listenerSupport.addListener(listener2);
111 listenerSupport.fire().vetoableChange(new PropertyChangeEvent(new Date(), "Day", 4, 5));
112 assertEquals(calledListeners.size(), 2);
113 assertSame(calledListeners.get(0), listener1);
114 assertSame(calledListeners.get(1), listener2);
115 }
116
117 @Test
118 public void testGetListeners() {
119 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
120
121 final VetoableChangeListener[] listeners = listenerSupport.getListeners();
122 assertEquals(0, listeners.length);
123 assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
124 final VetoableChangeListener[] empty = listeners;
125
126 assertSame(empty, listenerSupport.getListeners());
127
128 final VetoableChangeListener listener1 = EasyMock.createNiceMock(VetoableChangeListener.class);
129 listenerSupport.addListener(listener1);
130 assertEquals(1, listenerSupport.getListeners().length);
131 final VetoableChangeListener listener2 = EasyMock.createNiceMock(VetoableChangeListener.class);
132 listenerSupport.addListener(listener2);
133 assertEquals(2, listenerSupport.getListeners().length);
134 listenerSupport.removeListener(listener1);
135 assertEquals(1, listenerSupport.getListeners().length);
136 listenerSupport.removeListener(listener2);
137 assertSame(empty, listenerSupport.getListeners());
138 }
139
140 @Test
141 public void testRemoveListenerDuringEvent() throws PropertyVetoException {
142 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
143 for (int i = 0; i < 10; ++i) {
144 addDeregisterListener(listenerSupport);
145 }
146 assertEquals(listenerSupport.getListenerCount(), 10);
147 listenerSupport.fire().vetoableChange(new PropertyChangeEvent(new Date(), "Day", 4, 5));
148 assertEquals(listenerSupport.getListenerCount(), 0);
149 }
150
151 @Test
152 public void testRemoveNullListener() {
153 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
154 assertThrows(NullPointerException.class, () -> listenerSupport.removeListener(null));
155 }
156
157 @Test
158 public void testSerialization() throws IOException, ClassNotFoundException, PropertyVetoException {
159 final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
160 listenerSupport.addListener(Function.identity()::apply);
161 listenerSupport.addListener(EasyMock.createNiceMock(VetoableChangeListener.class));
162
163
164 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
165 try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
166 objectOutputStream.writeObject(listenerSupport);
167 }
168
169
170 @SuppressWarnings("unchecked")
171 final
172 EventListenerSupport<VetoableChangeListener> deserializedListenerSupport = (EventListenerSupport<VetoableChangeListener>) new ObjectInputStream(
173 new ByteArrayInputStream(outputStream.toByteArray())).readObject();
174
175
176 final VetoableChangeListener[] listeners = deserializedListenerSupport.getListeners();
177 assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
178 assertEquals(1, listeners.length);
179
180
181 final VetoableChangeListener listener = listeners[0];
182 final PropertyChangeEvent evt = new PropertyChangeEvent(new Date(), "Day", 7, 9);
183 listener.vetoableChange(evt);
184 EasyMock.replay(listener);
185 deserializedListenerSupport.fire().vetoableChange(evt);
186 EasyMock.verify(listener);
187
188
189 deserializedListenerSupport.removeListener(listener);
190 assertEquals(0, deserializedListenerSupport.getListeners().length);
191 }
192
193 @Test
194 public void testSubclassInvocationHandling() throws PropertyVetoException {
195
196 final EventListenerSupport<VetoableChangeListener> eventListenerSupport = new EventListenerSupport<VetoableChangeListener>(
197 VetoableChangeListener.class) {
198 private static final long serialVersionUID = 1L;
199
200 @Override
201 protected java.lang.reflect.InvocationHandler createInvocationHandler() {
202 return new ProxyInvocationHandler() {
203 @Override
204 public Object invoke(final Object proxy, final Method method, final Object[] args)
205 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
206 return "vetoableChange".equals(method.getName()) && "Hour".equals(((PropertyChangeEvent) args[0]).getPropertyName()) ? null
207 : super.invoke(proxy, method, args);
208 }
209 };
210 }
211 };
212
213 final VetoableChangeListener listener = EasyMock.createNiceMock(VetoableChangeListener.class);
214 eventListenerSupport.addListener(listener);
215 final Object source = new Date();
216 final PropertyChangeEvent ignore = new PropertyChangeEvent(source, "Hour", 5, 6);
217 final PropertyChangeEvent respond = new PropertyChangeEvent(source, "Day", 6, 7);
218 listener.vetoableChange(respond);
219 EasyMock.replay(listener);
220 eventListenerSupport.fire().vetoableChange(ignore);
221 eventListenerSupport.fire().vetoableChange(respond);
222 EasyMock.verify(listener);
223 }
224 }