Coverage Report - org.apache.commons.configuration.event.EventSource
 
Classes in this File Line Coverage Branch Coverage Complexity
EventSource
98%
51/52
100%
16/16
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 &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  
  * {@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  12324
     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  12323
     {
 89  12318
         initListeners();
 90  12321
     }
 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  8303
         checkListener(l);
 100  8287
         listeners.add(l);
 101  8149
     }
 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  2313
         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  1087
         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  5618733
         synchronized (lockDetailEventsCount)
 159  
         {
 160  5661504
             if (enable)
 161  
             {
 162  2848224
                 detailEvents++;
 163  
             }
 164  
             else
 165  
             {
 166  2836603
                 detailEvents--;
 167  
             }
 168  5660739
         }
 169  5684159
     }
 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  3266
         checkListener(l);
 181  3265
         errorListeners.add(l);
 182  3265
     }
 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  409320
         if (checkDetailEvents(-1))
 235  
         {
 236  148203
             Iterator<ConfigurationListener> it = listeners.iterator();
 237  148194
             if (it.hasNext())
 238  
             {
 239  64234
                 ConfigurationEvent event =
 240  
                         createEvent(type, propName, propValue, before);
 241  133808
                 while (it.hasNext())
 242  
                 {
 243  69606
                     it.next().configurationChanged(event);
 244  
                 }
 245  
             }
 246  
         }
 247  409348
     }
 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  64224
         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  11561
         if (l == null)
 335  
         {
 336  2
             throw new IllegalArgumentException("Listener must not be null!");
 337  
         }
 338  11552
     }
 339  
 
 340  
     /**
 341  
      * Initializes the collections for storing registered event listeners.
 342  
      */
 343  
     private void initListeners()
 344  
     {
 345  12349
         listeners = new CopyOnWriteArrayList<ConfigurationListener>();
 346  12349
         errorListeners = new CopyOnWriteArrayList<ConfigurationErrorListener>();
 347  12345
     }
 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  409327
         synchronized (lockDetailEventsCount)
 360  
         {
 361  409367
             return detailEvents > limit;
 362  0
         }
 363  
     }
 364  
 }