1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.configuration2.reloading; 18 19 import java.util.concurrent.Executors; 20 import java.util.concurrent.ScheduledExecutorService; 21 import java.util.concurrent.ScheduledFuture; 22 import java.util.concurrent.ThreadFactory; 23 import java.util.concurrent.TimeUnit; 24 25 import org.apache.commons.lang3.concurrent.BasicThreadFactory; 26 27 /** 28 * <p> 29 * A timer-based trigger for reloading checks. 30 * </p> 31 * <p> 32 * An instance of this class is constructed with a reference to a {@link ReloadingController} and a period. After 33 * calling the {@code start()} method a periodic task is started which calls 34 * {@link ReloadingController#checkForReloading(Object)} on the associated reloading controller. This way changes on a 35 * configuration source can be detected without client code having to poll actively. The {@code ReloadingController} 36 * will perform its checks and generates events if it detects the need for a reloading operation. 37 * </p> 38 * <p> 39 * Triggering of the controller can be disabled by calling the {@code stop()} method and later be resumed by calling 40 * {@code start()} again. When the trigger is no more needed its {@code shutdown()} method should be called. 41 * </p> 42 * <p> 43 * When creating an instance a {@code ScheduledExecutorService} can be provided which is then used by the object. 44 * Otherwise, a default executor service is created and used. When shutting down this object it can be specified whether 45 * the {@code ScheduledExecutorService} should be shut down, too. 46 * </p> 47 * 48 * @since 2.0 49 * @see ReloadingController 50 */ 51 public class PeriodicReloadingTrigger { 52 /** The executor service used by this trigger. */ 53 private final ScheduledExecutorService executorService; 54 55 /** The associated reloading controller. */ 56 private final ReloadingController controller; 57 58 /** The parameter to be passed to the controller. */ 59 private final Object controllerParam; 60 61 /** The period. */ 62 private final long period; 63 64 /** The time unit. */ 65 private final TimeUnit timeUnit; 66 67 /** Stores the future object for the current trigger task. */ 68 private ScheduledFuture<?> triggerTask; 69 70 /** 71 * Creates a new instance of {@code PeriodicReloadingTrigger} and sets all parameters. 72 * 73 * @param ctrl the {@code ReloadingController} (must not be <b>null</b>) 74 * @param ctrlParam the optional parameter to be passed to the controller when doing reloading checks 75 * @param triggerPeriod the period in which the controller is triggered 76 * @param unit the time unit for the period 77 * @param exec the executor service to use (can be <b>null</b>, then a default executor service is created 78 * @throws IllegalArgumentException if a required argument is missing 79 */ 80 public PeriodicReloadingTrigger(final ReloadingController ctrl, final Object ctrlParam, final long triggerPeriod, final TimeUnit unit, 81 final ScheduledExecutorService exec) { 82 if (ctrl == null) { 83 throw new IllegalArgumentException("ReloadingController must not be null!"); 84 } 85 86 controller = ctrl; 87 controllerParam = ctrlParam; 88 period = triggerPeriod; 89 timeUnit = unit; 90 executorService = exec != null ? exec : createDefaultExecutorService(); 91 } 92 93 /** 94 * Creates a new instance of {@code PeriodicReloadingTrigger} with a default executor service. 95 * 96 * @param ctrl the {@code ReloadingController} (must not be <b>null</b>) 97 * @param ctrlParam the optional parameter to be passed to the controller when doing reloading checks 98 * @param triggerPeriod the period in which the controller is triggered 99 * @param unit the time unit for the period 100 * @throws IllegalArgumentException if a required argument is missing 101 */ 102 public PeriodicReloadingTrigger(final ReloadingController ctrl, final Object ctrlParam, final long triggerPeriod, final TimeUnit unit) { 103 this(ctrl, ctrlParam, triggerPeriod, unit, null); 104 } 105 106 /** 107 * Starts this trigger. The associated {@code ReloadingController} will be triggered according to the specified period. 108 * The first triggering happens after a period. If this trigger is already started, this invocation has no effect. 109 */ 110 public synchronized void start() { 111 if (!isRunning()) { 112 triggerTask = getExecutorService().scheduleAtFixedRate(createTriggerTaskCommand(), period, period, timeUnit); 113 } 114 } 115 116 /** 117 * Stops this trigger. The associated {@code ReloadingController} is no more triggered. If this trigger is already 118 * stopped, this invocation has no effect. 119 */ 120 public synchronized void stop() { 121 if (isRunning()) { 122 triggerTask.cancel(false); 123 triggerTask = null; 124 } 125 } 126 127 /** 128 * Returns a flag whether this trigger is currently active. 129 * 130 * @return a flag whether this trigger is running 131 */ 132 public synchronized boolean isRunning() { 133 return triggerTask != null; 134 } 135 136 /** 137 * Shuts down this trigger and optionally shuts down the {@code ScheduledExecutorService} used by this object. This 138 * method should be called if this trigger is no more needed. It ensures that the trigger is stopped. If the parameter 139 * is <b>true</b>, the executor service is also shut down. This should be done if this trigger is the only user of this 140 * executor service. 141 * 142 * @param shutdownExecutor a flag whether the associated {@code ScheduledExecutorService} is to be shut down 143 */ 144 public void shutdown(final boolean shutdownExecutor) { 145 stop(); 146 if (shutdownExecutor) { 147 getExecutorService().shutdown(); 148 } 149 } 150 151 /** 152 * Shuts down this trigger and its {@code ScheduledExecutorService}. This is a shortcut for {@code shutdown(true)}. 153 * 154 * @see #shutdown(boolean) 155 */ 156 public void shutdown() { 157 shutdown(true); 158 } 159 160 /** 161 * Gets the {@code ScheduledExecutorService} used by this object. 162 * 163 * @return the associated {@code ScheduledExecutorService} 164 */ 165 ScheduledExecutorService getExecutorService() { 166 return executorService; 167 } 168 169 /** 170 * Creates the task which triggers the reloading controller. 171 * 172 * @return the newly created trigger task 173 */ 174 private Runnable createTriggerTaskCommand() { 175 return () -> controller.checkForReloading(controllerParam); 176 } 177 178 /** 179 * Creates a default executor service. This method is called if no executor has been passed to the constructor. 180 * 181 * @return the default executor service 182 */ 183 private static ScheduledExecutorService createDefaultExecutorService() { 184 final ThreadFactory factory = new BasicThreadFactory.Builder().namingPattern("ReloadingTrigger-%s").daemon(true).build(); 185 return Executors.newScheduledThreadPool(1, factory); 186 } 187 }