You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by hl...@apache.org on 2003/09/29 22:56:19 UTC
cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java
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 apache@apache.org.
*
* 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 {@link 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 apache@apache.org.
*
* 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 {@link 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();
}
Re: cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java
Posted by Christian Essl <ch...@yahoo.de>.
The copy-go works certainly more efficient, but has some threading
considerations.
I think that a Listener expects rightfully that it won't receive an event
anymore after the deregister-method returns however this is not garanteed
with copy-go (a mistake I also made with my event-service).
I guess this problem does not apply to the ThreadListenerService, but
that's realy a special case. So I would not make the class a general util
(or at least document on it).
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org
Re: cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java
Posted by Christian Essl <ch...@yahoo.de>.
The copy-go works certainly more efficient, but has some threading
considerations.
I think that a Listener expects rightfully that it won't receive an event
anymore after the deregister-method returns however this is not garanteed
with copy-go (a mistake I also made with my event-service).
I guess this problem does not apply to the ThreadListenerService, but
that's realy a special case. So I would not make the class a general util
(or at least document on it).