/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2006 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** All rights reserved **
** **
** This program and the accompanying materials are made available under **
** the terms of the Eclipse Public License v1.0 which accompanies this **
** distribution, and is available at: **
** http://www.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.core.tests;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.rssowl.core.internal.Activator;
import org.rssowl.core.util.ITask;
import org.rssowl.core.util.LoggingSafeRunnable;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author bpasero
*/
public class TestJobQueue {
private final int fMaxConcurrentJobs;
private final AtomicInteger fScheduledJobs = new AtomicInteger(0);
private final AtomicInteger fTotalWork = new AtomicInteger(0);
private final AtomicLong fDuration = new AtomicLong(0);
private final AtomicInteger fDone = new AtomicInteger(0);
private final ConcurrentLinkedQueue<ITask> fOpenTasksQueue;
/**
* Creates an instance of <code>JobQueue</code> that allows to add
* <code>Runnables</code> into a Queue to process them in Jobs up to a
* certain amount of allowed parallel Jobs.
*
* @param maxConcurrentJobs The maximum number of concurrent running Tasks.
*/
public TestJobQueue(int maxConcurrentJobs) {
fMaxConcurrentJobs = maxConcurrentJobs;
fOpenTasksQueue = new ConcurrentLinkedQueue<ITask>();
}
/**
* Adds the given List of Tasks into the Queue. Each Runnable is processed in
* a <code>Job</code> once the number of parallel processed Tasks is below
* <code>MAX_SCHEDULED_JOBS</code>.
*
* @param tasks The Tasks to add into this Queue.
*/
public void schedule(List<ITask> tasks) {
final int tasksSize = tasks.size();
final long start = System.currentTimeMillis();
/* Ignore empty lists */
if (tasksSize == 0)
return;
/* Add into List of open tasks */
fOpenTasksQueue.addAll(tasks);
/* Adjust Total Work Counter */
fTotalWork.addAndGet(tasksSize);
/* Optimisation: We are able to release the calling thread without locking. */
if (fScheduledJobs.get() >= fMaxConcurrentJobs)
return;
/* Start a new Job for each free Slot */
for (int i = 0; i < tasksSize && !fOpenTasksQueue.isEmpty(); ++i) {
/* Never exceed max number of allowed concurrent Jobs */
if (fScheduledJobs.incrementAndGet() > fMaxConcurrentJobs) {
fScheduledJobs.decrementAndGet();
break;
}
/* Create the Job */
Job job = createJob();
/* Listen to Job's Lifecycle */
job.addJobChangeListener(new JobChangeAdapter() {
/* Update Fields when a Job is Done */
@Override
public void done(IJobChangeEvent event) {
fDone.incrementAndGet();
if (fDone.get() == fTotalWork.get())
fDuration.set(System.currentTimeMillis() - start);
/* Re-Schedule this Job if there is work left to do */
if (!fOpenTasksQueue.isEmpty())
event.getJob().schedule();
else
fScheduledJobs.decrementAndGet();
}
});
/*
* Workaround: Since we are using our own Job for displaying Progress, we
* don't want these Jobs show up in the Progress View. There is currently
* no bug-free solution of aggregating the Progress of N Jobs into a
* single Monitor.
*/
job.setSystem(true);
/* Schedule it immediately */
job.schedule();
}
}
/* Create a Job for a Task to handle */
private Job createJob() {
Job job = new Job("") { //$NON-NLS-1$
@Override
protected IStatus run(final IProgressMonitor monitor) {
/* Poll the next Task */
final ITask task = fOpenTasksQueue.poll();
/* Queue is empty - so all work is done */
if (task == null)
return Status.OK_STATUS;
/* Perform the Operation if not yet Cancelled */
if (!monitor.isCanceled()) {
SafeRunner.run(new LoggingSafeRunnable() {
public void run() throws Exception {
IStatus status = task.run(monitor);
/* Log anything that is an Error or Warning */
if (status.getSeverity() == IStatus.ERROR || status.getSeverity() == IStatus.WARNING)
Activator.getDefault().getLog().log(status);
}
});
}
/* Inform about cancelation if present */
return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return family == TestJobQueue.this;
}
};
return job;
}
/**
* @return The value of done Jobs.
*/
public int getDone() {
return fDone.get();
}
/**
* @return The Duration of this Queue.
*/
public long getDuration() {
return fDuration.get();
}
}