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}