The problem is you can't really do anything simple because the outcome of whether a job is allowed to be queued or not depends on what jobs are already in the queue.
I would suggest you need a JobManager that controls the queue, and a JobRunner that takes any jobs from the queue and runs them. You need to both check the contents of the queue and add to the queue under the same exclusive lock.
public class JobManager {
private final Queue<Jobs> queue;
private final JobRunner jobRunner;
public JobManager() {
this.queue = new LinkedList<Jobs>();
this.jobRunner = new JobRunner(this);
jobRunner.start();
}
public synchronized void requestFirst() {
if (queue.isEmpty()) {
queue.add(Jobs.FIRST);
notifyAll();
}
}
public synchronized void requestSecond() {
if (!queue.contains(Jobs.SECOND)) {
queue.add(Jobs.SECOND);
notifyAll();
}
}
public synchronized Jobs getJob() throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
return queue.peek();
}
public synchronized void notifyFinished(Jobs job) {
queue.remove(job);
}
public void startRunner() {
jobRunner.start();
}
public void stopRunner() {
jobRunner.stopRunner();
}
}
public class JobRunner extends Thread {
private final JobManager jobManager;
private volatile boolean stopping;
public JobRunner(JobManager jobManager) {
this.jobManager = jobManager;
this.stopping = false;
}
public void stopRunner() {
stopping = true;
this.interrupt();
}
@Override
public void run() {
while (!stopping) {
try {
Jobs job = jobManager.getJob();
if (job.equals(Jobs.FIRST)) {
// run first job
} else if (job.equals(Jobs.SECOND)) {
// run second job
}
jobManager.notifyFinished(job);
} catch (InterruptedException ignored) {}
}
}
}
public enum Jobs {
FIRST,
SECOND
}
@Controller
public class WebAccess {
private final JobManager jobManager;
public WebAccess() {
jobManager = new JobManager();
}
@Scheduled(cron = "0 * * * * *")
public void first() {
jobManager.requestFirst();
}
@RequestMapping(value = "/second", method = RequestMethod.POST)
public void second() {
jobManager.requestSecond();
}
@EventListener(ContextClosedEvent.class)
public void stopRunner() {
jobManager.stopRunner();
}
}
Unfortunately because of your complicated requirements for choosing first or second job, you need to hold a synchronization lock whilst checking the queue and deciding whether to add the job or not.
This kind of approach may be extremely difficult to test so probably it should be a last resort.
If you want to get Spring to do the autowiring you could annotate it accordingly.