/* * */ package vroom.common.utilities.events; import java.util.Arrays; import java.util.Comparator; import java.util.Observable; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import vroom.common.utilities.ExtendedReentrantLock; /** * <code>EventQueue</code> is a generic utility class for the management of a * list of events, it uses a priority queue to store the pending events. * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los * Andes</a> - <a href="http://copa.uniandes.edu.co">Copa</a>, <a * href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @see PriorityBlockingQueue * @param <E> * the type of events managed by this instance */ public class EventQueue<E extends IEvent<?>> extends Observable { private final ExtendedReentrantLock mLock = new ExtendedReentrantLock(true); private final Condition mEmptyCondition = mLock.newCondition(); private final Comparator<IEvent<?>> mComparator; /** The name of the <code>nextEventPreemptive</code> property */ public static final String NEXT_EVENT_Preemptive_PROP = "NextEventPreemptive"; /** * The list of events */ private final PriorityBlockingQueue<E> mEventsQueue; /** * Creates a new event manager that will order events according to the * {@linkplain Comparable natural ordering} * * @see #EventQueue(Comparator) */ public EventQueue() { this(null); } /** * Creates a new <code>EventQueue</code> that will order events according to * the specified comparator * * @param eventComparator * the comparator that will be used to order this priority queue. * If null, the natural ordering of the elements will be used. * @see PriorityBlockingQueue#PriorityBlockingQueue(int, Comparator) */ public EventQueue(Comparator<IEvent<?>> eventComparator) { mComparator = eventComparator; mEventsQueue = new PriorityBlockingQueue<E>(20, mComparator); } /** * Check if the next event of the queue is preemptive * * @return <code>true</code> if the next event is preemptive */ public synchronized boolean isNextEventPreemptive() { return mEventsQueue != null && mEventsQueue.peek() != null && mEventsQueue.peek().isPreemptive(); } /** * Causes the calling thread to wait until a new event becomes available or * the timeout expires (does nothing if there is an available event) * * @param timeout * the time in milliseconds to wait * @return {@code false} if the waiting time detectably elapsed before * return from the method, else {@code true} * @throws InterruptedException * @see Condition#await(long, TimeUnit) */ public boolean awaitForNewEvent(long timeout) throws InterruptedException { boolean r = true; if (isEmpty()) { this.mLock.lockInterruptibly(); r = mEmptyCondition.await(timeout, TimeUnit.MILLISECONDS); this.mLock.unlock(); } return r; } /** * Retrieves and removes the next event, waiting if necessary until an event * becomes available. * * @return the next event * @throws InterruptedException * if interrupted while waiting * @see PriorityBlockingQueue#take() * @see EventQueue#pollNextEvent() */ public synchronized E takeNextEvent() throws InterruptedException { return this.mEventsQueue.take(); } /** * Retrieves and removes the next event * * @return the next event * @see PriorityBlockingQueue#poll() * @see EventQueue#takeNextEvent() */ public synchronized E pollNextEvent() throws InterruptedException { return this.mEventsQueue.poll(); } /** * Remove all the pending events */ public synchronized void clear() { mEventsQueue.clear(); } /** * @return the number of pending events */ public synchronized int getPendingEventsCount() { return this.mEventsQueue.size(); } /** * @return the pending events */ public synchronized IEvent<?>[] getPendingEvents() { IEvent<?>[] e = this.mEventsQueue .toArray(new IEvent<?>[getPendingEventsCount()]); Arrays.sort(e, mComparator); return e; } /** * Add an event to the current pending queue * * @param event * the event to be added to the queue * @return <code>true</code> if <code>event</code> was successfully added to * the queue, <code>false</code> otherwise * @throws InterruptedException * @see PriorityBlockingQueue#offer(Object) */ public synchronized boolean pushEvent(E event) throws InterruptedException { boolean p = isNextEventPreemptive(); boolean b = this.mEventsQueue.offer(event); if (b) { this.mLock.lockInterruptibly(); // The preemptive property has changed if (p != isNextEventPreemptive()) { // Notify observers notifyObservers(NEXT_EVENT_Preemptive_PROP); } mEmptyCondition.signalAll(); this.mLock.unlock(); } return b; } /** * State of the event queue * * @return <code>true</code> if there is no pending events, * <code>false</code> otherwise */ public synchronized boolean isEmpty() { return this.mEventsQueue.isEmpty(); } /** * Checks if an event of the specified class is present in the queue * * @param eventClass * the class of event to be searched * @return <code>true</code> if the current queue contains at list one event * of class <code>eventClass</code>, <code>false</code> otherwise */ public synchronized boolean contains(Class<? extends E> eventClass) { if (isEmpty()) { return false; } for (IEvent<?> e : getPendingEvents()) { if (e != null && e.getClass() == eventClass) { return true; } } return false; } }