leosutic 2002/10/03 09:59:40
Added: util/src/java/org/apache/excalibur/util
MultiDelegateFactory.java MultiDelegate.java
Log:
Multicast delegates. Initial checkin.
Revision Changes Path
1.1
jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegateFactory.java
Index: MultiDelegateFactory.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) @year@ The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, 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 acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" 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 name, without prior written permission of the
Apache Software Foundation.
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 (INCLU-
DING, 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.excalibur.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A factory for MultiDelegates. Please see the MultiDelegate interface for
* examples of use.
*
* @see MultiDelegate
* @author <a href="mailto:[EMAIL PROTECTED]">Berin Loritsch</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Leo Sutic</a>
*/
public class MultiDelegateFactory
{
static private final class MultiDelegateHandler implements InvocationHandler
{
private final Class delegateClass;
private final List handlers = new ArrayList ();
private static final Method addMethod;
private static final Method removeMethod;
/*
* Static initializer. Finds the add and remove methods in the MultiDelegate
interface.
*/
static
{
try
{
addMethod = MultiDelegate.class.getDeclaredMethod( "add", new
Class[]{ Object.class } );
removeMethod = MultiDelegate.class.getDeclaredMethod( "remove", new
Class[]{ Object.class } );
}
catch (Exception e)
{
throw new LinkageError( "Unable to find MultiDelegate.add and
MultiDelegate.remove in the MultiDelegate interface." );
}
}
/**
* Creates a new MultiDelegateHandler. The handler's add and remove methods
will only accept
* objects that implements the interface given in delegateClass.
*/
public MultiDelegateHandler( Class delegateClass )
{
this.delegateClass = delegateClass;
}
/**
* Handles invocations. If the call is made through the MultiDelegate
interface,
* the appropriate methods are called. Otherwise the delegate methods are
called.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable
{
if (method.equals (addMethod))
{
if( args[0] == null )
{
throw new NullPointerException( "Argument to
MultiDelegate.add(Object) is null." );
}
if( !delegateClass.isAssignableFrom(args[0].getClass()) )
{
throw new ClassCastException( "The argument to
MultiDelegate.add(Object) does not have the " +
"correct delegate interface, which is " +
delegateClass.getName () + ". Instead, it is a " +
args[0].getClass().getName () + ".");
}
synchronized( handlers )
{
if( !handlers.contains( args[0] ) )
{
handlers.add (args[0]);
}
}
return null;
}
else if (method.equals (removeMethod))
{
synchronized( handlers )
{
handlers.remove (args[0]);
}
return null;
}
else
{
List activeList = new ArrayList ();
synchronized( handlers )
{
// Copy the list of handlers to allow for updates to
// the list during invocation.
activeList.addAll( handlers );
}
Iterator iter = activeList.iterator ();
while (iter.hasNext ())
{
method.invoke (iter.next (), args);
}
// As MultiDelegates are multicast, the only allowed return value is
void.
return null;
}
}
}
/**
* Creates a new MultiDelegate.
*
* @param delegateInterface the delegate interface. The multiDelegate's add and
remove
* methods will only accept Objects that implement this
interface.
* The interface may only contain a single method that
must have a
* return type of void. The rationale behind the return
type is this:
* Since the MultiDelegate will in turn invoke many
delegates, there
* is no good way of determining which return value to
give back to the
* caller.
*
* @return the MultiDelegate instance. You have to cast it to the interface
* you passed in (<code>delegateInterface</code>) to invoke it.
*/
public static MultiDelegate newMultiDelegate( Class delegateInterface )
{
Method[] declaredMethods = delegateInterface.getDeclaredMethods();
if( declaredMethods.length != 1 )
{
throw new IllegalArgumentException("The delegate interface must have one
(1) and only one method.");
}
Class methodReturnType = declaredMethods[0].getReturnType();
if( !methodReturnType.equals(Void.TYPE) )
{
throw new IllegalArgumentException("The delegate interface's only method
must return void. (was: " +
methodReturnType.getName () + ")");
}
ClassLoader proxyClassLoader = Thread.currentThread ().getContextClassLoader
();
if (proxyClassLoader == null)
{
proxyClassLoader = delegateInterface.getClassLoader ();
}
return (MultiDelegate) Proxy.newProxyInstance(
proxyClassLoader,
new Class[]{ MultiDelegate.class, delegateInterface },
new MultiDelegateHandler( delegateInterface ));
}
}
1.1
jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegate.java
Index: MultiDelegate.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) @year@ The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, 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 acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" 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 name, without prior written permission of the
Apache Software Foundation.
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 (INCLU-
DING, 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.excalibur.util;
/**
* A multicast-type delegate interface. While a delegate is a pointer
* to a method, a MultiDelegate is intended for the times when you want
* to send an event to several delegates. This is best illustrated
* with a code example:
*
* <p>
* <pre><code>
* public static class Publisher {
*
* public static interface Listener
* {
* public void onEvent ();
* }
*
* public MultiDelegate event = MultiDelegateFactory.newMultiDelegate(
Listener.class );
*
* public void fireEvent ()
* {
* ((Listener) event).onEvent ();
* }
* }
* </code></pre>
* <p>
*
* Note the following: We can cast the MultiDelegate instance to the interface given
to
* the MultiDelegateFactory.newMultiDelegate method.
* Subscribers/Listeners to the Publisher above can register themselves with:
*
* <p>
* <pre><code>
* public void myEventHandler () {
* ...
* }
*
* public void registerWithPublisher( Publisher publisher )
* {
* publisher.event.add( Delegate.newDelegate (this, "myEventHandler",
Publisher.Listener.class) );
* }
* </code></pre>
* <p>
*
* <b>Note that the add and remove methods have Set-like behavior. Adding a delegate
twice has
* the same effect as only adding it once.</b>
*
* All MultiDelegates are thread-safe, provided that the delegates they in turn call
* are thread-safe. For example, if you invoke a MultiDelegate while some other
thread
* modifies the MultiDelegate (via add or remove), those changes will not affect the
* current invocation.
*
* It is also legal for delegates to remove themselves from the MultiDelegate when
invoked,
* or perform operations on it.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Berin Loritsch</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Leo Sutic</a>
*/
public interface MultiDelegate
{
/**
* Adds a new delegate to the MultiDelegate's list. The Delegate is only added
* if it does not yet exist in the list.
*/
public void add (Object o);
/**
* Removes a delegate from the MultiDelegate's list.
*/
public void remove (Object o);
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>