org.jtrim2.executor.TaskExecutors Maven / Gradle / Ivy
package org.jtrim2.executor;
import java.util.Objects;
import org.jtrim2.cancel.CancellationToken;
/**
* Contains static helper and factory methods for various useful
* {@link TaskExecutor} and {@link TaskExecutorService} implementations.
*
* This class cannot be inherited and instantiated.
*
*
Thread safety
* Unless otherwise noted, methods of this class are safe to use by multiple
* threads concurrently.
*
* Synchronization transparency
* Unless otherwise noted, methods of this class are
* synchronization transparent.
*/
public final class TaskExecutors {
/**
* Returns an {@code TaskExecutorService} forwarding all of its methods to
* the given {@code TaskExecutorService} but the returned
* {@code TaskExecutorService} cannot be shutted down. Attempting to
* shutdown the returned {@code ExecutorService} results in an unchecked
* {@code UnsupportedOperationException} to be thrown.
*
* @param executor the executor to which calls to be forwarded by the
* returned {@code TaskExecutorService}. This argument cannot be
* {@code null}.
* @return an {@code TaskExecutorService} which forwards all of its calls to
* the specified executor but cannot be shutted down. This method never
* returns {@code null}.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*/
public static TaskExecutorService asUnstoppableExecutor(
TaskExecutorService executor) {
if (executor instanceof UnstoppableTaskExecutor) {
return executor;
} else {
return new UnstoppableTaskExecutor(executor);
}
}
/**
* Returns an executor which forwards task to a given executor and executes
* tasks without running them concurrently. The tasks will be executed in
* the order the they were submitted to the
* {@link TaskExecutor#execute(CancellationToken, CancelableTask) execute}
* method of the returned {@code TaskExecutor}. Subsequent tasks, attempted to
* be executed while another one scheduled to this executor is running will
* be queued and be executed when the running task terminates. Note that
* even if a tasks schedules a task to this executor, the scheduled task
* will only be called after the scheduling task terminates. See the
* following code for clarification:
* {@code
* void doPrint(TaskExecutor executor) {
* TaskExecutor inOrderExec = TaskExecutors.inOrderExecutor(executor);
* executor.execute(() -> {
* System.out.print("1");
* executor.execute(() -> System.out.print("3"));
* System.out.print("2");
* });
* }
* }
* The {@code doPrint} method will always print "123", regardless what the
* passed executor is.
*
* The returned executor is useful for calling tasks which are not safe to
* be called concurrently. This executor will effectively serialize the
* calls as if all the tasks were executed by a single thread even if the
* underlying executor uses multiple threads to execute tasks.
*
* Note that this implementation does not expect the tasks to be
* synchronization transparent but of course, they cannot wait for
* each other. If a tasks executed by this executor submits a task to this
* same executor and waits for this newly submitted tasks, it will dead-lock
* always. This is because no other tasks may run concurrently with the
* already running tasks and therefore the newly submitted task has no
* chance to start.
*
* Note: This method may return the same executor passed in the
* argument if the specified executor already executes tasks in the order they
* were submitted.
* Warning: Instances of this class use an internal queue for tasks
* yet to be executed and if tasks are submitted to executor faster than it
* can actually execute it will eventually cause the internal buffer to
* overflow and throw an {@link OutOfMemoryError}. This can occur even if
* the underlying executor does not execute tasks scheduled to them because
* tasks will be queued immediately by the {@code execute} method before
* actually executing the task.
*
* @param executor the executor to which tasks will be eventually forwarded
* to. This argument cannot be {@code null}.
* @return executor which forwards task to a given executor and executes
* tasks without running them concurrently. This method never returns
* {@code null} and may return the same executor passed in the argument if
* the specified executor executes tasks in the order they were submitted.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*
* @see SingleThreadedExecutor
* @see #inOrderSimpleExecutor(TaskExecutor)
*/
public static MonitorableTaskExecutor inOrderExecutor(TaskExecutor executor) {
Objects.requireNonNull(executor, "executor");
if (FifoExecutor.isFifoExecutor(executor)
&& executor instanceof MonitorableTaskExecutor) {
return (MonitorableTaskExecutor) executor;
} else {
return new InOrderTaskExecutor(executor);
}
}
/**
* Returns an executor which forwards task to a given executor and executes
* tasks without running them concurrently (this method differs from
* {@link #inOrderExecutor(TaskExecutor)} only by not necessarily returning
* a {@link MonitorableTaskExecutor}). The tasks will be executed in the
* order the they were submitted to the
* {@link TaskExecutor#execute(CancellationToken, CancelableTask) execute}
* method of the returned {@code TaskExecutor}. Subsequent tasks, attempted to
* be executed while another one scheduled to this executor is running will
* be queued and be executed when the running task terminates. Note that
* even if a tasks schedules a task to this executor, the scheduled task
* will only be called after the scheduling task terminates. See the
* following code for clarification:
*
{@code
* void doPrint(TaskExecutor executor) {
* TaskExecutor inOrderExec = TaskExecutors.inOrderSimpleExecutor(executor);
* executor.execute(() -> {
* System.out.print("1");
* executor.execute(() -> System.out.print("3"));
* System.out.print("2");
* });
* }
* }
* The {@code doPrint} method will always print "123", regardless what the
* passed executor is.
*
* The returned executor is useful for calling tasks which are not safe to
* be called concurrently. This executor will effectively serialize the
* calls as if all the tasks were executed by a single thread even if the
* underlying executor uses multiple threads to execute tasks.
*
* Note that this implementation does not expect the tasks to be
* synchronization transparent but of course, they cannot wait for
* each other. If a tasks executed by this executor submits a task to this
* same executor and waits for this newly submitted tasks, it will dead-lock
* always. This is because no other tasks may run concurrently with the
* already running tasks and therefore the newly submitted task has no
* chance to start.
*
* Note: This method may return the same executor passed in the
* argument if the specified executor already executes tasks in submittation
* order.
* Warning: Instances of this class use an internal queue for tasks
* yet to be executed and if tasks are submitted to executor faster than it
* can actually execute it will eventually cause the internal buffer to
* overflow and throw an {@link OutOfMemoryError}. This can occur even if
* the underlying executor does not execute tasks scheduled to them because
* tasks will be queued immediately by the {@code execute} method before
* actually executing the task.
*
* @param executor the executor to which tasks will be eventually forwarded
* to. This argument cannot be {@code null}.
* @return executor which forwards task to a given executor and executes
* tasks without running them concurrently. This method never returns
* {@code null} and may return the same executor passed in the argument if
* the specified executor executes tasks in submittation order.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*
* @see SingleThreadedExecutor
* @see #inOrderExecutor(TaskExecutor)
*/
public static TaskExecutor inOrderSimpleExecutor(TaskExecutor executor) {
Objects.requireNonNull(executor, "executor");
if (FifoExecutor.isFifoExecutor(executor)) {
return executor;
} else {
return new InOrderTaskExecutor(executor);
}
}
/**
* Returns an executor which invokes tasks in the order they were scheduled
* to it. The tasks will execute in one of the
* {@link TaskExecutor#execute(CancellationToken, CancelableTask) execute}
* calls of the same executor but the user has no influence in which one it
* will actually be called.
*
* This method is effectively equivalent to calling
* {@code inOrderExecutor(SyncTaskExecutor.getSimpleExecutor())}.
*
* @return an executor which invokes tasks in the order they were scheduled
* to it. This method never returns {@code null}.
*/
public static MonitorableTaskExecutor inOrderSyncExecutor() {
return inOrderExecutor(SyncTaskExecutor.getSimpleExecutor());
}
/**
* Returns a {@code TaskExecutorService} which upgrades the specified
* {@link TaskExecutor TaskExecutor} to provide all the features of a
* {@code TaskExecutorService}. Tasks submitted to the returned
* {@code TaskExecutorService} will be forwarded to the {@code execute}
* method of the specified {@code TaskExecutor}.
*
* Shutting down the returned executor will shutdown the returned executor
* but not the passed {@code TaskExecutor} even if it implemented the
* {@code TaskExecutorService} interface.
*
* Note: If you don't want to shutdown the return
* {@code TaskExecutorService}, you should rather use the
* {@link #upgradeToUnstoppable(TaskExecutor) upgradeToUnstoppable} method.
*
* @param executor the {@code TaskExecutor} to which the returned
* {@code TaskExecutorService} will forward submitted tasks to be
* executed. This argument cannot be {@code null}.
* @return a {@code TaskExecutorService} which upgrades the specified
* {@link TaskExecutor TaskExecutor} to provide all the features of a
* {@code TaskExecutorService}. This method never returns {@code null}.
*
* @throws NullPointerException thrown if the specified {@code TaskExecutor}
* is {@code null}
*
* @see #upgradeToUnstoppable(TaskExecutor)
*/
public static TaskExecutorService upgradeToStoppable(TaskExecutor executor) {
return new UpgradedTaskExecutor(executor);
}
/**
* Returns a {@code TaskExecutorService} which upgrades the specified
* {@link TaskExecutor TaskExecutor} to provide all the features of a
* {@code TaskExecutorService} except that the returned executer cannot be
* shut down. Tasks submitted to the returned {@code TaskExecutorService}
* will be forwarded to the {@code execute} method of the specified
* {@code TaskExecutor}.
*
* The returned executor cannot be shut down. Attempting to do so will cause
* an unchecked {@code UnsupportedOperationException} to be thrown.
*
* @param executor the {@code TaskExecutor} to which the returned
* {@code TaskExecutorService} will forward submitted tasks to be
* executed. This argument cannot be {@code null}.
* @return a {@code TaskExecutorService} which upgrades the specified
* {@link TaskExecutor TaskExecutor} to provide all the features of a
* {@code TaskExecutorService}. This method never returns {@code null}.
*
* @throws NullPointerException thrown if the specified {@code TaskExecutor}
* is {@code null}
*
* @see #upgradeToStoppable(TaskExecutor)
*/
public static TaskExecutorService upgradeToUnstoppable(TaskExecutor executor) {
if (executor instanceof UnstoppableTaskExecutor) {
return (TaskExecutorService) executor;
} else {
return new UnstoppableTaskExecutor(new UpgradedTaskExecutor(executor));
}
}
/**
* Returns an executor which submits tasks to the specified executor and
* is context aware.
*
* Note that, tasks passed to the executor specified in the parameters has
* no effect on the returned executor.
*
* @param executor the specified executor to which the returned executor
* will submit tasks to. This argument cannot be {@code null}.
* @return an executor which submits tasks to the specified executor and
* is context aware. This method never returns {@code null}.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*/
public static ContextAwareWrapper contextAware(TaskExecutor executor) {
return new ContextAwareWrapper(executor);
}
/**
* Returns an executor which submits tasks to the specified executor and
* is context aware. If the passed executor is already an instance of
* {@link ContextAwareTaskExecutor} then the passed executor is returned.
*
* Note that, tasks passed to the executor specified in the parameters may
* or may not be count as running in the context of the returned executor.
*
* @param executor the specified executor to which the returned executor
* will submit tasks to. This argument cannot be {@code null}.
* @return an executor which submits tasks to the specified executor and
* is context aware. This method never returns {@code null}.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*/
public static ContextAwareTaskExecutor contextAwareIfNecessary(TaskExecutor executor) {
if (executor instanceof ContextAwareTaskExecutor) {
return (ContextAwareTaskExecutor) executor;
} else {
return contextAware(executor);
}
}
/**
* Returns a {@code TaskExecutorService} which forwards tasks to the
* specified {@code TaskExecutorService} but always logs exceptions thrown
* by tasks sheduled to the returned executor. This is useful when debugging
* because a {@code TaskExecutorService} implementation cannot determine if
* an exception thrown by a task expected by the client code or not.
*
* Other than logging exceptions the returned executor delegates all method
* calls to the appropriate method of the specified executor.
*
* The exceptions are logged in a {@code SEVERE} level logmessage.
*
* Warning: The returned executor will not log exceptions thrown by
* cleanup tasks because {@code TaskExecutorService} implementations are
* expected to log or rethrow them.
*
* @param executor the {@code TaskExecutorService} to which method calls are
* forwarded to. This argument cannot be {@code null}.
* @return a {@code TaskExecutorService} which forwards tasks to the
* specified {@code TaskExecutorService} but always logs exceptions thrown
* by tasks sheduled to the returned executor. This method never returns
* {@code null}.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*/
public static TaskExecutorService debugExecutorService(TaskExecutorService executor) {
return new DebugTaskExecutorService(executor);
}
/**
* Returns a {@code TaskExecutor} which forwards tasks to the specified
* {@code TaskExecutor} but always logs exceptions thrown by tasks sheduled
* to the returned executor. This is useful when debugging because a
* {@code TaskExecutor} implementation cannot determine if an exception
* thrown by a task expected by the client code or not.
*
* Other than logging exceptions the returned executor delegates all method
* calls to the appropriate method of the specified executor.
*
* The exceptions are logged in a {@code SEVERE} level logmessage.
*
* @param executor the {@code TaskExecutor} to which method calls are
* forwarded to. This argument cannot be {@code null}.
* @return a {@code TaskExecutor} which forwards tasks to the specified
* {@code TaskExecutor} but always logs exceptions thrown by tasks
* scheduled to the returned executor. This method never returns
* {@code null}.
*
* @throws NullPointerException thrown if the specified executor is
* {@code null}
*/
public static TaskExecutor debugExecutor(TaskExecutor executor) {
return new DebugTaskExecutor(executor);
}
private TaskExecutors() {
throw new AssertionError();
}
}