}
}
private boolean startJobExecution(final JobHandler handler, final JobExecutor consumer) {
this.closeMarker.set(false);
final JobImpl job = handler.getJob();
if ( handler.startProcessing(this) ) {
if ( logger.isDebugEnabled() ) {
logger.debug("Starting job {}", Utility.toString(job));
}
try {
handler.started = System.currentTimeMillis();
if ( consumer != null ) {
final long queueTime = handler.started - handler.queued;
NotificationUtility.sendNotification(this.services.eventAdmin, NotificationConstants.TOPIC_JOB_STARTED, job, queueTime);
synchronized ( this.processingJobsLists ) {
this.processingJobsLists.put(job.getId(), handler);
}
final Runnable task = new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
final Object lock = new Object();
final Thread currentThread = Thread.currentThread();
// update priority and name
final String oldName = currentThread.getName();
final int oldPriority = currentThread.getPriority();
currentThread.setName(oldName + "-" + job.getQueueName() + "(" + job.getTopic() + ")");
if ( configuration.getThreadPriority() != null ) {
switch ( configuration.getThreadPriority() ) {
case NORM : currentThread.setPriority(Thread.NORM_PRIORITY);
break;
case MIN : currentThread.setPriority(Thread.MIN_PRIORITY);
break;
case MAX : currentThread.setPriority(Thread.MAX_PRIORITY);
break;
}
}
JobExecutionResultImpl result = JobExecutionResultImpl.CANCELLED;
Job.JobState resultState = Job.JobState.ERROR;
final AtomicBoolean isAsync = new AtomicBoolean(false);
try {
synchronized ( lock ) {
final JobExecutionContext ctx = new JobExecutionContext() {
private boolean hasInit = false;
@Override
public void initProgress(final int steps,
final long eta) {
if ( !hasInit ) {
handler.persistJobProperties(job.startProgress(steps, eta));
hasInit = true;
}
}
@Override
public void incrementProgressCount(final int steps) {
if ( hasInit ) {
handler.persistJobProperties(job.setProgress(steps));
}
}
@Override
public void updateProgress(final long eta) {
if ( hasInit ) {
handler.persistJobProperties(job.update(eta));
}
}
@Override
public void log(final String message, Object... args) {
handler.persistJobProperties(job.log(message, args));
}
@Override
public boolean isStopped() {
return handler.isStopped();
}
@Override
public void asyncProcessingFinished(final JobExecutionResult result) {
synchronized ( lock ) {
if ( isAsync.compareAndSet(true, false) ) {
services.jobConsumerManager.unregisterListener(job.getId());
Job.JobState state = null;
if ( result.succeeded() ) {
state = Job.JobState.SUCCEEDED;
} else if ( result.failed() ) {
state = Job.JobState.QUEUED;
} else if ( result.cancelled() ) {
if ( handler.isStopped() ) {
state = Job.JobState.STOPPED;
} else {
state = Job.JobState.ERROR;
}
}
finishedJob(job.getId(), state, true);
asyncCounter.decrementAndGet();
} else {
throw new IllegalStateException("Job is not processed async " + job.getId());
}
}
}
@Override
public ResultBuilder result() {
return new ResultBuilder() {
private String message;
private Long retryDelayInMs;
@Override
public JobExecutionResult failed(final long retryDelayInMs) {
this.retryDelayInMs = retryDelayInMs;
return new JobExecutionResultImpl(InternalJobState.FAILED, message, retryDelayInMs);
}
@Override
public ResultBuilder message(final String message) {
this.message = message;
return this;
}
@Override
public JobExecutionResult succeeded() {
return new JobExecutionResultImpl(InternalJobState.SUCCEEDED, message, retryDelayInMs);
}
@Override
public JobExecutionResult failed() {
return new JobExecutionResultImpl(InternalJobState.FAILED, message, retryDelayInMs);
}
@Override
public JobExecutionResult cancelled() {
return new JobExecutionResultImpl(InternalJobState.CANCELLED, message, retryDelayInMs);
}
};
}
};
result = (JobExecutionResultImpl)consumer.process(job, ctx);
if ( result == null ) { // ASYNC processing
services.jobConsumerManager.registerListener(job.getId(), consumer, ctx);
asyncCounter.incrementAndGet();
isAsync.set(true);
} else {
if ( result.succeeded() ) {
resultState = Job.JobState.SUCCEEDED;
} else if ( result.failed() ) {
resultState = Job.JobState.QUEUED;
} else if ( result.cancelled() ) {
if ( handler.isStopped() ) {
resultState = Job.JobState.STOPPED;
} else {
resultState = Job.JobState.ERROR;
}
}
}
}
} catch (final Throwable t) { //NOSONAR
logger.error("Unhandled error occured in job processor " + t.getMessage() + " while processing job " + Utility.toString(job), t);
// we don't reschedule if an exception occurs
result = JobExecutionResultImpl.CANCELLED;
resultState = Job.JobState.ERROR;
} finally {
currentThread.setPriority(oldPriority);
currentThread.setName(oldName);
if ( result != null ) {
if ( result.getRetryDelayInMs() != null ) {
job.setProperty(JobImpl.PROPERTY_DELAY_OVERRIDE, result.getRetryDelayInMs());
}
if ( result.getMessage() != null ) {
job.setProperty(Job.PROPERTY_RESULT_MESSAGE, result.getMessage());
}
finishedJob(job.getId(), resultState, false);
}
}
}
};
// check if the thread pool is available
final ThreadPool pool = this.threadPool;
if ( pool != null ) {
pool.execute(task);
} else {
// if we don't have a thread pool, we create the thread directly
// (this should never happen for jobs, but is a safe fall back)
new Thread(task).start();
}
} else {
// let's add the event to our started jobs list
synchronized ( this.startedJobsLists ) {
this.startedJobsLists.put(job.getId(), handler);
}
final Event jobEvent = this.getJobEvent(handler);
// we need async delivery, otherwise we might create a deadlock
// as this method runs inside a synchronized block and the finishedJob
// method as well!