Package org.gudy.azureus2.core3.util

Source Code of org.gudy.azureus2.core3.util.ListenerManager

/*
* File    : ListenerManager.java
* Created : 15-Jan-2004
* By      : parg
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.gudy.azureus2.core3.util;

/**
* @author parg
*
*/

/**
* This class exists to support the invocation of listeners while *not* synchronized.
* This is important as in general it is a bad idea to invoke an "external" component
* whilst holding a lock on something as unexpected deadlocks can result.
* It has been introduced to reduce the likelyhood of such deadlocks
*/

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;


public class
ListenerManager<T>
{
  private static final boolean TIME_LISTENERS = false;
 
  public static <T>ListenerManager<T>
  createManager(
    String                    name,
    ListenerManagerDispatcher<T>        target )
  {
    return( new ListenerManager<T>( name, target, false ));
  }
 
  public static <T>ListenerManager<T>
  createAsyncManager(
    String              name,
    ListenerManagerDispatcher<T>  target )
  {
    return( new ListenerManager<T>( name, target, true ));
  }
 
 
  protected String  name;
 
  protected ListenerManagerDispatcher<T>        target;
  protected ListenerManagerDispatcherWithException  target_with_exception;
 
  protected boolean  async;
  protected AEThread2  async_thread;
 
  protected List<T>      listeners    = new ArrayList<T>(0);
 
  protected List<Object[]>  dispatch_queue;
  protected AESemaphore    dispatch_sem;
 
  protected
  ListenerManager(
    String                _name,
    ListenerManagerDispatcher<T>    _target,
    boolean                _async )
  {
    name  = _name;
    target  = _target;
    async  = _async;
   
    if ( target instanceof ListenerManagerDispatcherWithException ){
     
      target_with_exception = (ListenerManagerDispatcherWithException)target;
    }
   
    if ( async ){
     
      dispatch_sem  = new AESemaphore("ListenerManager::"+name);
      dispatch_queue   = new LinkedList<Object[]>();
     
      if ( target_with_exception != null ){
       
        throw( new RuntimeException( "Can't have an async manager with exceptions!"));
      }
    }
  }
 
  public void
  addListener(
    T    listener )
  {
    if (listener == null) {

      Debug.out("Trying to add null listener to " + name);
      return;
    }

    synchronized( this ){
     
      ArrayList<T>  new_listeners  = new ArrayList<T>( listeners );
     
      if (new_listeners.contains(listener)) {
        Logger.log(new LogEvent(LogIDs.CORE, LogEvent.LT_WARNING,
            "addListener called but listener already added for " + name
                + "\n\t" + Debug.getStackTrace(true, false)));
      }
      new_listeners.add( listener );
     
      if (new_listeners.size() > 50) {
        Logger.log(new LogEvent(LogIDs.CORE, LogEvent.LT_WARNING,
            "addListener: over 50 listeners added for " + name
                + "\n\t" + Debug.getStackTrace(true, false)));
      }
     
      listeners  = new_listeners;
     
      if ( async && async_thread == null ){
       
        async_thread = new AEThread2( name, true )
          {
            public void
            run()
            {
              dispatchLoop();
            }
          };
                 
        async_thread.start();
      }
    }
  }
 
  public void
  removeListener(
    Object    listener )
  {
    synchronized( this ){
     
      ArrayList<T>  new_listeners = new ArrayList<T>( listeners );
     
      new_listeners.remove( listener );
     
      listeners  = new_listeners;
     
      if ( async && listeners.size() == 0 ){
       
        async_thread = null;
       
          // try and wake up the thread so it kills itself
       
        dispatch_sem.release();
      }
    }
  }
 
  public boolean
  hasListener(
    T    listener )
  {
    synchronized( this ){

      return( listeners.contains( listener ));
    }
  }
 
  public void
  clear()
  {
    synchronized( this ){
                 
      listeners  = new ArrayList<T>();
     
      if ( async ){
       
        async_thread = null;
       
          // try and wake up the thread so it kills itself
       
        dispatch_sem.release();
      }
    }
  }
 
  public List<T>
  getListenersCopy()
  {
      // we can just return the listeners as we copy on update
       
    return( listeners );
  }
 
  public void
  dispatch(
    int    type,
    Object  value )
  {
    dispatch( type, value, false );
  }
 
  public void
  dispatch(
    int      type,
    Object    value,
    boolean    blocking )
  {
    if ( async ){
     
      AESemaphore  sem = null;
     
      if ( blocking ){
       
        sem = new AESemaphore( "ListenerManager:blocker");
      }
     
      synchronized( this ){
       
          // if there's nobody listening then no point in queueing
       
        if ( listeners.size() == 0 ){
           
          return;
        }
       
          // listeners are "copy on write" updated, hence we grab a reference to the
          // current listeners here. Any subsequent change won't affect our listeners
                       
        dispatch_queue.add(new Object[]{listeners, new Integer(type), value, sem });
      }
     
      dispatch_sem.release();
     
      if ( sem != null ){
       
        sem.reserve();
      }
    }else{
     
      if ( target_with_exception != null ){
       
        throw( new RuntimeException( "call dispatchWithException, not dispatch"));
      }
     
      List<T>  listeners_ref;
     
      synchronized( this ){
       
        listeners_ref = listeners;       
     
     
      try{
        dispatchInternal( listeners_ref, type, value );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace( e );
      }
    }
  } 
 
  public void
  dispatchWithException(
    int    type,
    Object  value )
 
    throws Throwable
  {
    List<T>  listeners_ref;
   
    synchronized( this ){
     
      listeners_ref = listeners;     
    }
   
    dispatchInternal( listeners_ref, type, value );
  }
 
  public void
  dispatch(
    T    listener,
    int    type,
    Object  value )
  {
    dispatch( listener, type, value, false );
  }
 
  public void
  dispatch(
    T    listener,
    int    type,
    Object  value,
    boolean  blocking )
  {
    if ( async ){
     
      AESemaphore  sem = null;
     
      if ( blocking ){
       
        sem = new AESemaphore( "ListenerManager:blocker");
      }
 
      synchronized( this ){
               
          // 5 entries to denote single listener
       
        dispatch_queue.add(new Object[]{ listener, new Integer(type), value, sem, null });
       
        if ( async_thread == null ){
         
          async_thread = new AEThread2( name, true )
            {
              public void
              run()
              {
                dispatchLoop();
              }
            };
                     
          async_thread.start();
        }
      }
     
      dispatch_sem.release();
 
      if ( sem != null ){
       
        sem.reserve();
      }
    }else{
     
      if ( target_with_exception != null ){
       
        throw( new RuntimeException( "call dispatchWithException, not dispatch"));
      }
     
      doDispatch( listener, type, value );
    }
  }

  protected String
  getListenerName(
    T   listener )
  {
    Class listener_class = listener.getClass();
   
    String  res = listener_class.getName();
   
    try{
      Method getString = listener_class.getMethod( "getString", new Class[0]);
     
      if ( getString != null ){
       
        String s = (String)getString.invoke( listener, new Object[0] );
       
        res += " (" + s + ")";
      }
    }catch( Throwable e ){
     
    }
   
    return( res );
  }
 
  protected void
  doDispatch(
    T      listener,
    int      type,
    Object    value )
  {
    try{
      if ( TIME_LISTENERS ){

        long  start = SystemTime.getCurrentTime();
       
        try{

          target.dispatch( listener, type, value );

        }finally{
         
          long duration = SystemTime.getCurrentTime() - start;
         
          System.out.println( name + "/" + type + ": " + getListenerName( listener ) + " - " + duration );
        }
      }else{
     
        target.dispatch( listener, type, value );
      }
     
    }catch( Throwable e ){
     
      Debug.printStackTrace( e );
    }
  }
 
  protected void
  doDispatchWithException(
    T      listener,
    int      type,
    Object    value )
 
    throws Throwable
  {
    if ( TIME_LISTENERS ){

      long  start = SystemTime.getCurrentTime();
     
      try{
        target_with_exception.dispatchWithException( listener, type, value );

      }finally{
       
        long duration = SystemTime.getCurrentTime() - start;
       
        System.out.println( name + "/" + type + ": " + getListenerName( listener ) + " - " + duration );
      }
    }else{
   
      target_with_exception.dispatchWithException( listener, type, value );
    }
  }
 
  protected void
  dispatchInternal(
    List<T>    listeners_ref,
    int      type,
    Object    value )
 
    throws Throwable
  {   
    for (int i=0;i<listeners_ref.size();i++){
   
     
      if ( target_with_exception != null ){
         
        // System.out.println( name + ":dispatchWithException" );
       
          // DON'T catch and handle exceptions here are they are permitted to
          // occur!
       
        doDispatchWithException( listeners_ref.get(i), type, value );
         
      }else{
     
        doDispatch( listeners_ref.get(i), type, value );
      }
    }
  }
 
  protected void
  dispatchInternal(
    T      listener,
    int      type,
    Object    value )
 
    throws Throwable
  {   
    if ( target_with_exception != null ){
       
        // System.out.println( name + ":dispatchWithException" );
       
        // DON'T catch and handle exceptions here are they are permitted to
        // occur!

      doDispatchWithException( listener, type, value );
       
    }else{
     
      doDispatch( listener, type, value );
    }
  }
 
  public void
  dispatchLoop()
  {
    // System.out.println( "ListenerManager::dispatch thread '" + Thread.currentThread() + "' starts");
   
    while(true){
     
      dispatch_sem.reserve();
     
      Object[] data = null;
     
      synchronized( this ){
       
        if ( async_thread == null || !async_thread.isCurrentThread()){
         
            // we've been asked to close. this sem reservation must be
            // "returned" to the pool in case it represents a valid  entry
            // to be picked up by another thread
         
          dispatch_sem.release();
         
          break;
        }
       
        if ( dispatch_queue.size() > 0 ){
         
          data = (Object[])dispatch_queue.remove(0);
        }
      }
     
      if ( data != null ){
     
        try{           
          if ( data.length == 4 ){
         
            dispatchInternal((List<T>)data[0], ((Integer)data[1]).intValue(), data[2] );
           
          }else{
           
            dispatchInternal((T)data[0], ((Integer)data[1]).intValue(), data[2] );
          }
         
        }catch( Throwable e ){
         
          Debug.printStackTrace( e );
         
        }finally{
         
          if ( data[3] != null ){
           
            ((AESemaphore)data[3]).release();
          }
        }
      }
    }
   
    // System.out.println( "ListenerManager::dispatch thread '" + Thread.currentThread() + "' ends");
  }
 
  public static <T>void
  dispatchWithTimeout(
    List<T>                _listeners,
    final ListenerManagerDispatcher<T>  _dispatcher,
    long                _timeout )
  {
    final List<T>  listeners = new ArrayList<T>( _listeners );
   
    final boolean[]  completed = new boolean[listeners.size()];
   
    final AESemaphore  timeout_sem = new AESemaphore("ListenerManager:dwt:timeout");
   
    for (int i=0;i<listeners.size();i++){
     
      final int f_i  = i;
           
      new AEThread2( "ListenerManager:dwt:dispatcher", true ){
        public void
        run()
        {
          try{
            _dispatcher.dispatch( listeners.get(f_i), -1, null );
           
          }catch( Throwable e ){
           
            Debug.printStackTrace(e);
           
          }finally{
           
            completed[f_i= true;
           
            timeout_sem.release();
          }
        }
      }.start();
    }
   
    boolean  timeout_occurred = false;
   
    for (int i=0;i<listeners.size() ;i++){
     
      if ( _timeout <= 0 ){
       
        timeout_occurred  = true;
       
        break;
      }
     
      long start = SystemTime.getCurrentTime();
     
      if ( !timeout_sem.reserve( _timeout )){
       
        timeout_occurred  = true;
       
        break;
      }
     
      long end = SystemTime.getCurrentTime();

      if ( end > start ){

        _timeout = _timeout - ( end - start );
      }
    }
   
    if ( timeout_occurred ){
     
      String  str = "";
     
      for (int i=0;i<completed.length;i++){
     
        if ( !completed[i] ){
         
          str += (str.length()==0?"":",") + listeners.get(i);
        }
      }
     
      if ( str.length() > 0 ){
       
        Debug.out( "Listener dispatch timeout: failed = " + str );
      }
    }
  }
 
  public long size() {
    if (listeners == null)
      return 0;

    return listeners.size();
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.util.ListenerManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.