/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * 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, or (at your option) any later version.
 * 
 * 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.
 * 
 * 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  scone.util;


import java.util.Stack;


/**
 * an unlimited pool of Threads.
 * This pool may be used for Thread-recycling in order to avoid
 * expensive creating and destroying of Threads.
 * In order to execute code in a PoolThread, call the ThreadPool's execute() method.
 */

public class ThreadPool {
    Stack idleThreads = new Stack();
    boolean isClosed = false;

    /**
     * executes the specified Runnable in a Thread.
     * If available, an idle Thread will be reused. If not, a new
     * one will be created. Does nothing if the pool is closed.
     *
     * @param runnable the code to be executed in a Thread
     */
    public synchronized void execute(Runnable runnable) {
        // handle only if this pool is not closed
        if (isClosed) {
            return;
        }
        // try to find an idle thread or create a new one
        if (!idleThreads.empty()) {
            // System.out.println("old thread");
            PoolThread pt = (PoolThread) idleThreads.pop();

            pt.setRunnable(runnable);
            synchronized (pt) {
                pt.notify();
            }
        } else {
            // System.out.println("new thread");
            PoolThread pt = new PoolThread(this);

            pt.setRunnable(runnable);
            pt.start();
        }
    }

    /**
     * closes the pool and terminates all idle Threads.
     * Busy threads will terminate after their Runnable has terminated.
     */
    public synchronized void close() {
        while (!idleThreads.empty()) {
            PoolThread pt = (PoolThread) idleThreads.pop();

            synchronized (pt) {
                pt.notify();
            }
        }
        isClosed = true;
    }

    /**
     * returns true, when the pool has been closed.
     * @return true if the pool has been closed.
     */
    public boolean isClosed() {
        return  isClosed;
    }

    /**
     * reports a Thread to be idle.
     * This should rather be an internal method.
     * @param pt the PoolThread
     */
    public synchronized void reportIdle(PoolThread pt) {
        idleThreads.push(pt);
    }
}


/**
 * A Thread to be used within the ThreadPool.
 */
class PoolThread extends Thread {
    ThreadPool pool = null;
    Runnable runnable = null;

    /**
     * creates a new PoolThread
     * @param pool the ThreadPool to which this Thread belongs
     */
    public PoolThread(ThreadPool pool) {
        this.pool = pool;
    }

    /**
     * set the Runnable of this Thread. The Runnable contains the code 
     * which is to be executed.
     * @param runnable the code to be executed.
     */
    public void setRunnable(Runnable runnable) {
        this.runnable = runnable;
    }

    /**
     * the Thread's run method().
     */
    public void run() {
        try {
            while (true) {
                // if runnable is null, this thread is no longer needed
                if (runnable == null) {
                    return;
                } // if not, it is executed
                else {
                    try {
                        runnable.run();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                synchronized (this) {
                    runnable = null;
                    if (pool.isClosed()) {
                        return;
                    }
                    // the thread is reported idle
                    pool.reportIdle(this);
                    wait();
                }
            }
        } catch (InterruptedException e) {}
    }
}

