/*******************************************************************************
 * Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0-standalone.html
 ******************************************************************************/
package se.unlogic.standardutils.threads;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ExtendedThreadPoolExecutor extends ThreadPoolExecutor {

   private boolean isPaused;
   private final ReentrantLock pauseLock = new ReentrantLock();
   private final Condition unpaused = pauseLock.newCondition();
   private final ConcurrentLinkedQueue<ThreadPoolListener> listeners = new ConcurrentLinkedQueue<ThreadPoolListener>();

   public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,   BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
      super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
   }

   public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,   BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,   RejectedExecutionHandler handler) {
      super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
   }

   public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,   BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
      super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
   }

   public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
      super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
   }

   @Override
   protected void beforeExecute(Thread t, Runnable r) {
      super.beforeExecute(t, r);
      pauseLock.lock();
      try {
         while (isPaused) {
            unpaused.await();
         }
      } catch (InterruptedException ie) {
         t.interrupt();
      } finally {
         pauseLock.unlock();
      }

      for(ThreadPoolListener listerner : this.listeners){

         listerner.beforeExecute(t,r);
      }
   }

   public void pause() {
      pauseLock.lock();
      try {
         isPaused = true;
      } finally {
         pauseLock.unlock();
      }
   }

   public void resume() {
      pauseLock.lock();
      try {
         isPaused = false;
         unpaused.signalAll();
      } finally {
         pauseLock.unlock();
      }
   }

   @Override
   protected void afterExecute(Runnable r, Throwable t) {

      super.afterExecute(r, t);

      for(ThreadPoolListener listerner : this.listeners){

         listerner.afterExecute(t,r);
      }
   }

   public boolean addListener(ThreadPoolListener listener) {

      return listeners.add(listener);
   }

   public boolean remove(ThreadPoolListener listener) {

      return listeners.remove(listener);
   }
}
