package com.limegroup.gnutella.util; import java.util.Iterator; import java.util.List; import java.util.Vector; /** * A reuseable class that allows for a WorkGroup of WorkerInterface threads to * perform tasks. All that needs to be done is to implement the WorkerInterface * (and optionally the CleanUpInterface) and supply instances as necessary. */ public class ThreadWorkGroup { /** * The interface that you should provide a implementation for. Threads of * Workers will perform tasks (via doTask). */ public static interface WorkerInterface extends Runnable { /** Given an Object[], check that the objects are in the correct order * and are of the correct type. Please save the input for use during * run. * @return true if the input types are valid, false if not. */ public boolean checkInput(Object[] input); /** Actually performs a 'task'. Will call checkInput() with the * Object[] before calling this. */ public void run(); } /** In case you want to provide a central cleanup mechanism. The cleanUp() * method will be called after any thread has performed doTask. */ public static interface CleanUpInterface { public void cleanUp(Object[] usedTaskObjects); } // synchronizing access to the list of _tasks is necessary, so we may as // make all access synchronized by using a Vector. private List _tasks = new Vector(); // the pool of worker threads. use an expandable list so the user of this // class can add and kill threads. private List /* of WorkerInterface */_workers = new Vector(); // the central cleanup mechanism - may be null private final CleanUpInterface _cleaner; // whether or not this work group is active. private boolean _stopped = false; /** * Starts workers.length threads that will execute upon input tasks (see * the addTask method). isActive() is true after this call (and is true * until stop() is called). * @param workers The initial set of workers you want to be executing. * @param cleaner An optional cleaner (to be run after a worker performs a * task). Can be null.... * @throws IllegalArgumentException If workers is malformed. */ public ThreadWorkGroup(WorkerInterface[] workers, CleanUpInterface cleaner) throws IllegalArgumentException { // create worker threads.... for (int i = 0; i < workers.length; i++) addWorker(workers[i]); if (_workers.size() != workers.length) throw new IllegalArgumentException("Invalid workers input!"); _cleaner = cleaner; // may be null but so what } /** @param task The input parameters to have a worker thread work on. * The task will not immediately be worked on; rather when one of the * worker threads becomes free it will execute the task. */ public void addTask(Object[] input) { synchronized (_tasks) { _tasks.add(input); // wake a worker up.... _tasks.notify(); } } /** @param worker A WorkerInterface instance to add to the mix. */ public synchronized void addWorker(WorkerInterface worker) { if ((worker != null) && isActive()) { WorkerThread workerThread = new WorkerThread(worker); _workers.add(workerThread); workerThread.start(); } } /** @return true if stop() has never been called. */ public boolean isActive() { return !_stopped; } /** Once this is called, the ThreadWorkGroup is useless and all threads * are correctly extinguished (though they may finish what they are working * on. * @exception InterruptedException thrown because this call may block while * threads are finishing.... * @param waitTime join's for up to waitTime millis, or forever if 0. * @return true if none of the threads were alive upon returning from the * method and after waiting waitTime per thread. */ public synchronized boolean stop(int waitTime) throws InterruptedException { _stopped = true; boolean retVal = true; synchronized (_workers) { // interrupt everybody Iterator workers = _workers.iterator(); while (workers.hasNext()) ((Thread) workers.next()).interrupt(); // wait for the stragglers.... workers = _workers.iterator(); while (workers.hasNext()) ((Thread) workers.next()).join(waitTime); // make sure everybody is dead workers = _workers.iterator(); while (workers.hasNext() && retVal) if (((Thread) workers.next()).isAlive()) retVal = false; _workers.clear(); } return retVal; } private class WorkerThread extends ManagedThread { private final WorkerInterface _worker; public WorkerThread(WorkerInterface worker) { super("WorkerThread"); // TODO: Use name of WorkerInterface _worker = worker; } public void managedRun() { // isActive() is a global on/off switch while (isActive()) { try { // get something to work with Object[] workInput = null; synchronized (_tasks) { while (_tasks.isEmpty()) _tasks.wait(); workInput = (Object[]) _tasks.remove(0); } // if you can work on it, do it.... if (_worker.checkInput(workInput)) _worker.run(); // do any cleaning, if necessary... if (_cleaner != null) _cleaner.cleanUp(workInput); } catch (InterruptedException ie) { } } } } }