--- experimental/server/Queue/Queue.java 2000/12/28 00:58:43 1.1 +++ experimental/server/Queue/Queue.java 2000/12/28 03:49:15 1.2 @@ -1,46 +1,216 @@ +//---PACKAGE DECLARATION--- + +//---IMPORTS--- import java.util.LinkedList; import java.util.NoSuchElementException; +//import uk.ac.ukc.iscream.util.*; +/** + * A Queue class designed to operate in a multi-threaded environment, with + * added support for multiple "consumer" threads. Also offers blocking on + * the get() methods, which ensures the consumer waits until the queue + * actually contains some elements. + * + * @author $Author: tdb $ + * @version $Id: Queue.java,v 1.2 2000/12/28 03:49:15 tdb Exp $ + */ class Queue { + +//---FINAL ATTRIBUTES--- + + /** + * The current CVS revision of this class + */ + public static final String REVISION = "$Revision: 1.2 $"; +//---STATIC METHODS--- + +//---CONSTRUCTORS--- + + /** + * This constructor sets up a given number of queues, all of which + * will be populated with data using the add() method. It is very + * important that the correct number is given, otherwise redundant + * queues will build up with large amounts of data in them. + * + * @param consumers The number of queues to be created. + */ + public Queue(int consumers) { + // constuct and initialise the queues + _lists = new LinkedList[consumers]; + for(int i=0; i < _lists.length; i++) { + _lists[i] = new LinkedList(); + } + } + + /** + * This constructor is intended for an environment with a single + * consumer. This should be used in conjunction with the no-args + * get() method. + */ public Queue() { - // Possible use this method instead ? - //_list = Collections.synchronizedList(new LinkedList(...)); - _list = new LinkedList(); + // call the proper constructor + this(1); } - public synchronized void add(Object o) { - int s = _list.size(); - // add() does the same thing, but this ensures behaviour - _list.addLast(o); - if (s == 0) { - notifyAll(); +//---PUBLIC METHODS--- + + /** + * This method adds a given object to every queue. It will notify + * any waiting consumers (on an empty queue) during this process. + * + * @param o An Object to be added to the queues. + */ + public void add(Object o) { + for(int i=0; i < _lists.length; i++) { + int s = _lists[i].size(); + synchronized(this) { + // add() does the same thing, but this ensures behaviour + _lists[i].addLast(o); + } + // if the queue was empty before the add it is possible + // that a consumer is waiting... so we notify them + if (s == 0) { + synchronized(_lists[i]) { + _lists[i].notifyAll(); + } + } } + // we keep track of the total additions for the status() method _count++; } - public synchronized Object get() { - if (_list.size() == 0) { - try { wait(); } catch(Exception e) {} + /** + * This method returns an object from the front of a given queue. + * It will block until data exists in the queue if required. + * + * @return The object from the front of the queue. + */ + public Object get(int queue) { + // block if the queue is empty + if (_lists[queue].size() == 0) { + synchronized(_lists[queue]) { + try { _lists[queue].wait(); } catch(Exception e) {} + } } + // get an item, it should never be null due to the blocking above Object o = null; - try { - o = _list.removeFirst(); + synchronized(this) { + try { + o = _lists[queue].removeFirst(); + } + catch (NoSuchElementException e) { + // This should never happen ! + } } - catch (NoSuchElementException e) { - // no element... null already... so just leave - } return o; } + /** + * This method is intended for an environment where there is + * only a single consumer. It simply gets the item from the + * first (and presumably only) queue. + * + * @return The object from the front of the queue. + */ + public Object get() { + return get(0); + } + + /** + * This method returns a textual status of the queues. It + * is merely for observation, and would most likely be used + * by a larger "monitoring" component. Information returned + * includes the current size of each queue, and the total + * items passed through. + * + * @return A String message containing status information. + */ public String status() { String status = ""; - status += "Current queue size = "+_list.size(); - status += "\n"; - status += "Queue-ometer = "+_count; + for(int i=0; i < _lists.length; i++) { + status += "Queue number "+i+" contains "+_lists[i].size()+" elements"; + status += "\n"; + } + status += "A total of "+_count+" elements have been added to the queues"; return status; } - private LinkedList _list; - private int _count; -} \ No newline at end of file + /** + * This method assigns a queue to a consumer. The idea behind + * this is to ensure that only 1 consumer can be associated with + * a given queue, otherwise the whole "blocking" thing fails + * miserably. + * + * @return An integer to be passed to the get() method. + * @throws NoQueueException if there are no un-assigned queue's. + */ + public int getQueue() throws NoQueueException { + if(_index < _lists.length) { + return _index++; + } + else { + throw new NoQueueException("Too many consumers, there are already "+_lists.length+" running"); + } + } + + /** + * Overrides the {@link java.lang.Object#toString() Object.toString()} + * method to provide clean logging (every class should have this). + * + * This uses the uk.ac.ukc.iscream.util.FormatName class + * to format the toString() + * + * @return the name of this class and its CVS revision + */ + /*public String toString() { + return FormatName.getName( + _name, + getClass().getName(), + REVISION); + }*/ + +//---PRIVATE METHODS--- + +//---ACCESSOR/MUTATOR METHODS--- + +//---ATTRIBUTES--- + + /** + * The array of lists, which the underlying queue data + * is stored in. + */ + private LinkedList[] _lists; + + /** + * A counter so we know how many data items have been + * passed through, for statistical purposes. + */ + private int _count = 0; + + /** + * An index of the next available queue. Used by the + * getQueue() method. + */ + private int _index = 0; + + /** + * This is the friendly identifier of the + * component this class is running in. + * eg, a Filter may be called "filter1", + * If this class does not have an owning + * component, a name from the configuration + * can be placed here. This name could also + * be changed to null for utility classes. + */ + //private String _name = ; + + /** + * This holds a reference to the + * system logger that is being used. + */ + //private Logger _logger = ReferenceManager.getInstance().getLogger(); + +//---STATIC ATTRIBUTES--- + +}