001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.config; 018 019import org.apache.logging.log4j.Logger; 020import org.apache.logging.log4j.core.AbstractLifeCycle; 021import org.apache.logging.log4j.core.async.DaemonThreadFactory; 022import org.apache.logging.log4j.core.util.CronExpression; 023import org.apache.logging.log4j.status.StatusLogger; 024 025import java.util.Date; 026import java.util.concurrent.Callable; 027import java.util.concurrent.ScheduledExecutorService; 028import java.util.concurrent.ScheduledFuture; 029import java.util.concurrent.ScheduledThreadPoolExecutor; 030import java.util.concurrent.TimeUnit; 031 032/** 033 * 034 */ 035public class ConfigurationScheduler extends AbstractLifeCycle { 036 037 private static final Logger LOGGER = StatusLogger.getLogger(); 038 private ScheduledExecutorService executorService; 039 040 private int scheduledItems = 0; 041 042 043 @Override 044 public void start() { 045 super.start(); 046 if (scheduledItems > 0) { 047 LOGGER.debug("Starting {} Log4j2Scheduled threads", scheduledItems); 048 if (scheduledItems > 5) { 049 scheduledItems = 5; 050 } 051 executorService = new ScheduledThreadPoolExecutor(scheduledItems, new DaemonThreadFactory("Log4j2Scheduled-")); 052 } else { 053 LOGGER.debug("No scheduled items"); 054 } 055 } 056 057 @Override 058 public void stop() { 059 if (executorService != null) { 060 LOGGER.debug("Stopping Log4j2Scheduled threads."); 061 executorService.shutdown(); 062 } 063 super.stop(); 064 } 065 066 /** 067 * Increment the number of threads in the pool. 068 */ 069 public void incrementScheduledItems() { 070 if (!isStarted()) { 071 ++scheduledItems; 072 } else { 073 LOGGER.error("Attempted to increment scheduled items after start"); 074 } 075 } 076 077 /** 078 * Decrement the number of threads in the pool 079 */ 080 public void decrementScheduledItems() { 081 if (!isStarted() && scheduledItems > 0) { 082 --scheduledItems; 083 } 084 } 085 086 /** 087 * Creates and executes a ScheduledFuture that becomes enabled after the given delay. 088 * @param callable the function to execute. 089 * @param delay the time from now to delay execution. 090 * @param unit the time unit of the delay parameter. 091 * @return a ScheduledFuture that can be used to extract result or cancel. 092 * 093 */ 094 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { 095 return executorService.schedule(callable, delay, unit); 096 } 097 098 /** 099 * Creates and executes a one-shot action that becomes enabled after the given delay. 100 * @param command the task to execute. 101 * @param delay the time from now to delay execution. 102 * @param unit the time unit of the delay parameter. 103 * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null 104 * upon completion. 105 */ 106 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { 107 return executorService.schedule(command, delay, unit); 108 } 109 110 111 /** 112 * Creates and executes an action that first based on a cron expression. 113 * @param cronExpression the cron expression describing the schedule. 114 * @param command The Runnable to run, 115 * @return a ScheduledFuture representing the next time the command will run. 116 */ 117 public CronScheduledFuture<?> scheduleWithCron(CronExpression cronExpression, Runnable command) { 118 CronRunnable runnable = new CronRunnable(command, cronExpression); 119 ScheduledFuture<?> future = schedule(runnable, nextFireInterval(cronExpression), TimeUnit.MILLISECONDS); 120 CronScheduledFuture<?> cronScheduledFuture = new CronScheduledFuture<>(future); 121 runnable.setScheduledFuture(cronScheduledFuture); 122 return cronScheduledFuture; 123 } 124 125 126 /** 127 * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently 128 * with the given period; that is executions will commence after initialDelay then initialDelay+period, 129 * then initialDelay + 2 * period, and so on. 130 * @param command the task to execute. 131 * @param initialDelay the time to delay first execution. 132 * @param period the period between successive executions. 133 * @param unit the time unit of the initialDelay and period parameters. 134 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an 135 * exception upon cancellation 136 */ 137 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { 138 return executorService.scheduleAtFixedRate(command, initialDelay, period, unit); 139 } 140 141 /** 142 * Creates and executes a periodic action that becomes enabled first after the given initial delay, and 143 * subsequently with the given delay between the termination of one execution and the commencement of the next. 144 * @param command the task to execute. 145 * @param initialDelay the time to delay first execution. 146 * @param delay the delay between the termination of one execution and the commencement of the next. 147 * @param unit the time unit of the initialDelay and delay parameters 148 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an 149 * exception upon cancellation 150 */ 151 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { 152 return executorService.scheduleWithFixedDelay(command, initialDelay, delay, unit); 153 } 154 155 private class CronRunnable implements Runnable { 156 157 private final CronExpression cronExpression; 158 private final Runnable runnable; 159 private CronScheduledFuture<?> scheduledFuture; 160 161 public CronRunnable(Runnable runnable, CronExpression cronExpression) { 162 this.cronExpression = cronExpression; 163 this.runnable = runnable; 164 } 165 166 public void setScheduledFuture(CronScheduledFuture<?> future) { 167 this.scheduledFuture = future; 168 } 169 170 @Override 171 public void run() { 172 try { 173 runnable.run(); 174 } catch(Throwable ex) { 175 LOGGER.error("Error running command", ex); 176 } finally { 177 ScheduledFuture<?> future = schedule(this, nextFireInterval(cronExpression), TimeUnit.MILLISECONDS); 178 scheduledFuture.setScheduledFuture(future); 179 } 180 } 181 } 182 183 private long nextFireInterval(CronExpression cronExpression) { 184 Date now = new Date(); 185 Date fireDate = cronExpression.getNextValidTimeAfter(now); 186 return fireDate.getTime() - now.getTime(); 187 } 188 189}