Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
EventSource |
|
| 1.4210526315789473;1,421 |
1 | /* | |
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 | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
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. | |
16 | */ | |
17 | package org.apache.commons.configuration.event; | |
18 | ||
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; | |
24 | ||
25 | /** | |
26 | * <p> | |
27 | * A base class for objects that can generate configuration events. | |
28 | * </p> | |
29 | * <p> | |
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. | |
35 | * </p> | |
36 | * <p> | |
37 | * Adding and removing event listeners can happen concurrently to manipulations | |
38 | * on a configuration that cause events. The operations are synchronized. | |
39 | * </p> | |
40 | * <p> | |
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. | |
55 | * </p> | |
56 | * <p> | |
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. | |
64 | * </p> | |
65 | * | |
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 $ | |
68 | * @since 1.3 | |
69 | */ | |
70 | public class EventSource | |
71 | { | |
72 | /** A collection for the registered event listeners. */ | |
73 | private Collection<ConfigurationListener> listeners; | |
74 | ||
75 | /** A collection for the registered error listeners.*/ | |
76 | private Collection<ConfigurationErrorListener> errorListeners; | |
77 | ||
78 | /** A lock object for guarding access to the detail events counter. */ | |
79 | 12310 | private final Object lockDetailEventsCount = new Object(); |
80 | ||
81 | /** A counter for the detail events. */ | |
82 | private int detailEvents; | |
83 | ||
84 | /** | |
85 | * Creates a new instance of {@code EventSource}. | |
86 | */ | |
87 | public EventSource() | |
88 | 12310 | { |
89 | 12310 | initListeners(); |
90 | 12310 | } |
91 | ||
92 | /** | |
93 | * Adds a configuration listener to this object. | |
94 | * | |
95 | * @param l the listener to add | |
96 | */ | |
97 | public void addConfigurationListener(ConfigurationListener l) | |
98 | { | |
99 | 8278 | checkListener(l); |
100 | 8277 | listeners.add(l); |
101 | 8277 | } |
102 | ||
103 | /** | |
104 | * Removes the specified event listener so that it does not receive any | |
105 | * further events caused by this object. | |
106 | * | |
107 | * @param l the listener to be removed | |
108 | * @return a flag whether the event listener was found | |
109 | */ | |
110 | public boolean removeConfigurationListener(ConfigurationListener l) | |
111 | { | |
112 | 2284 | return listeners.remove(l); |
113 | } | |
114 | ||
115 | /** | |
116 | * Returns a collection with all configuration event listeners that are | |
117 | * currently registered at this object. | |
118 | * | |
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) | |
123 | */ | |
124 | public Collection<ConfigurationListener> getConfigurationListeners() | |
125 | { | |
126 | 1058 | return Collections.unmodifiableCollection(new ArrayList<ConfigurationListener>(listeners)); |
127 | } | |
128 | ||
129 | /** | |
130 | * Removes all registered configuration listeners. | |
131 | */ | |
132 | public void clearConfigurationListeners() | |
133 | { | |
134 | 19 | listeners.clear(); |
135 | 19 | } |
136 | ||
137 | /** | |
138 | * Returns a flag whether detail events are enabled. | |
139 | * | |
140 | * @return a flag if detail events are generated | |
141 | */ | |
142 | public boolean isDetailEvents() | |
143 | { | |
144 | 4 | return checkDetailEvents(0); |
145 | } | |
146 | ||
147 | /** | |
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. | |
153 | * | |
154 | * @param enable a flag if detail events should be enabled or disabled | |
155 | */ | |
156 | public void setDetailEvents(boolean enable) | |
157 | { | |
158 | 447019 | synchronized (lockDetailEventsCount) |
159 | { | |
160 | 447019 | if (enable) |
161 | { | |
162 | 223526 | detailEvents++; |
163 | } | |
164 | else | |
165 | { | |
166 | 223493 | detailEvents--; |
167 | } | |
168 | 447019 | } |
169 | 447019 | } |
170 | ||
171 | /** | |
172 | * Adds a new configuration error listener to this object. This listener | |
173 | * will then be notified about internal problems. | |
174 | * | |
175 | * @param l the listener to register (must not be <b>null</b>) | |
176 | * @since 1.4 | |
177 | */ | |
178 | public void addErrorListener(ConfigurationErrorListener l) | |
179 | { | |
180 | 3256 | checkListener(l); |
181 | 3255 | errorListeners.add(l); |
182 | 3255 | } |
183 | ||
184 | /** | |
185 | * Removes the specified error listener so that it does not receive any | |
186 | * further events caused by this object. | |
187 | * | |
188 | * @param l the listener to remove | |
189 | * @return a flag whether the listener could be found and removed | |
190 | * @since 1.4 | |
191 | */ | |
192 | public boolean removeErrorListener(ConfigurationErrorListener l) | |
193 | { | |
194 | 15 | return errorListeners.remove(l); |
195 | } | |
196 | ||
197 | /** | |
198 | * Removes all registered error listeners. | |
199 | * | |
200 | * @since 1.4 | |
201 | */ | |
202 | public void clearErrorListeners() | |
203 | { | |
204 | 119 | errorListeners.clear(); |
205 | 119 | } |
206 | ||
207 | /** | |
208 | * Returns a collection with all configuration error listeners that are | |
209 | * currently registered at this object. | |
210 | * | |
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) | |
214 | * @since 1.4 | |
215 | */ | |
216 | public Collection<ConfigurationErrorListener> getErrorListeners() | |
217 | { | |
218 | 101 | return Collections.unmodifiableCollection(new ArrayList<ConfigurationErrorListener>(errorListeners)); |
219 | } | |
220 | ||
221 | /** | |
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. | |
226 | * | |
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 | |
231 | */ | |
232 | protected void fireEvent(int type, String propName, Object propValue, boolean before) | |
233 | { | |
234 | 404932 | if (checkDetailEvents(-1)) |
235 | { | |
236 | 146691 | Iterator<ConfigurationListener> it = listeners.iterator(); |
237 | 146691 | if (it.hasNext()) |
238 | { | |
239 | 63702 | ConfigurationEvent event = |
240 | createEvent(type, propName, propValue, before); | |
241 | 132612 | while (it.hasNext()) |
242 | { | |
243 | 68910 | it.next().configurationChanged(event); |
244 | } | |
245 | } | |
246 | } | |
247 | 404932 | } |
248 | ||
249 | /** | |
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. | |
253 | * | |
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 | |
259 | */ | |
260 | protected ConfigurationEvent createEvent(int type, String propName, Object propValue, boolean before) | |
261 | { | |
262 | 63702 | return new ConfigurationEvent(this, type, propName, propValue, before); |
263 | } | |
264 | ||
265 | /** | |
266 | * Creates an error event object and delivers it to all registered error | |
267 | * listeners. | |
268 | * | |
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 | |
273 | * @since 1.4 | |
274 | */ | |
275 | protected void fireError(int type, String propName, Object propValue, Throwable ex) | |
276 | { | |
277 | 51 | Iterator<ConfigurationErrorListener> it = errorListeners.iterator(); |
278 | 51 | if (it.hasNext()) |
279 | { | |
280 | 20 | ConfigurationErrorEvent event = |
281 | createErrorEvent(type, propName, propValue, ex); | |
282 | 37 | while (it.hasNext()) |
283 | { | |
284 | 22 | it.next().configurationError(event); |
285 | } | |
286 | } | |
287 | 46 | } |
288 | ||
289 | /** | |
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. | |
293 | * | |
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 | |
298 | * event | |
299 | * @return the event object | |
300 | * @since 1.4 | |
301 | */ | |
302 | protected ConfigurationErrorEvent createErrorEvent(int type, String propName, Object propValue, Throwable ex) | |
303 | { | |
304 | 20 | return new ConfigurationErrorEvent(this, type, propName, propValue, ex); |
305 | } | |
306 | ||
307 | /** | |
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. | |
312 | * | |
313 | * @return the cloned object | |
314 | * @throws CloneNotSupportedException if cloning is not allowed | |
315 | * @since 1.4 | |
316 | */ | |
317 | @Override | |
318 | protected Object clone() throws CloneNotSupportedException | |
319 | { | |
320 | 28 | EventSource copy = (EventSource) super.clone(); |
321 | 28 | copy.initListeners(); |
322 | 28 | return copy; |
323 | } | |
324 | ||
325 | /** | |
326 | * Checks whether the specified event listener is not <b>null</b>. If this | |
327 | * is the case, an {@code IllegalArgumentException} exception is thrown. | |
328 | * | |
329 | * @param l the listener to be checked | |
330 | * @throws IllegalArgumentException if the listener is <b>null</b> | |
331 | */ | |
332 | private static void checkListener(Object l) | |
333 | { | |
334 | 11534 | if (l == null) |
335 | { | |
336 | 2 | throw new IllegalArgumentException("Listener must not be null!"); |
337 | } | |
338 | 11532 | } |
339 | ||
340 | /** | |
341 | * Initializes the collections for storing registered event listeners. | |
342 | */ | |
343 | private void initListeners() | |
344 | { | |
345 | 12338 | listeners = new CopyOnWriteArrayList<ConfigurationListener>(); |
346 | 12338 | errorListeners = new CopyOnWriteArrayList<ConfigurationErrorListener>(); |
347 | 12338 | } |
348 | ||
349 | /** | |
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. | |
352 | * | |
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 | |
356 | */ | |
357 | private boolean checkDetailEvents(int limit) | |
358 | { | |
359 | 404936 | synchronized (lockDetailEventsCount) |
360 | { | |
361 | 404936 | return detailEvents > limit; |
362 | 0 | } |
363 | } | |
364 | } |