avalon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From leosu...@apache.org
Subject cvs commit: jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util MultiDelegateProxy.java MultiDelegate.java Delegate.java
Date Thu, 03 Oct 2002 18:10:27 GMT
leosutic    2002/10/03 11:10:27

  Modified:    util/src/java/org/apache/excalibur/util
                        MultiDelegateProxy.java MultiDelegate.java
                        Delegate.java
  Log:
  Merged MultiDelegateFactory with Delegate, and added a MultiDelegateProxy so MultiDelegates
can be exposed safely without having to write getters and setters.
  
  Revision  Changes    Path
  1.2       +1 -1      jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegateProxy.java
  
  Index: MultiDelegateProxy.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegateProxy.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MultiDelegateProxy.java	3 Oct 2002 17:50:32 -0000	1.1
  +++ MultiDelegateProxy.java	3 Oct 2002 18:10:26 -0000	1.2
  @@ -62,7 +62,7 @@
   {
       private final MultiDelegate multiDelegate;
       
  -    public MultiDelegateWrapper( MultiDelegate multiDelegate )
  +    public MultiDelegateProxy( MultiDelegate multiDelegate )
       {
           this.multiDelegate = multiDelegate;
       }
  
  
  
  1.3       +25 -13    jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegate.java
  
  Index: MultiDelegate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/MultiDelegate.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MultiDelegate.java	3 Oct 2002 17:37:15 -0000	1.2
  +++ MultiDelegate.java	3 Oct 2002 18:10:26 -0000	1.3
  @@ -57,25 +57,37 @@
    * 
    * <p>
    * <pre><code>
  - * public static class Publisher {
  + * public class Publisher {
    *
    *     public static interface Listener 
    *     {
    *         public void onEvent ();
    *     }
    *     
  - *     public MultiDelegate event = MultiDelegateFactory.newMultiDelegate( Listener.class
);
  + *     private final Listener eventDelegate = (Listener) MultiDelegateFactory.newMultiDelegate(
Listener.class );
  + *
  + *     // Expose the MultiDelegate's add and remove methods in a safe way.
  + *     // Make sure the field exposed is final.
  + *     public final MultiDelegate event = new MultiDelegateProxy( (MultiDelegate) eventDelegate
);
    *
    *     public void fireEvent () 
    *     {
  - *         ((Listener) event).onEvent ();
  + *         eventDelegate.onEvent ();
    *     }
    * }
    * </code></pre>
    * <p>
    *
  - * Note the following: We can cast the MultiDelegate instance to the interface given to
  - * the MultiDelegateFactory.newMultiDelegate method.
  + * Note the following:
  + * <ul>
  + * <li>We can cast the <code>MultiDelegate</code> instance to the interface
given to
  + * the <code>MultiDelegateFactory.newMultiDelegate</code> method. 
  + * <li>While we do expose a <code>MultiDelegate</code> interface to the
client, we do so via a proxy. 
  + * This is because the <code>MultiDelegate</code> instance returned from the
<code>MultiDelegateFactory</code> can 
  + * be cast to the delegate interface, meaning that a client can invoke <code>onEvent
()</code>
  + * on it.
  + * </ul>
  + * <p>
    * Subscribers/Listeners to the Publisher above can register themselves with:
    *
    * <p>
  @@ -93,13 +105,13 @@
    * 
    * <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,
  + * <p>
  + * All <code>MultiDelegates</code> are thread-safe, provided that the delegates
they in turn call
  + * are thread-safe. For example, if you invoke a <code>MultiDelegate</code>
while some other thread
  + * modifies the <code>MultiDelegate</code> (via <code>add</code>
or <code>remove</code>), those changes 
  + * will not affect the current invocation.
  + * <p>
  + * It is also legal for delegates to remove themselves from the <code>MultiDelegate</code>
when invoked,
    * or perform operations on it.
    *
    * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
  @@ -109,7 +121,7 @@
   {
       
       /**
  -     * Adds a new delegate to the MultiDelegate's list. The Delegate is only added
  +     * Adds a new delegate to the MultiDelegate's list. The delegate is only added
        * if it does not yet exist in the list.
        *
        * @param o  The delegate we are adding to this MultiDelegate.  The delegate
  
  
  
  1.8       +169 -1    jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/Delegate.java
  
  Index: Delegate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/util/src/java/org/apache/excalibur/util/Delegate.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Delegate.java	3 Oct 2002 16:58:48 -0000	1.7
  +++ Delegate.java	3 Oct 2002 18:10:26 -0000	1.8
  @@ -49,7 +49,14 @@
   */
   package org.apache.excalibur.util;
   
  -import java.lang.reflect.*;
  +import java.lang.reflect.Proxy;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.InvocationHandler;
  +import java.lang.reflect.InvocationTargetException;
  +
  +import java.util.ArrayList;
  +import java.util.Iterator;
  +import java.util.List;
   
   
   /**
  @@ -271,6 +278,167 @@
               else
               {
                   return m_delegateMethod.invoke( m_instance, args );
  +            }
  +        }
  +    }
  +    
  +    //-------------------------------------------------------
  +    // MultiDelegate related methods and classes
  +    //-------------------------------------------------------
  +    
  +    /**
  +     * 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 ));
  +    }    
  +    
  +    /**
  +     * InvocationHandler for MultiDelegates
  +     */
  +    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.
  +         * The Class passed in must be an interface with only one method that returns
  +         * <code>void</code>
  +         *
  +         * @param delegateClass  The interface representing the MultiDelegate.
  +         */
  +        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.
  +         *
  +         * @param proxy   The object to forward the method invocations to.
  +         * @param method  The method that was called
  +         * @param args    The arguments used for the method
  +         *
  +         * @return Object  must always be an "instance" of void.class
  +         *
  +         * @throws IllegalAccessException  When the method or proxy object
  +         *                                 is not accessible
  +         * @throws InvocationTargetException  When the method throws an
  +         *                                    exception
  +         */
  +        public Object invoke(Object proxy, Method method, Object[] args)
  +            throws IllegalAccessException, InvocationTargetException
  +        {
  +            if (method.equals (addMethod)) 
  +            {
  +                if( args[0] == null )
  +                {
  +                    throw new IllegalArgumentException( "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;
               }
           }
       }
  
  
  

--
To unsubscribe, e-mail:   <mailto:avalon-cvs-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:avalon-cvs-help@jakarta.apache.org>


Mime
View raw message