/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.service;

//Spring classes
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

// Framework
import gov.va.med.fw.service.event.Asynchronous;
import gov.va.med.fw.service.event.ServiceEvent;
import gov.va.med.fw.util.ThreadPool;

/** Implements an application listener interface to listen to application events.
 * If an application event is an asynchronous event, the event will be processed
 * on a different thread.
 * 
 * @author Vu Le
 * @version 1.0
 */
public abstract class AbstractListenerService extends AbstractComponent implements ApplicationListener {

   /**
    * An instance of a thread pool to
    * process asynchronous events
    */
   private ThreadPool pool = null;
   
   /**
    * A default constructor for a service that
    * listens to a service event 
    */
   public AbstractListenerService() {
      super();
   }
   
   /** Returns a thread used to process asynchronous events
    * @return Returns the pool.
    */
   public ThreadPool getPool() {
      return pool;
   }
   
   /** Sets a thread pool for processing asynchronous events
    * @param pool The pool to set.
    */
   public void setPool(ThreadPool pool) {
      this.pool = pool;
   }
   
   /** Checks for a required thread pool
    * @exception Thrown if a thread pool is not set
    */
   public void afterPropertiesSet() throws Exception {
      super.afterPropertiesSet();
   }
   /** Process an application event that is published by a publisher through
    * an application context.  If an event implements an asynchronous interface, 
    * an event will be registered to a task pool to be executed on a different 
    * thread from a caller.
    * 
    * @param	event	An application event
    */
   public void onApplicationEvent( ApplicationEvent event ) {
      if( event instanceof ServiceEvent ) {
         ServiceEvent serviceEvent = (ServiceEvent)event;
         if( serviceEvent instanceof Asynchronous && pool != null ) {
            pool.invokeLater( new Processor( serviceEvent ) ); 
         }
         else {
            processServiceEvent( (ServiceEvent)event );
         }
      }
   }
   
   /**
    * Processes a service event. Derived classes should check for
    * the specific event type to process.  If an event implements
    * an asynchronous interface, this method will be called on a 
    * different thread from a caller.
    * 
    * @param event A service event to process
    */
   protected abstract void processServiceEvent( ServiceEvent event );
   
   /**
    * A processor to encapsulate an event in a runnable interface
    * for processing on a different thread. 
    */
   private class Processor implements Runnable {

      private ServiceEvent asynchEvent = null;
      
      Processor( ServiceEvent event ) {
         super();
         asynchEvent = event;
      }
      
      /** Process an event on a different thread
       */
      public void run() {
         processServiceEvent( asynchEvent );
      }
   }
}
