Coverage Report - org.apache.commons.configuration.event.EventSource
 
Classes in this File Line Coverage Branch Coverage Complexity
EventSource
96%
65/68
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
 67  
  * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons
 68  
  * Configuration team</a>
 69  
  * @version $Id: EventSource.java 495918 2007-01-13 16:33:02Z oheger $
 70  
  * @since 1.3
 71  
  */
 72  
 public class EventSource
 73  
 {
 74  
     /** A collection for the registered event listeners. */
 75  
     private Collection listeners;
 76  
 
 77  
     /** A collection for the registered error listeners.*/
 78  
     private Collection errorListeners;
 79  
 
 80  
     /** A counter for the detail events. */
 81  
     private int detailEvents;
 82  
 
 83  
     /**
 84  
      * Creates a new instance of <code>EventSource</code>.
 85  
      */
 86  
     public EventSource()
 87  2614
     {
 88  2614
         initListeners();
 89  2614
     }
 90  
 
 91  
     /**
 92  
      * Adds a configuration listener to this object.
 93  
      *
 94  
      * @param l the listener to add
 95  
      */
 96  
     public void addConfigurationListener(ConfigurationListener l)
 97  
     {
 98  1478
         doAddListener(listeners, l);
 99  1477
     }
 100  
 
 101  
     /**
 102  
      * Removes the specified event listener so that it does not receive any
 103  
      * further events caused by this object.
 104  
      *
 105  
      * @param l the listener to be removed
 106  
      * @return a flag whether the event listener was found
 107  
      */
 108  
     public boolean removeConfigurationListener(ConfigurationListener l)
 109  
     {
 110  423
         return doRemoveListener(listeners, l);
 111  
     }
 112  
 
 113  
     /**
 114  
      * Returns a collection with all configuration event listeners that are
 115  
      * currently registered at this object.
 116  
      *
 117  
      * @return a collection with the registered
 118  
      * <code>ConfigurationListener</code>s (this collection is a snapshot
 119  
      * of the currently registered listeners; manipulating it has no effect
 120  
      * on this event source object)
 121  
      */
 122  
     public Collection getConfigurationListeners()
 123  
     {
 124  28
         return doGetListeners(listeners);
 125  
     }
 126  
 
 127  
     /**
 128  
      * Removes all registered configuration listeners.
 129  
      */
 130  
     public void clearConfigurationListeners()
 131  
     {
 132  5
         doClearListeners(listeners);
 133  5
     }
 134  
 
 135  
     /**
 136  
      * Returns a flag whether detail events are enabled.
 137  
      *
 138  
      * @return a flag if detail events are generated
 139  
      */
 140  
     public boolean isDetailEvents()
 141  
     {
 142  4
         synchronized (listeners)
 143  
         {
 144  4
             return detailEvents > 0;
 145  0
         }
 146  
     }
 147  
 
 148  
     /**
 149  
      * Determines whether detail events should be generated. If enabled, some
 150  
      * methods can generate multiple update events. Note that this method
 151  
      * records the number of calls, i.e. if for instance
 152  
      * <code>setDetailEvents(false)</code> was called three times, you will
 153  
      * have to invoke the method as often to enable the details.
 154  
      *
 155  
      * @param enable a flag if detail events should be enabled or disabled
 156  
      */
 157  
     public void setDetailEvents(boolean enable)
 158  
     {
 159  10549
         synchronized (listeners)
 160  
         {
 161  10549
             if (enable)
 162  
             {
 163  5283
                 detailEvents++;
 164  
             }
 165  
             else
 166  
             {
 167  5266
                 detailEvents--;
 168  
             }
 169  10549
         }
 170  10549
     }
 171  
 
 172  
     /**
 173  
      * Adds a new configuration error listener to this object. This listener
 174  
      * will then be notified about internal problems.
 175  
      *
 176  
      * @param l the listener to register (must not be <b>null</b>)
 177  
      * @since 1.4
 178  
      */
 179  
     public void addErrorListener(ConfigurationErrorListener l)
 180  
     {
 181  1036
         doAddListener(errorListeners, l);
 182  1035
     }
 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 doRemoveListener(errorListeners, l);
 195  
     }
 196  
 
 197  
     /**
 198  
      * Removes all registered error listeners.
 199  
      *
 200  
      * @since 1.4
 201  
      */
 202  
     public void clearErrorListeners()
 203  
     {
 204  2
         doClearListeners(errorListeners);
 205  2
     }
 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</code>s (this collection is a
 213  
      * snapshot of the currently registered listeners; it cannot be manipulated)
 214  
      * @since 1.4
 215  
      */
 216  
     public Collection getErrorListeners()
 217  
     {
 218  20
         return doGetListeners(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</code> 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,
 233  
             boolean before)
 234  
     {
 235  87618
         Collection listenersToCall = null;
 236  
 
 237  87618
         synchronized (listeners)
 238  
         {
 239  87618
             if (detailEvents >= 0 && listeners.size() > 0)
 240  
             {
 241  
                 // Copy listeners to another collection so that manipulating
 242  
                 // the listener list during event delivery won't cause problems
 243  595
                 listenersToCall = new ArrayList(listeners);
 244  
             }
 245  87618
         }
 246  
 
 247  87618
         if (listenersToCall != null)
 248  
         {
 249  595
             ConfigurationEvent event = createEvent(type, propName, propValue,
 250  
                     before);
 251  1836
             for (Iterator it = listenersToCall.iterator(); it.hasNext();)
 252  
             {
 253  646
                 ((ConfigurationListener) it.next()).configurationChanged(event);
 254  
             }
 255  
         }
 256  87618
     }
 257  
 
 258  
     /**
 259  
      * Creates a <code>ConfigurationEvent</code> object based on the passed in
 260  
      * parameters. This is called by <code>fireEvent()</code> if it decides
 261  
      * that an event needs to be generated.
 262  
      *
 263  
      * @param type the event's type
 264  
      * @param propName the name of the affected property (can be <b>null</b>)
 265  
      * @param propValue the value of the affected property (can be <b>null</b>)
 266  
      * @param before the before update flag
 267  
      * @return the newly created event object
 268  
      */
 269  
     protected ConfigurationEvent createEvent(int type, String propName,
 270  
             Object propValue, boolean before)
 271  
     {
 272  595
         return new ConfigurationEvent(this, type, propName, propValue, before);
 273  
     }
 274  
 
 275  
     /**
 276  
      * Creates an error event object and delivers it to all registered error
 277  
      * listeners.
 278  
      *
 279  
      * @param type the event's type
 280  
      * @param propName the name of the affected property (can be <b>null</b>)
 281  
      * @param propValue the value of the affected property (can be <b>null</b>)
 282  
      * @param ex the <code>Throwable</code> object that caused this error
 283  
      * event
 284  
      * @since 1.4
 285  
      */
 286  
     protected void fireError(int type, String propName, Object propValue,
 287  
             Throwable ex)
 288  
     {
 289  15
         Collection listenersToCall = null;
 290  
 
 291  15
         synchronized (errorListeners)
 292  
         {
 293  15
             if (errorListeners.size() > 0)
 294  
             {
 295  
                 // Copy listeners to another collection so that manipulating
 296  
                 // the listener list during event delivery won't cause problems
 297  14
                 listenersToCall = new ArrayList(errorListeners);
 298  
             }
 299  15
         }
 300  
 
 301  15
         if (listenersToCall != null)
 302  
         {
 303  14
             ConfigurationErrorEvent event = createErrorEvent(type, propName,
 304  
                     propValue, ex);
 305  41
             for (Iterator it = listenersToCall.iterator(); it.hasNext();)
 306  
             {
 307  14
                 ((ConfigurationErrorListener) it.next())
 308  
                         .configurationError(event);
 309  
             }
 310  
         }
 311  14
     }
 312  
 
 313  
     /**
 314  
      * Creates a <code>ConfigurationErrorEvent</code> object based on the
 315  
      * passed in parameters. This is called by <code>fireError()</code> if it
 316  
      * decides that an event needs to be generated.
 317  
      *
 318  
      * @param type the event's type
 319  
      * @param propName the name of the affected property (can be <b>null</b>)
 320  
      * @param propValue the value of the affected property (can be <b>null</b>)
 321  
      * @param ex the <code>Throwable</code> object that caused this error
 322  
      * event
 323  
      * @return the event object
 324  
      * @since 1.4
 325  
      */
 326  
     protected ConfigurationErrorEvent createErrorEvent(int type,
 327  
             String propName, Object propValue, Throwable ex)
 328  
     {
 329  14
         return new ConfigurationErrorEvent(this, type, propName, propValue, ex);
 330  
     }
 331  
 
 332  
     /**
 333  
      * Overrides the <code>clone()</code> method to correctly handle so far
 334  
      * registered event listeners. This implementation ensures that the clone
 335  
      * will have empty event listener lists, i.e. the listeners registered at an
 336  
      * <code>EventSource</code> object will not be copied.
 337  
      *
 338  
      * @return the cloned object
 339  
      * @throws CloneNotSupportedException if cloning is not allowed
 340  
      * @since 1.4
 341  
      */
 342  
     protected Object clone() throws CloneNotSupportedException
 343  
     {
 344  25
         EventSource copy = (EventSource) super.clone();
 345  25
         copy.initListeners();
 346  25
         return copy;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Adds a new listener object to a listener collection. This is done in a
 351  
      * synchronized block. The listener must not be <b>null</b>.
 352  
      *
 353  
      * @param listeners the collection with the listeners
 354  
      * @param l the listener object
 355  
      */
 356  
     private static void doAddListener(Collection listeners, Object l)
 357  
     {
 358  2514
         if (l == null)
 359  
         {
 360  2
             throw new IllegalArgumentException("Listener must not be null!");
 361  
         }
 362  2512
         synchronized (listeners)
 363  
         {
 364  2512
             listeners.add(l);
 365  2512
         }
 366  2512
     }
 367  
 
 368  
     /**
 369  
      * Removes an event listener from a listener collection. This is done in a
 370  
      * synchronized block.
 371  
      *
 372  
      * @param listeners the collection with the listeners
 373  
      * @param l the listener object
 374  
      * @return a flag whether the listener could be found and removed
 375  
      */
 376  
     private static boolean doRemoveListener(Collection listeners, Object l)
 377  
     {
 378  438
         synchronized (listeners)
 379  
         {
 380  438
             return listeners.remove(l);
 381  0
         }
 382  
     }
 383  
 
 384  
     /**
 385  
      * Removes all entries from the given list of event listeners.
 386  
      *
 387  
      * @param listeners the collection with the listeners
 388  
      */
 389  
     private static void doClearListeners(Collection listeners)
 390  
     {
 391  7
         synchronized (listeners)
 392  
         {
 393  7
             listeners.clear();
 394  7
         }
 395  7
     }
 396  
 
 397  
     /**
 398  
      * Returns an unmodifiable snapshot of the given event listener collection.
 399  
      *
 400  
      * @param listeners the collection with the listeners
 401  
      * @return a snapshot of the listeners collection
 402  
      */
 403  
     private static Collection doGetListeners(Collection listeners)
 404  
     {
 405  48
         synchronized (listeners)
 406  
         {
 407  48
             return Collections.unmodifiableCollection(new ArrayList(listeners));
 408  0
         }
 409  
     }
 410  
 
 411  
     /**
 412  
      * Initializes the collections for storing registered event listeners.
 413  
      */
 414  
     private void initListeners()
 415  
     {
 416  2639
         listeners = new LinkedList();
 417  2639
         errorListeners = new LinkedList();
 418  2639
     }
 419  
 }