
net.scattersphere.util.thread.JobManagerQueueExecutor Maven / Gradle / Ivy
Show all versions of scattersphere-core Show documentation
/*
* Scattersphere
* Copyright 2014-2015, Scattersphere Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.scattersphere.util.thread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This is an executor class that takes a job from a queue, and runs it in order of insertion in the job queue.
*
* This class constructs a {@link StrictBlockingQueue} that can only contain {@link JobExecutionContext} objects.
* This function sits on the queue and waits for a job to be injected into the queue (via the {@link #queue} call.
*
*
As a job is injected and pulled from the queue, it is executed in the current {@link Thread}. After the job
* completes, it indicates the completion status on the command line. If an exception state occurs, that is also
* echoed on the command line.
*
*
Once the job completes, the entire process repeats itself until the underlying {@link net.scattersphere.server.Server}
* is shutdown, or the executor thread is interrupted.
*
*
TODO: Check to see if Guava has any classes that make this easier to perform.
*
* Created by kenji on 11/27/14.
*/
public class JobManagerQueueExecutor extends Thread {
private volatile JobExecutionContext status;
private volatile JobManagerThread jmThread;
private final AtomicBoolean isRunning;
private final StrictBlockingQueue queuedJobs;
private final Logger LOG = LoggerFactory.getLogger(JobManagerQueueExecutor.class);
/**
* Constructor. Takes in a name of the thread for debugging purposes.
*
* @param threadName The name of the current executor thread.
*/
public JobManagerQueueExecutor(String threadName) {
super(threadName);
status = null;
jmThread = null;
isRunning = new AtomicBoolean(false);
int threadsAvailable = Runtime.getRuntime().availableProcessors();
this.queuedJobs = new StrictBlockingQueue<>(threadsAvailable * 16);
this.queuedJobs.addAllowedClass(JobExecutionContext.class);
}
/**
* Offers a new job to run. Once queued, the {@link #run} method will grab the job when available, and execute it.
*
* @param job The {@link JobExecutionContext} object to run.
*/
public void queue(JobExecutionContext job) {
job.getJobContext().setJobResult(JobExecutionResult.QUEUED);
queuedJobs.add(job);
}
/**
* This is the runnable function that waits for new jobs to be offered to the queue, taking each one in FIFO order,
* executing it, and logging the results.
*/
public void run() {
isRunning.set(true);
while(isRunning.get()) {
try {
status = queuedJobs.take();
jmThread = new JobManagerThread(status);
LOG.info("[{}] new job received: thread={}", this.getName(), jmThread);
jmThread.started(() -> {
status.getJobContext().setJobResult(JobExecutionResult.RUNNING);
status.getJobContext().setJobStart();
JobManagerCache.instance().addRunningJob(status);
})
.completed(() -> {
status.getJobContext().setJobResult(JobExecutionResult.COMPLETED);
status.getJobContext().setJobEnd();
JobManagerCache.instance().removeRunningJob(status);
JobManagerCache.instance().addCompletedJob(status);
LOG.info("[{}] complete: elapsed={}ms", this.getName(),
(status.getJobContext().getJobEnd() - status.getJobContext().getJobStart()));
})
.exception(ex -> {
LOG.info("[{}] exception state", this.getName(), ex);
if (ex instanceof RuntimeException) {
Exception cause = (Exception) ex.getCause();
if (cause instanceof InterruptedException) {
status.getJobContext().setJobResult(JobExecutionResult.STOPPED, cause);
} else {
status.getJobContext().setJobResult(JobExecutionResult.FAILED, ex);
}
} else {
status.getJobContext().setJobResult(JobExecutionResult.FAILED, ex);
}
status.getJobContext().setJobEnd();
JobManagerCache.instance().removeRunningJob(status);
JobManagerCache.instance().addFailedJob(status);
})
.start();
LOG.info("[{}] joining thread, waiting to complete.", this.getName());
jmThread.join();
LOG.info("[{}] join of thread complete, thread execution done: {}", this.getName(), status.getJobContext());
status = null;
} catch (InterruptedException e) {
LOG.info("Job Manager Queue Executor thread interrupted. Exiting.");
return;
}
}
}
/**
* This function stops a job by ID.
*
* @param jobId {@code String} containing the ID of the job to stop.
* @param reason The reason for stopping the job.
* @return {@code true} if the job was stopped, {@code false} otherwise.
*/
public boolean stop(String jobId, String reason) {
if (status != null && status.getJobContext().getJobId().equals(jobId)) {
status.getJobContext().setJobStopReason(reason);
jmThread.interrupt();
return true;
}
AtomicInteger removedJobsCount = new AtomicInteger(0);
// TODO
// Improve this lambda to include a find/insert (findAll)
queuedJobs.stream()
.forEach(job -> {
if (job.getJobContext().getJobId().equals(jobId)) {
job.getJobContext().setJobStopReason(reason);
removedJobsCount.incrementAndGet();
queuedJobs.remove(job);
}
});
return removedJobsCount.get() > 0;
}
/**
* Indicates whether or not a job by ID is currently queued to be run.
*
* @param jobId The Job ID to search for.
* @return {@code true} if the job is queued, {@code false} otherwise.
*/
public boolean isQueued(String jobId) {
AtomicInteger jobsCount = new AtomicInteger(0);
// TODO
// Improve this lambda to do a find/insert (findAll)
queuedJobs.stream()
.forEach(job -> {
if (job.getJobContext().getJobId().equals(jobId)) {
jobsCount.incrementAndGet();
}
});
return jobsCount.get() > 0;
}
/**
* Returns a list of queued jobs for this executor.
*
* @return {@link List} of {@link net.scattersphere.util.thread.JobExecutionContext} objects.
*/
public List getQueuedJobs() {
List returnList = new ArrayList<>();
returnList.addAll(queuedJobs);
return returnList;
}
}