hlship      2003/09/29 13:56:19

  Modified:    hivemind/framework/src/java/org/apache/commons/hivemind/service/impl
                        ThreadEventNotifierImpl.java
  Added:       hivemind/framework/src/test/hivemind/test/util
                        TestEventListenerList.java
               hivemind/framework/src/java/org/apache/commons/hivemind/util
                        EventListenerList.java
  Log:
  Create utility class EventListenerList, used for managing listeners lists (for 
services like ThreadEventNotifier).
  
  Revision  Changes    Path
  1.1                  
jakarta-commons-sandbox/hivemind/framework/src/test/hivemind/test/util/TestEventListenerList.java
  
  Index: TestEventListenerList.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package hivemind.test.util;
  
  import hivemind.test.FrameworkTestCase;
  
  import java.util.Iterator;
  
  import org.apache.commons.hivemind.util.EventListenerList;
  
  /**
   * Tests for [EMAIL PROTECTED] org.apache.commons.hivemind.util.EventListenerList}.
   *
   * @author Howard Lewis Ship
   * @version $Id: TestEventListenerList.java,v 1.1 2003/09/29 20:56:18 hlship Exp $
   */
  public class TestEventListenerList extends FrameworkTestCase
  {
      private static class Trigger
      {
          private boolean _trigger;
  
          public boolean isTrigger()
          {
              return _trigger;
          }
  
          public void setTrigger(boolean b)
          {
              _trigger = b;
          }
  
      }
  
      public TestEventListenerList(String name)
      {
          super(name);
      }
  
      private Trigger[] buildTriggers(int count)
      {
          Trigger[] result = new Trigger[count];
  
          for (int i = 0; i < count; i++)
          {
              result[i] = new Trigger();
          }
  
          return result;
      }
  
      private void addAll(EventListenerList l, Trigger[] t)
      {
          for (int i = 0; i < t.length; i++)
              l.addListener(t[i]);
      }
  
      private void checkAllTrue(Trigger[] t)
      {
          for (int i = 0; i < t.length; i++)
              assertEquals(true, t[i].isTrigger());
      }
  
      public void testBasic()
      {
          EventListenerList l = new EventListenerList();
  
          Trigger[] ta = buildTriggers(20);
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
  
              t.setTrigger(true);
          }
  
      }
  
      public void testEmptyList()
      {
          EventListenerList l = new EventListenerList();
  
          Iterator i = l.getListeners();
  
          assertEquals(false, i.hasNext());
      }
  
      public void testLateAdd()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          Trigger tnew = new Trigger();
          l.addListener(tnew);
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          assertEquals(false, tnew.isTrigger());
  
          checkAllTrue(ta);
      }
  
      public void testLateRemove()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          Trigger tremoved = ta[15];
          l.removeListener(tremoved);
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          checkAllTrue(ta);
      }
  
      public void testRemoveMissing()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Trigger tremove = new Trigger();
  
          l.removeListener(tremove);
  
          Iterator i = l.getListeners();
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          checkAllTrue(ta);
      }
  
      public void testIteratorRemoveFailure()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
              i.next();
  
          try
          {
              i.remove();
              unreachable();
          }
          catch (UnsupportedOperationException ex)
          {
          }
      }
  }
  
  
  
  1.1                  
jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/util/EventListenerList.java
  
  Index: EventListenerList.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.hivemind.util;
  
  import java.util.Iterator;
  
  /**
   * Convienience class for tracking a list of event listeners. Works efficiently
   * (using a copy-on-write approach) to iterating through the listeners in
   * the list even when the list of listeners may be modified.
   * 
   * <p>
   * EventListenerList is <em>not</em> thread-safe.
   *
   * @author Howard Lewis Ship
   * @version $Id: EventListenerList.java,v 1.1 2003/09/29 20:56:19 hlship Exp $
   */
  public class EventListenerList
  {
      private static final int START_SIZE = 5;
  
      private Object[] _listeners;
      private int _count;
      private int _iteratorCount;
      private int _uid;
  
      private class ListenerIterator implements Iterator
      {
        private Object[] _localListeners;
        private int _localCount;
        private int _localUid;
        private int _pos;
        
        private ListenerIterator()
        {
                _localListeners = _listeners;
                _localCount = _count;
                _localUid = _uid;
        }
        
          public boolean hasNext()
          {
                        if (_pos >= _localCount)
                        {
                                // If _listeners has not been recopied during the 
lifespan
                                // of this iterator, then knock the count down by one.
                                
                                if (_uid == _localUid)
                                        _iteratorCount--;
        
                                _localListeners = null;
                                _localCount = 0;
                                _localUid = -1;
                                _pos = 0;
                                
                                return false;                   
                        }
                        
                        return true;
          }
  
          public Object next()
          {
                        return _localListeners[_pos++];
          }
  
          public void remove()
          {
              throw new UnsupportedOperationException();
          }
  
      }
      
      /**
       * Returns an Iterator used to find all the listeners previously added.
       * The order in which listeners are returned is not guaranteed.
       * Currently, you may not invoke <code>remove()</code> on the Iterator.
       */
        public Iterator getListeners()
        {
                _iteratorCount++;
                
                return new ListenerIterator();
        }
  
        /**
         * Adds a new listener to the list of listeners. The same instance
         * will may be added multiple times.
         */
      public void addListener(Object listener)
      {
          copyOnWrite(_count + 1);
  
          _listeners[_count] = listener;
  
          _count++;
      }
  
        /**
         * Removes a listener from the list.  Does nothing if the listener
         * is not already in the list. Comparison is based on identity, not equality.
         * If the listener is in the list multiple times, only a single
         * instance is removed.
         */
      public void removeListener(Object listener)
      {
          for (int i = 0; i < _count; i++)
          {
              if (_listeners[i] == listener)
              {
                                removeListener(i);
                  return;
              }
          }
      }
      
      private void removeListener(int index)
      {
                copyOnWrite(_count);
  
                // Move the last listener in the list into the index to be removed.
        
        _listeners[index] = _listeners[_count - 1];
        
        // Null out the old position.
        
        _listeners[_count - 1] = null;
        
        _count--;
      }
  
        /**
         * Copies the array before an update operation if necessary (because there
         * is a known iterator for the current array, or because the 
         * array is not large enough).
         */
      private void copyOnWrite(int requiredSize)
      {
          int size = _listeners == null ? 0 : _listeners.length;
  
          if (_iteratorCount > 0 || size < requiredSize)
          {
              int newSize = Math.max(requiredSize, START_SIZE);
  
              Object[] newListeners = new Object[newSize];
  
              if (_count > 0)
                  System.arraycopy(_listeners, 0, newListeners, 0, _count);
  
              _listeners = newListeners;
  
              // No iterators on the *new* array
              _iteratorCount = 0;
              _uid++;
          }
      }
  
  }
  
  
  
  1.2       +14 -17    
jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl/ThreadEventNotifierImpl.java
  
  Index: ThreadEventNotifierImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl/ThreadEventNotifierImpl.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ThreadEventNotifierImpl.java      16 Sep 2003 18:51:10 -0000      1.1
  +++ ThreadEventNotifierImpl.java      29 Sep 2003 20:56:19 -0000      1.2
  @@ -57,12 +57,11 @@
   
   package org.apache.commons.hivemind.service.impl;
   
  -import java.util.List;
  -import java.util.ListIterator;
  +import java.util.Iterator;
   
  -import org.apache.commons.collections.CursorableLinkedList;
   import org.apache.commons.hivemind.service.ThreadCleanupListener;
   import org.apache.commons.hivemind.service.ThreadEventNotifier;
  +import org.apache.commons.hivemind.util.EventListenerList;
   
   /**
    * Implementation of [EMAIL PROTECTED] 
org.apache.commons.hivemind.service.ThreadEventNotifier},
  @@ -77,26 +76,23 @@
   
       public void addThreadCleanupListener(ThreadCleanupListener listener)
       {
  -        List list = (List) _storage.get();
  +        EventListenerList list = (EventListenerList) _storage.get();
   
           if (list == null)
           {
  -            // We use this implementation since it is less testy
  -            // about concurrent modifications.
  -
  -            list = new CursorableLinkedList();
  +            list = new EventListenerList();
               _storage.set(list);
           }
   
  -        list.add(listener);
  +        list.addListener(listener);
       }
   
       public void removeThreadCleanupListener(ThreadCleanupListener listener)
       {
  -        List list = (List) _storage.get();
  +             EventListenerList list = (EventListenerList) _storage.get();
   
           if (list != null)
  -            list.remove(listener);
  +            list.removeListener(listener);
       }
   
       public void fireThreadCleanup()
  @@ -105,20 +101,21 @@
           // are free to unregister as listeners from threadDidCleanup() and
           // we need to avoid concurrent modification errors.
   
  -        CursorableLinkedList list = (CursorableLinkedList) _storage.get();
  +             EventListenerList list = (EventListenerList) _storage.get();
   
           if (list == null)
               return;
   
  -        // cursor() returns a ListIterator that isn't bothered by
  -        // modifications to the list.
  -
  -        ListIterator i = list.cursor();
  +        Iterator i = list.getListeners();
   
           while (i.hasNext())
           {
               ThreadCleanupListener listener = (ThreadCleanupListener) i.next();
   
  +                     // Each listener may decide to remove itself; that's OK,
  +                     // EventListenerList handles that kind of concurrent 
modification
  +                     // well.
  +                     
               listener.threadDidCleanup();
           }
   
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to