ExecutorServices.java

  1. /*******************************************************************************
  2.  * Copyright 2013 André Rouél
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *   http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  ******************************************************************************/
  16. package net.sf.uadetector.internal.util;

  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.HashSet;
  20. import java.util.List;
  21. import java.util.Set;
  22. import java.util.concurrent.ExecutorService;
  23. import java.util.concurrent.Executors;
  24. import java.util.concurrent.ScheduledExecutorService;
  25. import java.util.concurrent.TimeUnit;

  26. import javax.annotation.Nonnegative;
  27. import javax.annotation.Nonnull;

  28. import net.sf.qualitycheck.Check;

  29. import org.slf4j.Logger;
  30. import org.slf4j.LoggerFactory;

  31. /**
  32.  * This utility is intended to provide predefined {@link ExecutorService}s which runs in background and can be easily
  33.  * shut-downed within {@link #shutdownAll()} if necessary.
  34.  *
  35.  * @author André Rouél
  36.  */
  37. public final class ExecutorServices {

  38.     /**
  39.      * This synchronized {@link Set} is a registry of all distributed background executors by this utility.
  40.      * <p>
  41.      * The containing {@link ExecutorService}s will be used to run a concrete update of <i>UAS data</i> instantly in
  42.      * background.
  43.      */
  44.     private static final Set<ExecutorService> BACKGROUND_EXECUTORS = Collections.synchronizedSet(new HashSet<ExecutorService>(3));

  45.     /**
  46.      * Default name of a thread which will be used to run a concrete update within a background executor
  47.      */
  48.     private static final String DEFAULT_BACKGROUND_EXECUTOR_NAME = "update-operation";

  49.     /**
  50.      * Default name of a thread which will be created within a scheduler
  51.      */
  52.     private static final String DEFAULT_SCHEDULER_NAME = "update-scheduler";

  53.     /**
  54.      * Corresponding logger for this class
  55.      */
  56.     private static final Logger LOG = LoggerFactory.getLogger(ExecutorServices.class);

  57.     /**
  58.      * This synchronized {@link Set} is a registry of all distributed schedulers by this utility.
  59.      * <p>
  60.      * The containing {@link ScheduledExecutorService}s will be used to schedule commands which updates the <i>UAS
  61.      * data</i> in defined intervals.
  62.      */
  63.     private static final Set<ScheduledExecutorService> SCHEDULERS = Collections.synchronizedSet(new HashSet<ScheduledExecutorService>(3));

  64.     /**
  65.      * Timeout (in seconds) to shutdown all available executors at the latest
  66.      */
  67.     public static final long SHUTDOWN_DURATION = 5;

  68.     /**
  69.      * Creates a single-threaded executor that is registered by this class in order to shut it down later (when it
  70.      * becomes necessary).
  71.      *
  72.      * @return a new background executor
  73.      */
  74.     public static ExecutorService createBackgroundExecutor() {
  75.         final ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory(DEFAULT_BACKGROUND_EXECUTOR_NAME));
  76.         BACKGROUND_EXECUTORS.add(executor);
  77.         return executor;
  78.     }

  79.     /**
  80.      * Creates a single-threaded scheduler that is registered by this class in order to shut it down later (when it
  81.      * becomes necessary).
  82.      *
  83.      * @return a new scheduler
  84.      */
  85.     public static ScheduledExecutorService createScheduler() {
  86.         final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new DaemonThreadFactory(DEFAULT_SCHEDULER_NAME));
  87.         SCHEDULERS.add(scheduler);
  88.         return scheduler;
  89.     }

  90.     /**
  91.      * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified default time
  92.      * (which is {@value #SHUTDOWN_DURATION} seconds).
  93.      *
  94.      * @param executorService
  95.      *            executor to stop
  96.      */
  97.     public static void shutdown(@Nonnull final ExecutorService executorService) {
  98.         Check.notNull(executorService, "executorService");
  99.         shutdown(executorService, SHUTDOWN_DURATION, TimeUnit.SECONDS);
  100.     }

  101.     /**
  102.      * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified time.
  103.      *
  104.      * @param executorService
  105.      *            executor to stop
  106.      * @param duration
  107.      *            duration as a numerical value
  108.      * @param unit
  109.      *            duration unit
  110.      */
  111.     public static void shutdown(@Nonnull final ExecutorService executorService, @Nonnegative final long duration,
  112.             @Nonnull final TimeUnit unit) {
  113.         Check.notNull(executorService, "executorService");
  114.         Check.notNull(duration, "duration");
  115.         Check.notNull(unit, "unit");

  116.         executorService.shutdown();
  117.         try {
  118.             if (!executorService.awaitTermination(duration, unit)) {
  119.                 LOG.info(String.format("Executor did not terminate in %s %s.", duration, unit.name().toLowerCase()));
  120.                 final List<Runnable> droppedTasks = executorService.shutdownNow();
  121.                 LOG.info("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed.");
  122.             }
  123.             unregisterIfPossible(executorService);
  124.         } catch (final InterruptedException e) {
  125.             LOG.warn("Executor termination failed: " + e.getLocalizedMessage(), e);
  126.         }
  127.     }

  128.     /**
  129.      * Shuts down all registered scheduler and background workers as soon as possible, but at the latest in specified
  130.      * {@link #SHUTDOWN_DURATION} seconds.
  131.      */
  132.     public static void shutdownAll() {
  133.         for (final ExecutorService executor : new ArrayList<ExecutorService>(BACKGROUND_EXECUTORS)) {
  134.             shutdown(executor);
  135.             BACKGROUND_EXECUTORS.remove(executor);
  136.         }
  137.         for (final ScheduledExecutorService scheduler : new ArrayList<ScheduledExecutorService>(SCHEDULERS)) {
  138.             shutdown(scheduler);
  139.             SCHEDULERS.remove(scheduler);
  140.         }
  141.     }

  142.     /**
  143.      * Unregisters the given {@code ExecutorService} if it is an instance of {@code ScheduledExecutorService} from the
  144.      * list of registered schedulers.
  145.      *
  146.      * @param executorService
  147.      *            a possible scheduler
  148.      */
  149.     private static void unregisterIfPossible(final ExecutorService executorService) {
  150.         if (executorService instanceof ScheduledExecutorService) {
  151.             SCHEDULERS.remove(executorService);
  152.         } else {
  153.             BACKGROUND_EXECUTORS.remove(executorService);
  154.         }
  155.     }

  156.     /**
  157.      * <strong>Attention:</strong> This class is not intended to create objects from it.
  158.      */
  159.     private ExecutorServices() {
  160.         // This class is not intended to create objects from it.
  161.     }

  162. }