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]>

Reply via email to