Coverage Report - org.apache.commons.configuration.event.EventSource
 
Classes in this File Line Coverage Branch Coverage Complexity
EventSource
96%
68/71
100%
9/9
1,429
 
 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.LinkedList;
 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 machanism. In this case these
 33  
  * classes only need to call the <code>fireEvent()</code> 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</code> 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()</code> method that can be implemented as a
 45  
  * combination of <code>clearProperty()</code> and <code>addProperty()</code>.
 46  
  * With <code>detailEvents</code> 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  
  * <code>{@link org.apache.commons.configuration.HierarchicalConfiguration HierarchicalConfiguration}</code>
 53  
  * for instance has a custom implementation of <code>setProperty()</code>,
 54  
  * which does not generate any detail events.
 55  
  * </p>
 56  
  * <p>
 57  
  * In addition to &quot;normal&quot; 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  
  * <code>{@link ConfigurationErrorListener}</code>. There is another set of
 61  
  * methods dealing with event listeners of this type. The
 62  
  * <code>fireError()</code> 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 561230 2007-07-31 04:17:09Z rahul $
 68  
  * @since 1.3
 69  
  */
 70  
 public class EventSource
 71  
 {
 72  
     /** A collection for the registered event listeners. */
 73  
     private Collection listeners;
 74  
 
 75  
     /** A collection for the registered error listeners.*/
 76  
     private Collection errorListeners;
 77  
 
 78  
     /** A counter for the detail events. */
 79  
     private int detailEvents;
 80  
 
 81  
     /**
 82  
      * Creates a new instance of <code>EventSource</code>.
 83  
      */
 84  
     public EventSource()
 85  3335
     {
 86  3335
         initListeners();
 87  3335
     }
 88  
 
 89  
     /**
 90  
      * Adds a configuration listener to this object.
 91  
      *
 92  
      * @param l the listener to add
 93  
      */
 94  
     public void addConfigurationListener(ConfigurationListener l)
 95  
     {
 96  2088
         doAddListener(listeners, l);
 97  2087
     }
 98  
 
 99  
     /**
 100  
      * Removes the specified event listener so that it does not receive any
 101  
      * further events caused by this object.
 102  
      *
 103  
      * @param l the listener to be removed
 104  
      * @return a flag whether the event listener was found
 105  
      */
 106  
     public boolean removeConfigurationListener(ConfigurationListener l)
 107  
     {
 108  451
         return doRemoveListener(listeners, l);
 109  
     }
 110  
 
 111  
     /**
 112  
      * Returns a collection with all configuration event listeners that are
 113  
      * currently registered at this object.
 114  
      *
 115  
      * @return a collection with the registered
 116  
      * <code>ConfigurationListener</code>s (this collection is a snapshot
 117  
      * of the currently registered listeners; manipulating it has no effect
 118  
      * on this event source object)
 119  
      */
 120  
     public Collection getConfigurationListeners()
 121  
     {
 122  28
         return doGetListeners(listeners);
 123  
     }
 124  
 
 125  
     /**
 126  
      * Removes all registered configuration listeners.
 127  
      */
 128  
     public void clearConfigurationListeners()
 129  
     {
 130  5
         doClearListeners(listeners);
 131  5
     }
 132  
 
 133  
     /**
 134  
      * Returns a flag whether detail events are enabled.
 135  
      *
 136  
      * @return a flag if detail events are generated
 137  
      */
 138  
     public boolean isDetailEvents()
 139  
     {
 140  4
         synchronized (listeners)
 141  
         {
 142  4
             return detailEvents > 0;
 143  0
         }
 144  
     }
 145  
 
 146  
     /**
 147  
      * Determines whether detail events should be generated. If enabled, some
 148  
      * methods can generate multiple update events. Note that this method
 149  
      * records the number of calls, i.e. if for instance
 150  
      * <code>setDetailEvents(false)</code> was called three times, you will
 151  
      * have to invoke the method as often to enable the details.
 152  
      *
 153  
      * @param enable a flag if detail events should be enabled or disabled
 154  
      */
 155  
     public void setDetailEvents(boolean enable)
 156  
     {
 157  15355
         synchronized (listeners)
 158  
         {
 159  15355
             if (enable)
 160  
             {
 161  7690
                 detailEvents++;
 162  7690
             }
 163  
             else
 164  
             {
 165  7665
                 detailEvents--;
 166  
             }
 167  15355
         }
 168  15355
     }
 169  
 
 170  
     /**
 171  
      * Adds a new configuration error listener to this object. This listener
 172  
      * will then be notified about internal problems.
 173  
      *
 174  
      * @param l the listener to register (must not be <b>null</b>)
 175  
      * @since 1.4
 176  
      */
 177  
     public void addErrorListener(ConfigurationErrorListener l)
 178  
     {
 179  1399
         doAddListener(errorListeners, l);
 180  1398
     }
 181  
 
 182  
     /**
 183  
      * Removes the specified error listener so that it does not receive any
 184  
      * further events caused by this object.
 185  
      *
 186  
      * @param l the listener to remove
 187  
      * @return a flag whether the listener could be found and removed
 188  
      * @since 1.4
 189  
      */
 190  
     public boolean removeErrorListener(ConfigurationErrorListener l)
 191  
     {
 192  15
         return doRemoveListener(errorListeners, l);
 193  
     }
 194  
 
 195  
     /**
 196  
      * Removes all registered error listeners.
 197  
      *
 198  
      * @since 1.4
 199  
      */
 200  
     public void clearErrorListeners()
 201  
     {
 202  35
         doClearListeners(errorListeners);
 203  35
     }
 204  
 
 205  
     /**
 206  
      * Returns a collection with all configuration error listeners that are
 207  
      * currently registered at this object.
 208  
      *
 209  
      * @return a collection with the registered
 210  
      * <code>ConfigurationErrorListener</code>s (this collection is a
 211  
      * snapshot of the currently registered listeners; it cannot be manipulated)
 212  
      * @since 1.4
 213  
      */
 214  
     public Collection getErrorListeners()
 215  
     {
 216  21
         return doGetListeners(errorListeners);
 217  
     }
 218  
 
 219  
     /**
 220  
      * Creates an event object and delivers it to all registered event
 221  
      * listeners. The method will check first if sending an event is allowed
 222  
      * (making use of the <code>detailEvents</code> property), and if
 223  
      * listeners are registered.
 224  
      *
 225  
      * @param type the event's type
 226  
      * @param propName the name of the affected property (can be <b>null</b>)
 227  
      * @param propValue the value of the affected property (can be <b>null</b>)
 228  
      * @param before the before update flag
 229  
      */
 230  
     protected void fireEvent(int type, String propName, Object propValue, boolean before)
 231  
     {
 232  78450
         Collection listenersToCall = null;
 233  
 
 234  78450
         synchronized (listeners)
 235  
         {
 236  78450
             if (detailEvents >= 0 && listeners.size() > 0)
 237  
             {
 238  
                 // Copy listeners to another collection so that manipulating
 239  
                 // the listener list during event delivery won't cause problems
 240  916
                 listenersToCall = new ArrayList(listeners);
 241  
             }
 242  78450
         }
 243  
 
 244  78450
         if (listenersToCall != null)
 245  
         {
 246  916
             ConfigurationEvent event = createEvent(type, propName, propValue, before);
 247  916
             for (Iterator it = listenersToCall.iterator(); it.hasNext();)
 248  
             {
 249  969
                 ((ConfigurationListener) it.next()).configurationChanged(event);
 250  969
             }
 251  
         }
 252  78450
     }
 253  
 
 254  
     /**
 255  
      * Creates a <code>ConfigurationEvent</code> object based on the passed in
 256  
      * parameters. This is called by <code>fireEvent()</code> if it decides
 257  
      * that an event needs to be generated.
 258  
      *
 259  
      * @param type the event's type
 260  
      * @param propName the name of the affected property (can be <b>null</b>)
 261  
      * @param propValue the value of the affected property (can be <b>null</b>)
 262  
      * @param before the before update flag
 263  
      * @return the newly created event object
 264  
      */
 265  
     protected ConfigurationEvent createEvent(int type, String propName, Object propValue, boolean before)
 266  
     {
 267  916
         return new ConfigurationEvent(this, type, propName, propValue, before);
 268  
     }
 269  
 
 270  
     /**
 271  
      * Creates an error event object and delivers it to all registered error
 272  
      * listeners.
 273  
      *
 274  
      * @param type the event's type
 275  
      * @param propName the name of the affected property (can be <b>null</b>)
 276  
      * @param propValue the value of the affected property (can be <b>null</b>)
 277  
      * @param ex the <code>Throwable</code> object that caused this error event
 278  
      * @since 1.4
 279  
      */
 280  
     protected void fireError(int type, String propName, Object propValue, Throwable ex)
 281  
     {
 282  28
         Collection listenersToCall = null;
 283  
 
 284  28
         synchronized (errorListeners)
 285  
         {
 286  28
             if (errorListeners.size() > 0)
 287  
             {
 288  
                 // Copy listeners to another collection so that manipulating
 289  
                 // the listener list during event delivery won't cause problems
 290  15
                 listenersToCall = new ArrayList(errorListeners);
 291  
             }
 292  28
         }
 293  
 
 294  28
         if (listenersToCall != null)
 295  
         {
 296  15
             ConfigurationErrorEvent event = createErrorEvent(type, propName, propValue, ex);
 297  15
             for (Iterator it = listenersToCall.iterator(); it.hasNext();)
 298  
             {
 299  15
                 ((ConfigurationErrorListener) it.next()).configurationError(event);
 300  14
             }
 301  
         }
 302  27
     }
 303  
 
 304  
     /**
 305  
      * Creates a <code>ConfigurationErrorEvent</code> object based on the
 306  
      * passed in parameters. This is called by <code>fireError()</code> if it
 307  
      * decides that an event needs to be generated.
 308  
      *
 309  
      * @param type the event's type
 310  
      * @param propName the name of the affected property (can be <b>null</b>)
 311  
      * @param propValue the value of the affected property (can be <b>null</b>)
 312  
      * @param ex the <code>Throwable</code> object that caused this error
 313  
      * event
 314  
      * @return the event object
 315  
      * @since 1.4
 316  
      */
 317  
     protected ConfigurationErrorEvent createErrorEvent(int type, String propName, Object propValue, Throwable ex)
 318  
     {
 319  15
         return new ConfigurationErrorEvent(this, type, propName, propValue, ex);
 320  
     }
 321  
 
 322  
     /**
 323  
      * Overrides the <code>clone()</code> method to correctly handle so far
 324  
      * registered event listeners. This implementation ensures that the clone
 325  
      * will have empty event listener lists, i.e. the listeners registered at an
 326  
      * <code>EventSource</code> object will not be copied.
 327  
      *
 328  
      * @return the cloned object
 329  
      * @throws CloneNotSupportedException if cloning is not allowed
 330  
      * @since 1.4
 331  
      */
 332  
     protected Object clone() throws CloneNotSupportedException
 333  
     {
 334  27
         EventSource copy = (EventSource) super.clone();
 335  27
         copy.initListeners();
 336  27
         return copy;
 337  
     }
 338  
 
 339  
     /**
 340  
      * Adds a new listener object to a listener collection. This is done in a
 341  
      * synchronized block. The listener must not be <b>null</b>.
 342  
      *
 343  
      * @param listeners the collection with the listeners
 344  
      * @param l the listener object
 345  
      */
 346  
     private static void doAddListener(Collection listeners, Object l)
 347  
     {
 348  3487
         if (l == null)
 349  
         {
 350  2
             throw new IllegalArgumentException("Listener must not be null!");
 351  
         }
 352  3485
         synchronized (listeners)
 353  
         {
 354  3485
             listeners.add(l);
 355  3485
         }
 356  3485
     }
 357  
 
 358  
     /**
 359  
      * Removes an event listener from a listener collection. This is done in a
 360  
      * synchronized block.
 361  
      *
 362  
      * @param listeners the collection with the listeners
 363  
      * @param l the listener object
 364  
      * @return a flag whether the listener could be found and removed
 365  
      */
 366  
     private static boolean doRemoveListener(Collection listeners, Object l)
 367  
     {
 368  466
         synchronized (listeners)
 369  
         {
 370  466
             return listeners.remove(l);
 371  0
         }
 372  
     }
 373  
 
 374  
     /**
 375  
      * Removes all entries from the given list of event listeners.
 376  
      *
 377  
      * @param listeners the collection with the listeners
 378  
      */
 379  
     private static void doClearListeners(Collection listeners)
 380  
     {
 381  40
         synchronized (listeners)
 382  
         {
 383  40
             listeners.clear();
 384  40
         }
 385  40
     }
 386  
 
 387  
     /**
 388  
      * Returns an unmodifiable snapshot of the given event listener collection.
 389  
      *
 390  
      * @param listeners the collection with the listeners
 391  
      * @return a snapshot of the listeners collection
 392  
      */
 393  
     private static Collection doGetListeners(Collection listeners)
 394  
     {
 395  49
         synchronized (listeners)
 396  
         {
 397  49
             return Collections.unmodifiableCollection(new ArrayList(listeners));
 398  0
         }
 399  
     }
 400  
 
 401  
     /**
 402  
      * Initializes the collections for storing registered event listeners.
 403  
      */
 404  
     private void initListeners()
 405  
     {
 406  3362
         listeners = new LinkedList();
 407  3362
         errorListeners = new LinkedList();
 408  3362
     }
 409  
 }