Skip to content

Commit 6e2741d

Browse files
committed
[LANG-1727] EventListenerSupport doesn't document ordering of events
- Javadoc - Use longer lines - Remove some vertical whitespace
1 parent a6af715 commit 6e2741d

3 files changed

Lines changed: 60 additions & 72 deletions

File tree

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ The <action> type attribute can be add,update,fix,remove.
9494
<action issue="LANG-1772" type="fix" dev="ggregory" due-to="Gary Gregory">Reimplement org.apache.commons.lang3.ClassUtils.hierarchy(Class, Interfaces) using an AtomicReference.</action>
9595
<action type="fix" dev="ggregory" due-to="Ken Dombeck">Fix Javadoc code examples in DiffBuilder and ReflectionDiffBuilder #1400.</action>
9696
<action type="fix" dev="ggregory" due-to="Gary Gregory">Fix generics in org.apache.commons.lang3.stream.Streams.toArray(Class) signature.</action>
97+
<action issue="LANG-1727" type="fix" dev="ggregory" due-to="Elliotte Rusty Harold, Gary Gregory">EventListenerSupport doesn't document ordering of events.</action>
9798
<!-- ADD -->
9899
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Strings and refactor StringUtils.</action>
99100
<action issue="LANG-1747" type="add" dev="ggregory" due-to="Oliver B. Fischer, Gary Gregory">Add StopWatch.run([Failable]Runnable) and get([Failable]Supplier).</action>

src/main/java/org/apache/commons/lang3/event/EventListenerSupport.java

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,28 @@
3737
import org.apache.commons.lang3.function.FailableConsumer;
3838

3939
/**
40-
* An EventListenerSupport object can be used to manage a list of event
41-
* listeners of a particular type. The class provides
42-
* {@link #addListener(Object)} and {@link #removeListener(Object)} methods
43-
* for registering listeners, as well as a {@link #fire()} method for firing
44-
* events to the listeners.
40+
* Manages a list of event listeners of a given generic type. This class provides {@link #addListener(Object)} and {@link #removeListener(Object)} methods for
41+
* managing listeners, as well as a {@link #fire()} method for firing events to the listeners.
4542
*
4643
* <p>
47-
* To use this class, suppose you want to support ActionEvents. You would do:
44+
* For example, to support ActionEvents:
4845
* </p>
46+
*
4947
* <pre>{@code
50-
* public class MyActionEventSource
51-
* {
52-
* private EventListenerSupport<ActionListener> actionListeners =
53-
* EventListenerSupport.create(ActionListener.class);
48+
* public class MyActionEventSource {
49+
*
50+
* private EventListenerSupport<ActionListener> actionListeners = EventListenerSupport.create(ActionListener.class);
5451
*
55-
* public void someMethodThatFiresAction()
56-
* {
57-
* ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "somethingCool");
58-
* actionListeners.fire().actionPerformed(e);
59-
* }
52+
* public void someMethodThatFiresAction() {
53+
* ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "something");
54+
* actionListeners.fire().actionPerformed(e);
55+
* }
6056
* }
6157
* }</pre>
62-
*
6358
* <p>
64-
* Serializing an {@link EventListenerSupport} instance will result in any
65-
* non-{@link Serializable} listeners being silently dropped.
59+
* Events are fired
60+
* <p>
61+
* Serializing an {@link EventListenerSupport} instance will result in any non-{@link Serializable} listeners being silently dropped.
6662
* </p>
6763
*
6864
* @param <L> the type of event listener that is supported by this proxy.
@@ -71,7 +67,7 @@
7167
public class EventListenerSupport<L> implements Serializable {
7268

7369
/**
74-
* An invocation handler used to dispatch the event(s) to all the listeners.
70+
* Invokes listeners through {@link #invoke(Object, Method, Object[])} in the order added to the underlying {@link List}.
7571
*/
7672
protected class ProxyInvocationHandler implements InvocationHandler {
7773

@@ -109,6 +105,9 @@ protected void handle(final Throwable t) throws IllegalAccessException, IllegalA
109105

110106
/**
111107
* Propagates the method call to all registered listeners in place of the proxy listener object.
108+
* <p>
109+
* Calls listeners in the order added to the underlying {@link List}.
110+
* </p>
112111
*
113112
* @param unusedProxy the proxy object representing a listener on which the invocation was called; not used
114113
* @param method the listener method that will be called on all of the listeners.
@@ -156,32 +155,30 @@ public static <T> EventListenerSupport<T> create(final Class<T> listenerInterfac
156155
}
157156

158157
/**
159-
* The list used to hold the registered listeners. This list is
160-
* intentionally a thread-safe copy-on-write-array so that traversals over
161-
* the list of listeners will be atomic.
158+
* Hold the registered listeners. This list is intentionally a thread-safe copy-on-write-array so that traversals over the list of listeners will be atomic.
162159
*/
163160
private List<L> listeners = new CopyOnWriteArrayList<>();
164161

165162
/**
166-
* The proxy representing the collection of listeners. Calls to this proxy
167-
* object will be sent to all registered listeners.
163+
* The proxy representing the collection of listeners. Calls to this proxy object will be sent to all registered listeners.
168164
*/
169165
private transient L proxy;
170-
171166
/**
172167
* Empty typed array for #getListeners().
173168
*/
174169
private transient L[] prototypeArray;
175170

176171
/**
177172
* Constructs a new EventListenerSupport instance.
178-
* Serialization-friendly constructor.
173+
* <p>
174+
* This constructor is needed for serialization.
175+
* </p>
179176
*/
180177
private EventListenerSupport() {
181178
}
182179

183180
/**
184-
* Creates an EventListenerSupport object which supports the provided
181+
* Constructs an EventListenerSupport object which supports the provided
185182
* listener interface.
186183
*
187184
* @param listenerInterface the type of listener interface that will receive
@@ -197,7 +194,7 @@ public EventListenerSupport(final Class<L> listenerInterface) {
197194
}
198195

199196
/**
200-
* Creates an EventListenerSupport object which supports the provided
197+
* Constructs an EventListenerSupport object which supports the provided
201198
* listener interface using the specified class loader to create the JDK
202199
* dynamic proxy.
203200
*
@@ -212,29 +209,31 @@ public EventListenerSupport(final Class<L> listenerInterface, final ClassLoader
212209
this();
213210
Objects.requireNonNull(listenerInterface, "listenerInterface");
214211
Objects.requireNonNull(classLoader, "classLoader");
215-
Validate.isTrue(listenerInterface.isInterface(), "Class %s is not an interface",
216-
listenerInterface.getName());
212+
Validate.isTrue(listenerInterface.isInterface(), "Class %s is not an interface", listenerInterface.getName());
217213
initializeTransientFields(listenerInterface, classLoader);
218214
}
219215

220216
/**
221-
* Registers an event listener.
217+
* Adds an event listener.
218+
* <p>
219+
* Listeners are called in the order added.
220+
* </p>
222221
*
223222
* @param listener the event listener (may not be {@code null}).
224-
* @throws NullPointerException if {@code listener} is
225-
* {@code null}.
223+
* @throws NullPointerException if {@code listener} is {@code null}.
226224
*/
227225
public void addListener(final L listener) {
228226
addListener(listener, true);
229227
}
230228

231229
/**
232-
* Registers an event listener. Will not add a pre-existing listener
233-
* object to the list if {@code allowDuplicate} is false.
230+
* Adds an event listener. Will not add a pre-existing listener object to the list if {@code allowDuplicate} is false.
231+
* <p>
232+
* Listeners are called in the order added.
233+
* </p>
234234
*
235-
* @param listener the event listener (may not be {@code null}).
236-
* @param allowDuplicate the flag for determining if duplicate listener
237-
* objects are allowed to be registered.
235+
* @param listener the event listener (may not be {@code null}).
236+
* @param allowDuplicate the flag for determining if duplicate listener objects are allowed to be registered.
238237
*
239238
* @throws NullPointerException if {@code listener} is {@code null}.
240239
* @since 3.5
@@ -247,7 +246,7 @@ public void addListener(final L listener, final boolean allowDuplicate) {
247246
}
248247

249248
/**
250-
* Creates the {@link InvocationHandler} responsible for broadcasting calls
249+
* Creates the {@link InvocationHandler} responsible for calling
251250
* to the managed listeners. Subclasses can override to provide custom behavior.
252251
*
253252
* @return ProxyInvocationHandler
@@ -263,8 +262,7 @@ protected InvocationHandler createInvocationHandler() {
263262
* @param classLoader the class loader to be used
264263
*/
265264
private void createProxy(final Class<L> listenerInterface, final ClassLoader classLoader) {
266-
proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader,
267-
new Class[] { listenerInterface }, createInvocationHandler()));
265+
proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader, new Class[] { listenerInterface }, createInvocationHandler()));
268266
}
269267

270268
/**
@@ -311,7 +309,7 @@ private void initializeTransientFields(final Class<L> listenerInterface, final C
311309
}
312310

313311
/**
314-
* Deserializes.
312+
* Deserializes the next object into this instance.
315313
*
316314
* @param objectInputStream the input stream
317315
* @throws IOException if an IO error occurs
@@ -326,26 +324,25 @@ private void readObject(final ObjectInputStream objectInputStream) throws IOExce
326324
}
327325

328326
/**
329-
* Unregisters an event listener.
327+
* Removes an event listener.
330328
*
331329
* @param listener the event listener (may not be {@code null}).
332330
* @throws NullPointerException if {@code listener} is
333331
* {@code null}.
334332
*/
335333
public void removeListener(final L listener) {
336-
Objects.requireNonNull(listener, "listener");
337-
listeners.remove(listener);
334+
listeners.remove(Objects.requireNonNull(listener, "listener"));
338335
}
339336

340337
/**
341-
* Serializes.
338+
* Serializes this instance onto the given ObjectOutputStream.
342339
*
343340
* @param objectOutputStream the output stream
344341
* @throws IOException if an IO error occurs
345342
*/
346343
private void writeObject(final ObjectOutputStream objectOutputStream) throws IOException {
347344
final ArrayList<L> serializableListeners = new ArrayList<>();
348-
// don't just rely on instanceof Serializable:
345+
// Don't just rely on instanceof Serializable:
349346
ObjectOutputStream testObjectOutputStream = new ObjectOutputStream(new ByteArrayOutputStream());
350347
for (final L listener : listeners) {
351348
try {
@@ -356,10 +353,8 @@ private void writeObject(final ObjectOutputStream objectOutputStream) throws IOE
356353
testObjectOutputStream = new ObjectOutputStream(new ByteArrayOutputStream());
357354
}
358355
}
359-
/*
360-
* we can reconstitute everything we need from an array of our listeners,
361-
* which has the additional advantage of typically requiring less storage than a list:
362-
*/
356+
// We can reconstitute everything we need from an array of our listeners,
357+
// which has the additional advantage of typically requiring less storage than a list:
363358
objectOutputStream.writeObject(serializableListeners.toArray(prototypeArray));
364359
}
365360
}

src/test/java/org/apache/commons/lang3/event/EventListenerSupportTest.java

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@
4747
import org.junit.jupiter.api.Test;
4848

4949
/**
50+
* Tests {@link EventListenerSupport}.
5051
*/
5152
class EventListenerSupportTest extends AbstractLangTest {
5253

5354
private void addDeregisterListener(final EventListenerSupport<VetoableChangeListener> listenerSupport) {
5455
listenerSupport.addListener(new VetoableChangeListener() {
56+
5557
@Override
5658
public void vetoableChange(final PropertyChangeEvent e) {
5759
listenerSupport.removeListener(this);
@@ -61,6 +63,7 @@ public void vetoableChange(final PropertyChangeEvent e) {
6163

6264
private VetoableChangeListener createListener(final List<VetoableChangeListener> calledListeners) {
6365
return new VetoableChangeListener() {
66+
6467
@Override
6568
public void vetoableChange(final PropertyChangeEvent e) {
6669
calledListeners.add(this);
@@ -71,14 +74,12 @@ public void vetoableChange(final PropertyChangeEvent e) {
7174
@Test
7275
void testAddListenerNoDuplicates() {
7376
final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
74-
7577
final VetoableChangeListener[] listeners = listenerSupport.getListeners();
7678
assertEquals(0, listeners.length);
7779
assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
7880
final VetoableChangeListener[] empty = listeners;
79-
//for fun, show that the same empty instance is used
81+
// for fun, show that the same empty instance is used
8082
assertSame(empty, listenerSupport.getListeners());
81-
8283
final VetoableChangeListener listener1 = EasyMock.createNiceMock(VetoableChangeListener.class);
8384
listenerSupport.addListener(listener1);
8485
assertEquals(1, listenerSupport.getListeners().length);
@@ -108,7 +109,6 @@ void testCreateWithNullParameter() {
108109
void testEventDispatchOrder() throws PropertyVetoException {
109110
final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
110111
final List<VetoableChangeListener> calledListeners = new ArrayList<>();
111-
112112
final VetoableChangeListener listener1 = createListener(calledListeners);
113113
final VetoableChangeListener listener2 = createListener(calledListeners);
114114
listenerSupport.addListener(listener1);
@@ -122,14 +122,12 @@ void testEventDispatchOrder() throws PropertyVetoException {
122122
@Test
123123
void testGetListeners() {
124124
final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
125-
126125
final VetoableChangeListener[] listeners = listenerSupport.getListeners();
127126
assertEquals(0, listeners.length);
128127
assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
129128
final VetoableChangeListener[] empty = listeners;
130-
//for fun, show that the same empty instance is used
129+
// for fun, show that the same empty instance is used
131130
assertSame(empty, listenerSupport.getListeners());
132-
133131
final VetoableChangeListener listener1 = EasyMock.createNiceMock(VetoableChangeListener.class);
134132
listenerSupport.addListener(listener1);
135133
assertEquals(1, listenerSupport.getListeners().length);
@@ -164,47 +162,42 @@ void testSerialization() throws IOException, ClassNotFoundException, PropertyVet
164162
final EventListenerSupport<VetoableChangeListener> listenerSupport = EventListenerSupport.create(VetoableChangeListener.class);
165163
listenerSupport.addListener(Function.identity()::apply);
166164
listenerSupport.addListener(EasyMock.createNiceMock(VetoableChangeListener.class));
167-
168-
//serialize:
165+
// serialize:
169166
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
170167
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
171168
objectOutputStream.writeObject(listenerSupport);
172169
}
173-
174-
//deserialize:
170+
// deserialize:
175171
@SuppressWarnings("unchecked")
176-
final
177-
EventListenerSupport<VetoableChangeListener> deserializedListenerSupport = (EventListenerSupport<VetoableChangeListener>) new ObjectInputStream(
172+
final EventListenerSupport<VetoableChangeListener> deserializedListenerSupport = (EventListenerSupport<VetoableChangeListener>) new ObjectInputStream(
178173
new ByteArrayInputStream(outputStream.toByteArray())).readObject();
179-
180-
//make sure we get a listener array back, of the correct component type, and that it contains only the serializable mock
174+
// make sure we get a listener array back, of the correct component type, and that it contains only the serializable mock
181175
final VetoableChangeListener[] listeners = deserializedListenerSupport.getListeners();
182176
assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType());
183177
assertEquals(1, listeners.length);
184-
185-
//now verify that the mock still receives events; we can infer that the proxy was correctly reconstituted
178+
// now verify that the mock still receives events; we can infer that the proxy was correctly reconstituted
186179
final VetoableChangeListener listener = listeners[0];
187180
final PropertyChangeEvent evt = new PropertyChangeEvent(new Date(), "Day", 7, 9);
188181
listener.vetoableChange(evt);
189182
EasyMock.replay(listener);
190183
deserializedListenerSupport.fire().vetoableChange(evt);
191184
EasyMock.verify(listener);
192-
193-
//remove listener and verify we get an empty array of listeners
185+
// remove listener and verify we get an empty array of listeners
194186
deserializedListenerSupport.removeListener(listener);
195187
assertEquals(0, deserializedListenerSupport.getListeners().length);
196188
}
197189

198190
@Test
199191
void testSubclassInvocationHandling() throws PropertyVetoException {
200-
201192
final EventListenerSupport<VetoableChangeListener> eventListenerSupport = new EventListenerSupport<VetoableChangeListener>(
202193
VetoableChangeListener.class) {
194+
203195
private static final long serialVersionUID = 1L;
204196

205197
@Override
206198
protected java.lang.reflect.InvocationHandler createInvocationHandler() {
207199
return new ProxyInvocationHandler() {
200+
208201
@Override
209202
public Object invoke(final Object proxy, final Method method, final Object[] args)
210203
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
@@ -214,7 +207,6 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg
214207
};
215208
}
216209
};
217-
218210
final VetoableChangeListener listener = EasyMock.createNiceMock(VetoableChangeListener.class);
219211
eventListenerSupport.addListener(listener);
220212
final Object source = new Date();

0 commit comments

Comments
 (0)