/**
*
*/
package vroom.common.utilities.events;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.logging.LoggerHelper;
/**
* <code>EventExecutor</code> is a specialization of {@link ThreadPoolExecutor} for the handling of {@link IEvent}.
* <p>
* It is aimed to replace {@link EventQueue} in a multithreading context, as it allows various events to be handled in
* parallel by using multiple threads.
* </p>
* <p>
* Creation date: 31/08/2010 - 10:57:33
*
* @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>
* @version 1.0
*/
public class EventExecutor {
private final ThreadPoolExecutor mExecutor;
private final EventHandlerManager mHandlerManager;
/**
* Creates a new <code>EventExecutor</code>
*
* @param minThreads
* the minimum number of threads to be kept in the pool
* @param maxThreads
* the maximum number of threads
* @param manager
* an {@link EventHandlerManager}
*/
public EventExecutor(int minThreads, int maxThreads, EventHandlerManager manager) {
PriorityBlockingQueue<Runnable> workQueue = new PriorityBlockingQueue<Runnable>(20, null);
ThreadFactory factory = new EventHandlingThreadFactory();
RejectedExecutionHandler handler = new EventRejectedExecutionHandler();
mHandlerManager = manager;
mExecutor = new ThreadPoolExecutor(minThreads, maxThreads, 60, TimeUnit.SECONDS, workQueue,
factory, handler);
}
/**
* Schedule the handling of an event
*
* @param <E>
* the type of event
* @param event
* the event to be handled
* @param handler
* the associated handler
*/
public synchronized <E extends IEvent<?>> void execute(E event, IEventHandler<E> handler) {
execute(new EventHandlerWorker<E>(event, handler));
}
/**
* Schedule the handling of an event
*
* @param call
* the {@link EventHandlerWorker} to be scheduled
*/
protected synchronized void execute(EventHandlerWorker<?> call) {
mExecutor.execute(call);
}
/**
* Schedule the handling of an event
*
* @param <E>
* the type of event
* @param event
* the event to be scheduled
* @return <code>true</code> if the associated handler was successfuly retreived and
* {@linkplain IEventHandler#canHandleEvent(IEvent) can handle} the given event.
*/
public synchronized <E extends IEvent<?>> boolean pushEvent(E event) {
IEventHandler<E> handler = mHandlerManager.getEventHandler(event);
if (handler == null) {
return false;
}
boolean b = handler.canHandleEvent(event);
if (b) {
execute(event, handler);
}
return b;
}
/**
* Returns <code>true</code> if there is no pending events
*
* @return <code>true</code> if there is no pending events
*/
public synchronized boolean isEmpty() {
return mExecutor.getQueue().isEmpty();
}
/**
* @return the number of pending events
*/
public synchronized int getPendingEventsCount() {
return mExecutor.getQueue().size();
}
/**
* @return the pending events
*/
public synchronized IEvent<?>[] getPendingEvents() {
BlockingQueue<Runnable> queue = mExecutor.getQueue();
IEvent<?>[] events = new IEvent<?>[queue.size()];
int idx = 0;
for (Runnable r : queue) {
events[idx++] = ((EventHandlerWorker<?>) r).getEvent();
}
Arrays.sort(events, new EventComparator());
return events;
}
/**
* Initiates an orderly shutdown in which previously submitted events are handled, but no new event will be
* accepted. Invocation has no additional effect if already shut down.
*
* @see ExecutorService#shutdown()
*/
public synchronized void shutdown() {
mExecutor.shutdown();
}
/**
* Attempts to stop all actively executing event handling, halts the processing of waiting events, and returns a
* list of the event handling that were awaiting execution.
*
* @see ExecutorService#shutdownNow()
*/
public synchronized List<EventHandlerWorker<?>> shutdownNow() {
return Utilities.convertToList(mExecutor.shutdownNow());
}
/**
* <code>EventRejectedExecutionHandler</code> is an implementation of {@link RejectedExecutionHandler} that logs an
* error message.
* <p>
* Creation date: 31/08/2010 - 11:05:40
*
* @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>
* @version 1.0
*/
public static class EventRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
LoggerHelper
.getLogger(getClass())
.error("EventRejectedExecutionHandler.rejectedExecution: Unable to execute %s in executor %s",
r, executor);
}
}
/**
* <code>EventHandlingThreadFactory</code> is an implementation of {@link ThreadFactory} that creates
* {@linkplain Thread threads} to be handle events
* <p>
* Creation date: 31/08/2010 - 11:48:00
*
* @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>
* @version 1.0
*/
public static class EventHandlingThreadFactory implements ThreadFactory {
private int mThreadCount = 1;
/** a thread group for this thread factory **/
private final ThreadGroup mThreadGroup;
/**
* Getter for threadGroup : a thread group for this thread factory
*
* @return the value of threadGroup
*/
public ThreadGroup getThreadGroup() {
return mThreadGroup;
}
public EventHandlingThreadFactory() {
mThreadCount = 1;
mThreadGroup = new ThreadGroup("EventThreads");
}
@Override
public Thread newThread(Runnable r) {
return new Thread(getThreadGroup(), r, "Event-" + mThreadCount++);
}
}
}