2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
17
package org.apache.commons.configuration.event;
19
import java.util.ArrayList;
20
import java.util.Collection;
21
import java.util.Collections;
22
import java.util.Iterator;
23
import java.util.concurrent.CopyOnWriteArrayList;
27
* A base class for objects that can generate configuration events.
30
* This class implements functionality for managing a set of event listeners
31
* that can be notified when an event occurs. It can be extended by
32
* configuration classes that support the event mechanism. In this case these
33
* classes only need to call the {@code fireEvent()} method when an event
34
* is to be delivered to the registered listeners.
37
* Adding and removing event listeners can happen concurrently to manipulations
38
* on a configuration that cause events. The operations are synchronized.
41
* With the {@code detailEvents} property the number of detail events can
42
* be controlled. Some methods in configuration classes are implemented in a way
43
* that they call other methods that can generate their own events. One example
44
* is the {@code setProperty()} method that can be implemented as a
45
* combination of {@code clearProperty()} and {@code addProperty()}.
46
* With {@code detailEvents} set to <b>true</b>, all involved methods
47
* will generate events (i.e. listeners will receive property set events,
48
* property clear events, and property add events). If this mode is turned off
49
* (which is the default), detail events are suppressed, so only property set
50
* events will be received. Note that the number of received detail events may
51
* differ for different configuration implementations.
52
* {@link org.apache.commons.configuration.HierarchicalConfiguration HierarchicalConfiguration}
53
* for instance has a custom implementation of {@code setProperty()},
54
* which does not generate any detail events.
57
* In addition to "normal" events, error events are supported. Such
58
* events signal an internal problem that occurred during access of properties.
59
* For them a special listener interface exists:
60
* {@link ConfigurationErrorListener}. There is another set of
61
* methods dealing with event listeners of this type. The
62
* {@code fireError()} method can be used by derived classes to send
63
* notifications about errors to registered observers.
66
* @author <a href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
67
* @version $Id: EventSource.java 1234617 2012-01-22 21:31:01Z oheger $
70
public class EventSource
72
/** A collection for the registered event listeners. */
73
private Collection<ConfigurationListener> listeners;
75
/** A collection for the registered error listeners.*/
76
private Collection<ConfigurationErrorListener> errorListeners;
78
/** A lock object for guarding access to the detail events counter. */
79
private final Object lockDetailEventsCount = new Object();
81
/** A counter for the detail events. */
82
private int detailEvents;
85
* Creates a new instance of {@code EventSource}.
93
* Adds a configuration listener to this object.
95
* @param l the listener to add
97
public void addConfigurationListener(ConfigurationListener l)
104
* Removes the specified event listener so that it does not receive any
105
* further events caused by this object.
107
* @param l the listener to be removed
108
* @return a flag whether the event listener was found
110
public boolean removeConfigurationListener(ConfigurationListener l)
112
return listeners.remove(l);
116
* Returns a collection with all configuration event listeners that are
117
* currently registered at this object.
119
* @return a collection with the registered
120
* {@code ConfigurationListener}s (this collection is a snapshot
121
* of the currently registered listeners; manipulating it has no effect
122
* on this event source object)
124
public Collection<ConfigurationListener> getConfigurationListeners()
126
return Collections.unmodifiableCollection(new ArrayList<ConfigurationListener>(listeners));
130
* Removes all registered configuration listeners.
132
public void clearConfigurationListeners()
138
* Returns a flag whether detail events are enabled.
140
* @return a flag if detail events are generated
142
public boolean isDetailEvents()
144
return checkDetailEvents(0);
148
* Determines whether detail events should be generated. If enabled, some
149
* methods can generate multiple update events. Note that this method
150
* records the number of calls, i.e. if for instance
151
* {@code setDetailEvents(false)} was called three times, you will
152
* have to invoke the method as often to enable the details.
154
* @param enable a flag if detail events should be enabled or disabled
156
public void setDetailEvents(boolean enable)
158
synchronized (lockDetailEventsCount)
172
* Adds a new configuration error listener to this object. This listener
173
* will then be notified about internal problems.
175
* @param l the listener to register (must not be <b>null</b>)
178
public void addErrorListener(ConfigurationErrorListener l)
181
errorListeners.add(l);
185
* Removes the specified error listener so that it does not receive any
186
* further events caused by this object.
188
* @param l the listener to remove
189
* @return a flag whether the listener could be found and removed
192
public boolean removeErrorListener(ConfigurationErrorListener l)
194
return errorListeners.remove(l);
198
* Removes all registered error listeners.
202
public void clearErrorListeners()
204
errorListeners.clear();
208
* Returns a collection with all configuration error listeners that are
209
* currently registered at this object.
211
* @return a collection with the registered
212
* {@code ConfigurationErrorListener}s (this collection is a
213
* snapshot of the currently registered listeners; it cannot be manipulated)
216
public Collection<ConfigurationErrorListener> getErrorListeners()
218
return Collections.unmodifiableCollection(new ArrayList<ConfigurationErrorListener>(errorListeners));
222
* Creates an event object and delivers it to all registered event
223
* listeners. The method will check first if sending an event is allowed
224
* (making use of the {@code detailEvents} property), and if
225
* listeners are registered.
227
* @param type the event's type
228
* @param propName the name of the affected property (can be <b>null</b>)
229
* @param propValue the value of the affected property (can be <b>null</b>)
230
* @param before the before update flag
232
protected void fireEvent(int type, String propName, Object propValue, boolean before)
234
if (checkDetailEvents(-1))
236
Iterator<ConfigurationListener> it = listeners.iterator();
239
ConfigurationEvent event =
240
createEvent(type, propName, propValue, before);
243
it.next().configurationChanged(event);
250
* Creates a {@code ConfigurationEvent} object based on the passed in
251
* parameters. This is called by {@code fireEvent()} if it decides
252
* that an event needs to be generated.
254
* @param type the event's type
255
* @param propName the name of the affected property (can be <b>null</b>)
256
* @param propValue the value of the affected property (can be <b>null</b>)
257
* @param before the before update flag
258
* @return the newly created event object
260
protected ConfigurationEvent createEvent(int type, String propName, Object propValue, boolean before)
262
return new ConfigurationEvent(this, type, propName, propValue, before);
266
* Creates an error event object and delivers it to all registered error
269
* @param type the event's type
270
* @param propName the name of the affected property (can be <b>null</b>)
271
* @param propValue the value of the affected property (can be <b>null</b>)
272
* @param ex the {@code Throwable} object that caused this error event
275
protected void fireError(int type, String propName, Object propValue, Throwable ex)
277
Iterator<ConfigurationErrorListener> it = errorListeners.iterator();
280
ConfigurationErrorEvent event =
281
createErrorEvent(type, propName, propValue, ex);
284
it.next().configurationError(event);
290
* Creates a {@code ConfigurationErrorEvent} object based on the
291
* passed in parameters. This is called by {@code fireError()} if it
292
* decides that an event needs to be generated.
294
* @param type the event's type
295
* @param propName the name of the affected property (can be <b>null</b>)
296
* @param propValue the value of the affected property (can be <b>null</b>)
297
* @param ex the {@code Throwable} object that caused this error
299
* @return the event object
302
protected ConfigurationErrorEvent createErrorEvent(int type, String propName, Object propValue, Throwable ex)
304
return new ConfigurationErrorEvent(this, type, propName, propValue, ex);
308
* Overrides the {@code clone()} method to correctly handle so far
309
* registered event listeners. This implementation ensures that the clone
310
* will have empty event listener lists, i.e. the listeners registered at an
311
* {@code EventSource} object will not be copied.
313
* @return the cloned object
314
* @throws CloneNotSupportedException if cloning is not allowed
318
protected Object clone() throws CloneNotSupportedException
320
EventSource copy = (EventSource) super.clone();
321
copy.initListeners();
326
* Checks whether the specified event listener is not <b>null</b>. If this
327
* is the case, an {@code IllegalArgumentException} exception is thrown.
329
* @param l the listener to be checked
330
* @throws IllegalArgumentException if the listener is <b>null</b>
332
private static void checkListener(Object l)
336
throw new IllegalArgumentException("Listener must not be null!");
341
* Initializes the collections for storing registered event listeners.
343
private void initListeners()
345
listeners = new CopyOnWriteArrayList<ConfigurationListener>();
346
errorListeners = new CopyOnWriteArrayList<ConfigurationErrorListener>();
350
* Helper method for checking the current counter for detail events. This
351
* method checks whether the counter is greater than the passed in limit.
353
* @param limit the limit to be compared to
354
* @return <b>true</b> if the counter is greater than the limit,
355
* <b>false</b> otherwise
357
private boolean checkDetailEvents(int limit)
359
synchronized (lockDetailEventsCount)
361
return detailEvents > limit;