com.sangupta.jerry.batch.BatchWorker Maven / Gradle / Ivy
/**
*
* jerry - Common Java Functionality
* Copyright (c) 2012-2016, Sandeep Gupta
*
* http://sangupta.com/projects/jerry-core
*
* 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 com.sangupta.jerry.batch;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.AbstractExecutionThreadService;
import com.sangupta.jerry.util.AssertUtils;
/**
* The worker thread that works over a single batch job. Multiple worker threads
* race to utilize and work over the amount of work backed by a queue.
*
* @author sangupta
*
* @since 3.0.0
*
* @param
* the {@link Class} type of the job item that this worker will
* handle
*/
public class BatchWorker extends AbstractExecutionThreadService {
/**
* My logger instance
*/
private static final Logger LOGGER = LoggerFactory.getLogger(BatchJob.class);
/**
* The thread name assigned to this worker
*/
private final String threadName;
/**
* The name of the job that this worker is working on
*/
private final String jobName;
/**
* If set to true indicates that the execution for this worker
* has been paused.
*/
private volatile boolean workerPaused = false;
/**
* If set to true indicates that the execution for this worker
* has been requested to be stopped immediately.
*/
private volatile boolean stopNow = false;
/**
* The item reader to be used to fetch one new job item from the batch
*/
private final Callable itemReader;
/**
* The executor to use to process the item read from the batch
*/
private final BatchJobItemExecutor executor;
/**
* Create a unique worker with the thread name.
*
* @param threadName
* the name of the thread to use
*
* @param jobName
* the name of the job that this worker is part of
*
* @param itemReader
* the item reader that reads an item to work on
*
* @param executor
* the executor to use for working on the job item
*
* @throws IllegalArgumentException
* if threadName or jobName are null/empty, or if
* itemReader or executor are null
*/
public BatchWorker(String threadName, String jobName, Callable itemReader, BatchJobItemExecutor executor) {
if(AssertUtils.isEmpty(threadName)) {
throw new IllegalArgumentException("Thread name cannot be null/empty");
}
if(executor == null) {
throw new IllegalArgumentException("Job piece executor cannot be null");
}
if(itemReader == null) {
throw new IllegalArgumentException("Itemreader cannot be null");
}
this.threadName = threadName;
this.jobName = jobName;
this.itemReader = itemReader;
this.executor = executor;
}
@Override
protected void run() throws Exception {
while(this.isRunning()) {
// check if we need to stop right away
if(this.stopNow) {
return;
}
// check if we need to pause via redis config
pauseIfNeeded();
// read one message from queue
LOGGER.debug("Fetching job item for batch-job: {}", this.jobName);
// read the job from where-ever we are supposed to read from
T job;
try {
job = this.itemReader.call();
} catch(Exception e) {
LOGGER.error("Unable to read job", e);
if(this.executor.getWaitTimeOnJobReadErrorInMillis() > 0) {
try {
LOGGER.debug("Sleeping for stipulated time on job read error...");
Thread.sleep(this.executor.getWaitTimeOnJobReadErrorInMillis());
} catch (InterruptedException e1) {
// make an exit immediately
return;
}
}
continue;
}
if(job == null) {
LOGGER.debug("Job is read as null");
if(this.executor.terminateExecutionOnNullJobItem()) {
LOGGER.debug("Flagged to stop worker on null job... ending worker.");
return;
}
if(this.executor.getWaitTimeOnNullJobInMillis() > 0) {
try {
LOGGER.debug("Sleeping for stipulated time on null job...");
Thread.sleep(this.executor.getWaitTimeOnNullJobInMillis());
} catch (InterruptedException e1) {
// make an exit immediately
return;
}
}
continue;
}
// we found a job
LOGGER.debug("Job to process read as: {}", job);
// check if job has already been processed
if(this.executor.jobAlreadyProcessed(job)) {
LOGGER.debug("Job has already been processed, skipping now: {}", job);
continue;
}
// run the job
try {
LOGGER.debug("Firing the job executor for job: {}", job);
this.executor.executeJobItem(job);
} catch(Exception e) {
LOGGER.error("Unable to execute job: " + job, e);
}
}
}
/**
* Pause the worker if need be.
*
*/
protected void pauseIfNeeded() {
do {
if(this.stopNow) {
this.workerPaused = false;
return;
}
if(!this.executor.pauseExecution()) {
this.workerPaused = false;
return;
}
// set pause
this.workerPaused = true;
// we need to pause
LOGGER.info("Pausing this worker as dictated by JobPieceExecutor");
// sleep for one minute
try {
Thread.sleep(this.executor.pauseCheckInterval());
} catch (InterruptedException e) {
// thread needs to shutdown
return;
}
} while(true);
}
/**
* Invoked to request halt of this workers. Calling this method sets
* the {@link #stopNow} to true to signal the execution that execution
* should immediately halt. Once the job item, if any, is being processed - the
* execution will stop after the processing is complete.
*
* @see com.google.common.util.concurrent.AbstractExecutionThreadService#triggerShutdown()
*/
@Override
protected void triggerShutdown() {
super.triggerShutdown();
this.stopNow = true;
}
/**
* Return the thread name associated with this worker.
*
* @return the name of the thread
*/
public String getThreadName() {
return this.threadName;
}
/**
* Returns if the current worker is under the pause state or not.
*
* @return true if worker is paused, false if
* working
*/
public boolean isWorkerPaused() {
return this.workerPaused;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy