package ru.decipher.extraction.impl;
import org.apache.log4j.Logger;
import ru.decipher.exception.ProcessorTaskSubmissionException;
import ru.decipher.extraction.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Invokes tasks according to priority.
* <p/>
* Those <code>PayloadRequest</code>, which have higher value returned by <code>getPriority()</code>
* will be executed first
* <p/>
* <code>allowedDuplicates</code> shows how many duplicate requests are allowed to
* submit. (0) - means all requests are unique, (-1) - means don't care about duplicates,
* (>= 1) - means amount of allowed duplicates per request
* <p/>
* User: Alexander Paderin (apocarteres@gmail.com)
* Date: 10/20/13
* Time: 9:49 PM
*/
public class GeneralProcessor<T extends PayloadRequest> implements Processor<T> {
private final ThreadPoolExecutor service;
private final Object shutdownLock;
private final Object duplicatesLock;
private final AtomicInteger activeTasks;
private final Map<T, Integer> duplicates;
private final int allowedDuplicates;
private final String description;
private static final Logger log = Logger.getLogger(GeneralProcessor.class);
public GeneralProcessor(int workers) {
this(workers, "payload-processor");
}
public GeneralProcessor(int workers, String description) {
this(workers, -1, description);
}
public GeneralProcessor(int workers, int allowedDuplicates, String description) {
this.allowedDuplicates = allowedDuplicates;
this.duplicatesLock = new Object();
this.duplicates = new HashMap<>();
this.description = description;
this.service = new PayloadExecutor(workers, workers, 10, TimeUnit.MINUTES, new PriorityBlockingQueue<Runnable>());
this.shutdownLock = new Object();
this.activeTasks = new AtomicInteger(0);
}
@Override
public Future<T> submit(PayloadTask<T> task) throws Exception {
final DuplicateRequestHandler dupRequestHandler = new DuplicateRequestHandler();
dupRequestHandler.prepareTask(task);
task.registerHandler(dupRequestHandler);
final ActiveTaskCountHandler taskCountHandler = new ActiveTaskCountHandler();
task.registerHandler(taskCountHandler);
taskCountHandler.inc();
return service.submit(task);
}
@Override
public long getTaskCount() {
return activeTasks.get();
}
@Override
public void shutdown() {
log.debug("shutdown requested. waiting for queued tasks");
synchronized (shutdownLock) {
while (getTaskCount() > 0) {
try {
shutdownLock.wait(60000);
} catch (InterruptedException ignored) {
}
}
}
service.shutdown();
log.info("shutdown completed");
}
class ActiveTaskCountHandler implements CallbackHandler<T> {
private void inc() {
activeTasks.incrementAndGet();
}
@Override
public void onProcessorResponse(T request) {
if (activeTasks.decrementAndGet() == 0) {
synchronized (shutdownLock) {
shutdownLock.notifyAll();
}
}
}
@Override
public <U extends Provider<? extends T>> void beforeExecution(T request, U provider) {
}
}
class DuplicateRequestHandler implements CallbackHandler<T> {
private void prepareTask(PayloadTask<T> task) {
if (allowedDuplicates >= 0) {
synchronized (duplicatesLock) {
final T request = task.getRequest();
Integer actual = duplicates.get(request);
if (actual == null) {
actual = 0;
} else {
actual++;
}
if (actual > allowedDuplicates) {
throw new ProcessorTaskSubmissionException(task + " can't be submitted. it exceeds duplicate request limit");
}
duplicates.put(request, actual);
}
}
}
@Override
public void onProcessorResponse(T request) throws Exception {
if (allowedDuplicates >= 0) {
synchronized (duplicatesLock) {
Integer actual = duplicates.get(request);
if (actual != null) {
actual--;
if (actual < 0) {
duplicates.remove(request);
} else {
duplicates.put(request, actual);
}
}
}
}
}
@Override
public <U extends Provider<? extends T>> void beforeExecution(T request, U provider) {
}
}
class PayloadExecutor extends ThreadPoolExecutor {
public PayloadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread thread = new Thread(r);
thread.setName(description + "-thread");
return thread;
}
}
);
}
@SuppressWarnings("unchecked")
@Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
if (callable instanceof GeneralTask) {
return new PayloadFuture<>(callable, ((GeneralTask) callable).getRequest().getPriority());
}
throw new IllegalStateException("only GeneralTask or derivatives allowed to submit");
}
}
static class PayloadFuture<T> extends FutureTask<T> implements Comparable<PayloadFuture<T>> {
private final Comparable<Object> priority;
public PayloadFuture(Callable<T> callable, Comparable<Object> priority) {
super(callable);
this.priority = priority;
}
@Override
public int compareTo(PayloadFuture<T> o) {
return o.priority.compareTo(this.priority);
}
}
}