/******************************************************************************* * Copyright 2013 André Rouél * * 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 net.sf.uadetector.internal.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import net.sf.qualitycheck.Check; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This utility is intended to provide predefined {@link ExecutorService}s which runs in background and can be easily * shut-downed within {@link #shutdownAll()} if necessary. * * @author André Rouél */ public final class ExecutorServices { /** * This synchronized {@link Set} is a registry of all distributed background executors by this utility. * <p> * The containing {@link ExecutorService}s will be used to run a concrete update of <i>UAS data</i> instantly in * background. */ private static final Set<ExecutorService> BACKGROUND_EXECUTORS = Collections.synchronizedSet(new HashSet<ExecutorService>(3)); /** * Default name of a thread which will be used to run a concrete update within a background executor */ private static final String DEFAULT_BACKGROUND_EXECUTOR_NAME = "update-operation"; /** * Default name of a thread which will be created within a scheduler */ private static final String DEFAULT_SCHEDULER_NAME = "update-scheduler"; /** * Corresponding logger for this class */ private static final Logger LOG = LoggerFactory.getLogger(ExecutorServices.class); /** * This synchronized {@link Set} is a registry of all distributed schedulers by this utility. * <p> * The containing {@link ScheduledExecutorService}s will be used to schedule commands which updates the <i>UAS * data</i> in defined intervals. */ private static final Set<ScheduledExecutorService> SCHEDULERS = Collections.synchronizedSet(new HashSet<ScheduledExecutorService>(3)); /** * Timeout (in seconds) to shutdown all available executors at the latest */ public static final long SHUTDOWN_DURATION = 5; /** * Creates a single-threaded executor that is registered by this class in order to shut it down later (when it * becomes necessary). * * @return a new background executor */ public static ExecutorService createBackgroundExecutor() { final ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory(DEFAULT_BACKGROUND_EXECUTOR_NAME)); BACKGROUND_EXECUTORS.add(executor); return executor; } /** * Creates a single-threaded scheduler that is registered by this class in order to shut it down later (when it * becomes necessary). * * @return a new scheduler */ public static ScheduledExecutorService createScheduler() { final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new DaemonThreadFactory(DEFAULT_SCHEDULER_NAME)); SCHEDULERS.add(scheduler); return scheduler; } /** * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified default time * (which is {@value #SHUTDOWN_DURATION} seconds). * * @param executorService * executor to stop */ public static void shutdown(@Nonnull final ExecutorService executorService) { Check.notNull(executorService, "executorService"); shutdown(executorService, SHUTDOWN_DURATION, TimeUnit.SECONDS); } /** * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified time. * * @param executorService * executor to stop * @param duration * duration as a numerical value * @param unit * duration unit */ public static void shutdown(@Nonnull final ExecutorService executorService, @Nonnegative final long duration, @Nonnull final TimeUnit unit) { Check.notNull(executorService, "executorService"); Check.notNull(duration, "duration"); Check.notNull(unit, "unit"); executorService.shutdown(); try { if (!executorService.awaitTermination(duration, unit)) { LOG.info(String.format("Executor did not terminate in %s %s.", duration, unit.name().toLowerCase())); final List<Runnable> droppedTasks = executorService.shutdownNow(); LOG.info("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); } unregisterIfPossible(executorService); } catch (final InterruptedException e) { LOG.warn("Executor termination failed: " + e.getLocalizedMessage(), e); } } /** * Shuts down all registered scheduler and background workers as soon as possible, but at the latest in specified * {@link #SHUTDOWN_DURATION} seconds. */ public static void shutdownAll() { for (final ExecutorService executor : new ArrayList<ExecutorService>(BACKGROUND_EXECUTORS)) { shutdown(executor); BACKGROUND_EXECUTORS.remove(executor); } for (final ScheduledExecutorService scheduler : new ArrayList<ScheduledExecutorService>(SCHEDULERS)) { shutdown(scheduler); SCHEDULERS.remove(scheduler); } } /** * Unregisters the given {@code ExecutorService} if it is an instance of {@code ScheduledExecutorService} from the * list of registered schedulers. * * @param executorService * a possible scheduler */ private static void unregisterIfPossible(final ExecutorService executorService) { if (executorService instanceof ScheduledExecutorService) { SCHEDULERS.remove(executorService); } else { BACKGROUND_EXECUTORS.remove(executorService); } } /** * <strong>Attention:</strong> This class is not intended to create objects from it. */ private ExecutorServices() { // This class is not intended to create objects from it. } }