/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.api.scheduler; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.mule.runtime.core.api.scheduler.SchedulerConfig.RejectionAction.DEFAULT; import org.mule.runtime.api.scheduler.Scheduler; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * Provides a fluent way of customizing a {@link Scheduler} obtained through the {@link SchedulerService}. * * @since 4.0 */ public class SchedulerConfig { /** * Different possible actions to handle the scenario where a task is dispatched to a busy {@link Scheduler}. * <p> * A {@link Scheduler} is considered busy when all of its threads are busy and it cannot accept a new task for execution. */ public enum RejectionAction { /** * The actual {@link RejectedExecutionHandler} of the target {@link Scheduler} will depend on the type of scheduler the thread * is from. For cpu-bound threads (cpuLight and cpuIntensive) it will be <b>abort</b>, and for the other cases it will be * <b>wait</b>. */ DEFAULT, /** * The {@link RejectedExecutionHandler} of the target {@link Scheduler} will cause the dispatcher thread to wait for the task * to be taken by the target scheduler, effectively blocking until that happens. */ WAIT; } /** * @return a default configuration, which can be further customized. */ public static SchedulerConfig config() { return new SchedulerConfig(); } private final Integer maxConcurrentTasks; private final String schedulerPrefix; private final String schedulerName; private final RejectionAction rejectionAction; private final Supplier<Long> shutdownTimeoutMillis; private SchedulerConfig() { this.maxConcurrentTasks = null; this.schedulerPrefix = null; this.schedulerName = null; this.rejectionAction = DEFAULT; this.shutdownTimeoutMillis = () -> null; } private SchedulerConfig(Integer maxConcurrentTasks, String schedulerPrefix, String schedulerName, RejectionAction rejectionAction, Supplier<Long> shutdownTimeoutMillis) { this.maxConcurrentTasks = maxConcurrentTasks; this.schedulerPrefix = schedulerPrefix; this.schedulerName = schedulerName; this.rejectionAction = rejectionAction; this.shutdownTimeoutMillis = shutdownTimeoutMillis; } /** * Sets the max tasks that can be run at the same time for the target {@link Scheduler}. * <p> * This is useful to apply throttling on the target {@link Scheduler}. The way exceeding tasks will be handled is determined by * the target {@link Scheduler}. * * @param maxConcurrentTasks how many tasks can be running at the same time for the target {@link Scheduler}. * @return the updated configuration. */ public SchedulerConfig withMaxConcurrentTasks(int maxConcurrentTasks) { return new SchedulerConfig(maxConcurrentTasks, schedulerPrefix, schedulerName, rejectionAction, shutdownTimeoutMillis); } /** * @return how many tasks can be running at the same time for the target {@link Scheduler}. */ public Integer getMaxConcurrentTasks() { return maxConcurrentTasks; } /** * Sets the prefix to prepend to the name for the target {@link Scheduler}, which will override the default one. * * @param schedulerPrefix the prefix for the name for the target {@link Scheduler}. * @return the updated configuration. */ public SchedulerConfig withPrefix(String schedulerPrefix) { return new SchedulerConfig(maxConcurrentTasks, schedulerPrefix, schedulerName, rejectionAction, shutdownTimeoutMillis); } /** * Sets the name for the target {@link Scheduler}, which will override the default one. * * @param schedulerName the name for the target {@link Scheduler}. * @return the updated configuration. */ public SchedulerConfig withName(String schedulerName) { return new SchedulerConfig(maxConcurrentTasks, schedulerPrefix, schedulerName, rejectionAction, shutdownTimeoutMillis); } /** * @return the name for the target {@link Scheduler}. */ public String getSchedulerName() { return schedulerPrefix == null ? schedulerName : format("[%s].%s", schedulerPrefix, schedulerName); } /** * @return {@code true} if {@link #withName(String)} was called with a non-null value, {@code false} otherwise. */ public boolean hasName() { return schedulerName != null; } /** * Sets the rejection policy to use when dispatching to a busy {@link Scheduler}. * <p> * This is only applicable for <b>custom</b> {@link Scheduler}s. The policy cannot be changed for the runtime managed * {@link Scheduler}. * * @see SchedulerBusyException * * @return the updated configuration */ public SchedulerConfig withRejectionAction(RejectionAction rejectionAction) { requireNonNull(rejectionAction); return new SchedulerConfig(maxConcurrentTasks, schedulerPrefix, schedulerName, rejectionAction, shutdownTimeoutMillis); } /** * @return the {@link RejectionAction} for the target custom {@link Scheduler}. */ public RejectionAction getRejectionAction() { return rejectionAction; } /** * Sets the graceful shutdown timeout to use when stopping the target {@link Scheduler}. * * @param shutdownTimeoutSupplier a supplier of the value of the timeout to use when gracefully stopping the target * {@link Scheduler}, expressed in the provided {@link TimeUnit}. * @param shutdownTimeoutUnit the unit of the timeout to use when gracefully stopping the target {@link Scheduler}. * @return the updated configuration */ public SchedulerConfig withShutdownTimeout(Supplier<Long> shutdownTimeoutSupplier, TimeUnit shutdownTimeoutUnit) { requireNonNull(shutdownTimeoutUnit); return new SchedulerConfig(maxConcurrentTasks, schedulerPrefix, schedulerName, rejectionAction, () -> { long shutdownTimeout = shutdownTimeoutSupplier.get(); validateTimeoutValue(shutdownTimeout); return shutdownTimeoutUnit.toMillis(shutdownTimeout); }); } /** * Sets the graceful shutdown timeout to use when stopping the target {@link Scheduler}. * * @param shutdownTimeout the value of the timeout to use when gracefully stopping the target {@link Scheduler}, expressed in * the provided {@link TimeUnit}. * @param shutdownTimeoutUnit the unit of the timeout to use when gracefully stopping the target {@link Scheduler}. * @return the updated configuration */ public SchedulerConfig withShutdownTimeout(long shutdownTimeout, TimeUnit shutdownTimeoutUnit) { requireNonNull(shutdownTimeoutUnit); validateTimeoutValue(shutdownTimeout); return withShutdownTimeout(() -> shutdownTimeout, shutdownTimeoutUnit); } private void validateTimeoutValue(long shutdownTimeout) { if (shutdownTimeout < 0) { throw new IllegalArgumentException(format("'shutdownTimeout' must be a possitive long. %d passed", shutdownTimeout)); } } /** * @return a supplier of the timeout to use when gracefully stopping the target {@link Scheduler}, in millis. */ public Supplier<Long> getShutdownTimeoutMillis() { return shutdownTimeoutMillis; } }