
org.jppf.client.concurrent.BatchHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jppf-client Show documentation
Show all versions of jppf-client Show documentation
JPPF, the open source grid computing solution
/*
* JPPF.
* Copyright (C) 2005-2015 JPPF Team.
* http://www.jppf.org
*
* 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 org.jppf.client.concurrent;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import org.jppf.JPPFException;
import org.jppf.client.JPPFJob;
import org.jppf.client.event.JobListener;
import org.jppf.client.taskwrapper.JPPFAnnotatedTask;
import org.jppf.node.protocol.Task;
import org.jppf.utils.*;
import org.slf4j.*;
/**
* This class is a processor for tasks submitted via a {@link JPPFExecutorService}.
* It handles both normal mode and batching mode, where the tasks throughput is streamlined
* by specifying how many tasks should be sent to the grid, and a which intervals.
* @author Laurent Cohen
* @exclude
*/
public class BatchHandler extends ThreadSynchronization implements Runnable {
/**
* Logger for this class.
*/
private static Logger log = LoggerFactory.getLogger(BatchHandler.class);
/**
* Determines whether debug-level logging is enabled.
*/
private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
/**
* Count of jobs created by this executor service.
*/
private static AtomicLong jobCount = new AtomicLong(0);
/**
* The minimum number of tasks that must be submitted before they are sent to the server.
*/
private int batchSize = 0;
/**
* The maximum time to wait before the next batch of tasks is to be sent for execution.
*/
private long batchTimeout = 0L;
/**
* The JPPFExecutorService whose tasks are batched.
*/
private JPPFExecutorService executor = null;
/**
* The job to send for execution. If the reference is ont null, then the job is sent immediately.
*/
private AtomicReference currentJobRef = new AtomicReference<>(null);
/**
* The next job being prepared. It will be assigned to currentJobRef
when it is ready for execution,
* depending on the batching parameters.
*/
private AtomicReference nextJobRef = new AtomicReference<>(null);
/**
* The time at which we started to count for the the timeout.
*/
private long start = 0L;
/**
* Time elapsed since the start.
*/
private long elapsed = 0L;
/**
* Used to synchronize access to currentJobRef
and nextJobRef
*/
private ReentrantLock lock = new ReentrantLock(true);
/**
* Represents a condition to await for and corresponding to when currentJobRef
is not null.
*/
private Condition jobReady = lock.newCondition();
/**
* Represents a condition to await for and corresponding to when currentJobRef
is not null.
*/
private Condition submittingJob = lock.newCondition();
/**
* The configuration for this batch handler.
*/
private ExecutorServiceConfiguration config = new ExecutorServiceConfigurationImpl();
/**
* Default constructor.
* @param executor the JPPFExecutorService whose tasks are batched.
*/
BatchHandler(final JPPFExecutorService executor) {
this.executor = executor;
nextJobRef.set(createJob());
}
/**
* Get the minimum number of tasks that must be submitted before they are sent to the server.
* @return the batch size as an int.
*/
synchronized int getBatchSize() {
return batchSize;
}
/**
* Set the minimum number of tasks that must be submitted before they are sent to the server.
* @param batchSize the batch size as an int.
*/
synchronized void setBatchSize(final int batchSize) {
this.batchSize = batchSize;
}
/**
* Get the maximum time to wait before the next batch of tasks is to be sent for execution.
* @return the timeout as a long.
*/
synchronized long getBatchTimeout() {
return batchTimeout;
}
/**
* Set the maximum time to wait before the next batch of tasks is to be sent for execution.
* @param batchTimeout the timeout as a long.
*/
synchronized void setBatchTimeout(final long batchTimeout) {
this.batchTimeout = batchTimeout;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
start = System.currentTimeMillis();
while (!isStopped()) {
try {
lock.lock();
try {
while (!isStopped() && (currentJobRef.get() == null)) {
if (batchTimeout > 0) {
long n = batchTimeout - elapsed;
if (n > 0) jobReady.await(n, TimeUnit.MILLISECONDS);
}
else jobReady.await();
updateNextJob(false);
}
if (isStopped()) break;
JPPFJob job = currentJobRef.get();
if (debugEnabled) log.debug("submitting job " + job.getName() + " with " + job.getJobTasks().size() + " tasks");
configureJob(job);
executor.submitJob(job);
currentJobRef.set(null);
elapsed = System.currentTimeMillis() - start;
submittingJob.signal();
} finally {
lock.unlock();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
/**
* Update the next job to submit if one is ready.
* @param sendSignal true if signal is to be sent, false otherwise.
*/
private void updateNextJob(final boolean sendSignal) {
JPPFJob job = nextJobRef.get();
int size = job.getJobTasks().size();
if (batchTimeout > 0L) elapsed = System.currentTimeMillis() - start;
if (size == 0) {
if ((batchTimeout > 0L) && (elapsed >= batchTimeout)) resetTimeout();
return;
}
if (((batchTimeout > 0L) && (elapsed >= batchTimeout)) ||
((batchSize > 0) && (size >= batchSize)) ||
((batchSize <= 0) && (batchTimeout <= 0L))) {
currentJobRef.set(job);
nextJobRef.set(createJob());
resetTimeout();
if (sendSignal) {
jobReady.signal();
try {
submittingJob.await();
} catch (InterruptedException e) {
throw new RejectedExecutionException(e);
}
}
}
}
/**
* Reset the timeout counter.
*/
private void resetTimeout() {
start = System.currentTimeMillis();
elapsed = 0L;
}
/**
* Submit a {@link Task} that returns the specified type of result.
* @param the type of result returned by the task.
* @param task the task to submit.
* @param result this parameter is only here for type inference (I know, it's ugly).
* @return a {@link Future} representing pending completion of the task.
*/
Future addTask(final Task> task, final T result) {
lock.lock();
try {
if (debugEnabled) log.debug("submitting one JPPFTask");
Future future = null;
JPPFJob job = nextJobRef.get();
try {
job.add(task);
future = new JPPFTaskFuture<>(job, task.getPosition());
} catch (JPPFException e) {
log.error(e.getMessage(), e);
throw new RejectedExecutionException(e);
}
updateNextJob(true);
return future;
} finally {
lock.unlock();
}
}
/**
* Submit a {@link Runnable} that returns the specified type of result.
* @param the type of result returned by the task.
* @param task the task to submit.
* @param result the result for the task.
* @return a {@link Future} representing pending completion of the task.
*/
Future addTask(final Runnable task, final T result) {
lock.lock();
try {
if (debugEnabled) log.debug("submitting one Runnable task with result");
Future future = null;
JPPFJob job = nextJobRef.get();
try {
@SuppressWarnings("deprecation")
JPPFAnnotatedTask t = (JPPFAnnotatedTask) job.add(task);
t.setResult(result);
configureTask(t);
future = new JPPFTaskFuture<>(job, t.getPosition());
} catch (JPPFException e) {
log.error(e.getMessage(), e);
throw new RejectedExecutionException(e);
}
updateNextJob(true);
return future;
} finally {
lock.unlock();
}
}
/**
* Submit a task for execution.
* @param the type of results.
* @param task the task to submit.
* @return a {@link Future} representing pending completion of the task.
*/
Future addTask(final Callable task) {
lock.lock();
try {
if (debugEnabled) log.debug("submitting one Callable Task");
Future future = null;
JPPFJob job = nextJobRef.get();
try {
@SuppressWarnings("deprecation")
JPPFAnnotatedTask jppfTask = (JPPFAnnotatedTask) job.add(task);
configureTask(jppfTask);
future = new JPPFTaskFuture<>(job, jppfTask.getPosition());
} catch (JPPFException e) {
log.error(e.getMessage(), e);
throw new RejectedExecutionException(e);
}
updateNextJob(true);
return future;
} finally {
lock.unlock();
}
}
/**
* Submit a list of tasks for execution.
* @param the type of the results.
* @param tasks the tasks to submit.
* @return a pair representing the result collector used in the current job, along with the position of the first task.
*/
@SuppressWarnings("unchecked")
Pair addTasks(final Collection extends Callable> tasks) {
lock.lock();
try {
if (debugEnabled) log.debug("submitting " + tasks.size() + " Callable Tasks");
Pair pair = null;
JPPFJob job = nextJobRef.get();
@SuppressWarnings("deprecation")
int start = 0;
try {
List> jobTasks = job.getJobTasks();
start = jobTasks.size();
for (Callable> task: tasks) {
Task> t = job.add(task);
configureTask((JPPFAnnotatedTask) t);
}
} catch (JPPFException e) {
log.error(e.getMessage(), e);
throw new RejectedExecutionException(e);
}
pair = new Pair<>(job, start);
updateNextJob(true);
return pair;
} finally {
lock.unlock();
}
}
/**
* Create a new job with a FutureResultCollector a results listener.
* @return a {@link JPPFJob} instance.
*/
private JPPFJob createJob() {
JPPFJob job = new JPPFJob();
job.setName(getClass().getSimpleName() + " job " + jobCount.incrementAndGet());
job.setBlocking(false);
job.addJobListener(executor);
if (debugEnabled) log.debug("created job " + job);
//configureJob(job);
return job;
}
/**
* Configure the specified job using the current configuration.
* @param job the job to configure.
*/
private synchronized void configureJob(final JPPFJob job) {
if (config != null) {
JobConfiguration jc = config.getJobConfiguration();
job.setSLA(jc.getSLA());
job.setClientSLA(jc.getClientSLA());
job.setMetadata(jc.getMetadata());
job.setPersistenceManager(jc.getPersistenceManager());
job.setDataProvider(jc.getDataProvider());
for (JobListener listener: jc.getAllJobListeners()) job.addJobListener(listener);
for (ClassLoader cl: jc.getClassLoaders()) executor.client.registerClassLoader(cl, job.getUuid());
}
}
/**
* Configure the specified job using the current configuration.
* @param task the task to configure.
*/
private synchronized void configureTask(final JPPFAnnotatedTask task) {
if (config != null) {
TaskConfiguration tc = config.getTaskConfiguration();
task.setCancelCallback(tc.getOnCancelCallback());
task.setTimeoutCallback(tc.getOnTimeoutCallback());
task.setTimeoutSchedule(tc.getTimeoutSchedule());
}
}
/**
* Close this batch handler.
*/
void close() {
setStopped(true);
lock.lock();
try {
jobReady.signalAll();
} finally {
lock.unlock();
}
}
/**
* Get the configuration for this batch handler.
* @return an {@link ExecutorServiceConfiguration} instance.
*/
synchronized ExecutorServiceConfiguration getConfig() {
return config;
}
/**
* Set the configuration for this batch handler.
* @param config an {@link ExecutorServiceConfiguration} instance.
* @throws IllegalArgumentException if the new configuration is null.
*/
synchronized void setConfig(final ExecutorServiceConfiguration config) throws IllegalArgumentException {
if (config == null) throw new IllegalArgumentException("configuration cannot be null");
this.config = config;
}
/**
* Get the configuration for this batch handler.
* @return an {@link ExecutorServiceConfiguration} instance.
*/
synchronized ExecutorServiceConfiguration resetConfig() {
config = new ExecutorServiceConfigurationImpl();
return config;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy