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 static final long serialVersionUID = 4570411889877332287L; 039 private ScheduledExecutorService executorService; 040 041 private int scheduledItems = 0; 042 043 044 @Override 045 public void start() { 046 super.start(); 047 if (scheduledItems > 0) { 048 LOGGER.debug("Starting {} Log4j2Scheduled threads", scheduledItems); 049 if (scheduledItems > 5) { 050 scheduledItems = 5; 051 } 052 executorService = new ScheduledThreadPoolExecutor(scheduledItems, new DaemonThreadFactory("Log4j2Scheduled-")); 053 } else { 054 LOGGER.debug("No scheduled items"); 055 } 056 } 057 058 @Override 059 public void stop() { 060 if (executorService != null) { 061 LOGGER.debug("Stopping Log4j2Scheduled threads."); 062 executorService.shutdown(); 063 } 064 super.stop(); 065 } 066 067 /** 068 * Increment the number of threads in the pool. 069 */ 070 public void incrementScheduledItems() { 071 if (!isStarted()) { 072 ++scheduledItems; 073 } else { 074 LOGGER.error("Attempted to increment scheduled items after start"); 075 } 076 } 077 078 /** 079 * Decrement the number of threads in the pool 080 */ 081 public void decrementScheduledItems() { 082 if (!isStarted() && scheduledItems > 0) { 083 --scheduledItems; 084 } 085 } 086 087 /** 088 * Creates and executes a ScheduledFuture that becomes enabled after the given delay. 089 * @param callable the function to execute. 090 * @param delay the time from now to delay execution. 091 * @param unit the time unit of the delay parameter. 092 * @return a ScheduledFuture that can be used to extract result or cancel. 093 * 094 */ 095 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { 096 return executorService.schedule(callable, delay, unit); 097 } 098 099 /** 100 * Creates and executes a one-shot action that becomes enabled after the given delay. 101 * @param command the task to execute. 102 * @param delay the time from now to delay execution. 103 * @param unit the time unit of the delay parameter. 104 * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null 105 * upon completion. 106 */ 107 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { 108 return executorService.schedule(command, delay, unit); 109 } 110 111 112 /** 113 * Creates and executes an action that first based on a cron expression. 114 * @param cronExpression the cron expression describing the schedule. 115 * @param command The Runnable to run, 116 * @return a ScheduledFuture representing the next time the command will run. 117 */ 118 public CronScheduledFuture<?> scheduleWithCron(CronExpression cronExpression, Runnable command) { 119 CronRunnable runnable = new CronRunnable(command, cronExpression); 120 ScheduledFuture<?> future = schedule(runnable, nextFireInterval(cronExpression), TimeUnit.MILLISECONDS); 121 CronScheduledFuture<?> cronScheduledFuture = new CronScheduledFuture<>(future); 122 runnable.setScheduledFuture(cronScheduledFuture); 123 return cronScheduledFuture; 124 } 125 126 127 /** 128 * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently 129 * with the given period; that is executions will commence after initialDelay then initialDelay+period, 130 * then initialDelay + 2 * period, and so on. 131 * @param command the task to execute. 132 * @param initialDelay the time to delay first execution. 133 * @param period the period between successive executions. 134 * @param unit the time unit of the initialDelay and period parameters. 135 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an 136 * exception upon cancellation 137 */ 138 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { 139 return executorService.scheduleAtFixedRate(command, initialDelay, period, unit); 140 } 141 142 /** 143 * Creates and executes a periodic action that becomes enabled first after the given initial delay, and 144 * subsequently with the given delay between the termination of one execution and the commencement of the next. 145 * @param command the task to execute. 146 * @param initialDelay the time to delay first execution. 147 * @param delay the delay between the termination of one execution and the commencement of the next. 148 * @param unit the time unit of the initialDelay and delay parameters 149 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an 150 * exception upon cancellation 151 */ 152 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { 153 return executorService.scheduleWithFixedDelay(command, initialDelay, delay, unit); 154 } 155 156 private class CronRunnable implements Runnable { 157 158 private final CronExpression cronExpression; 159 private final Runnable runnable; 160 private CronScheduledFuture<?> scheduledFuture; 161 162 public CronRunnable(Runnable runnable, CronExpression cronExpression) { 163 this.cronExpression = cronExpression; 164 this.runnable = runnable; 165 } 166 167 public void setScheduledFuture(CronScheduledFuture<?> future) { 168 this.scheduledFuture = future; 169 } 170 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}