View Javadoc
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.logging.log4j.core.config;
18  
19  import org.apache.logging.log4j.Logger;
20  import org.apache.logging.log4j.core.AbstractLifeCycle;
21  import org.apache.logging.log4j.core.async.DaemonThreadFactory;
22  import org.apache.logging.log4j.core.util.CronExpression;
23  import org.apache.logging.log4j.status.StatusLogger;
24  
25  import java.util.Date;
26  import java.util.concurrent.Callable;
27  import java.util.concurrent.ScheduledExecutorService;
28  import java.util.concurrent.ScheduledFuture;
29  import java.util.concurrent.ScheduledThreadPoolExecutor;
30  import java.util.concurrent.TimeUnit;
31  
32  /**
33   *
34   */
35  public class ConfigurationScheduler extends AbstractLifeCycle {
36  
37      private static final Logger LOGGER = StatusLogger.getLogger();
38      private ScheduledExecutorService executorService;
39  
40      private int scheduledItems = 0;
41  
42  
43      @Override
44      public void start() {
45          super.start();
46          if (scheduledItems > 0) {
47              LOGGER.debug("Starting {} Log4j2Scheduled threads", scheduledItems);
48              if (scheduledItems > 5) {
49                  scheduledItems = 5;
50              }
51              executorService = new ScheduledThreadPoolExecutor(scheduledItems, new DaemonThreadFactory("Log4j2Scheduled-"));
52          } else {
53              LOGGER.debug("No scheduled items");
54          }
55      }
56  
57      @Override
58      public void stop() {
59          if (executorService != null) {
60              LOGGER.debug("Stopping Log4j2Scheduled threads.");
61              executorService.shutdown();
62          }
63          super.stop();
64      }
65  
66      /**
67       * Increment the number of threads in the pool.
68       */
69      public void incrementScheduledItems() {
70          if (!isStarted()) {
71              ++scheduledItems;
72          } else {
73              LOGGER.error("Attempted to increment scheduled items after start");
74          }
75      }
76  
77      /**
78       * Decrement the number of threads in the pool
79       */
80      public void decrementScheduledItems() {
81          if (!isStarted() && scheduledItems > 0) {
82              --scheduledItems;
83          }
84      }
85  
86      /**
87       * Creates and executes a ScheduledFuture that becomes enabled after the given delay.
88       * @param callable the function to execute.
89       * @param delay the time from now to delay execution.
90       * @param unit the time unit of the delay parameter.
91       * @return a ScheduledFuture that can be used to extract result or cancel.
92       *
93       */
94      public <V> ScheduledFuture<V> schedule(final Callable<V> callable, final long delay, final TimeUnit unit) {
95          return executorService.schedule(callable, delay, unit);
96      }
97  
98      /**
99       * 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(final Runnable command, final long delay, final 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(final CronExpression cronExpression, final Runnable command) {
118         final CronRunnable runnable = new CronRunnable(command, cronExpression);
119         final ScheduledFuture<?> future = schedule(runnable, nextFireInterval(cronExpression), TimeUnit.MILLISECONDS);
120         final 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(final Runnable command, final long initialDelay, final long period, final 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(final Runnable command, final long initialDelay, final long delay, final 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(final Runnable runnable, final CronExpression cronExpression) {
162             this.cronExpression = cronExpression;
163             this.runnable = runnable;
164         }
165 
166         public void setScheduledFuture(final CronScheduledFuture<?> future) {
167             this.scheduledFuture = future;
168         }
169 
170         @Override
171         public void run() {
172             try {
173                 runnable.run();
174             } catch(final Throwable ex) {
175                 LOGGER.error("Error running command", ex);
176             } finally {
177                 final ScheduledFuture<?> future = schedule(this, nextFireInterval(cronExpression), TimeUnit.MILLISECONDS);
178                 scheduledFuture.setScheduledFuture(future);
179             }
180         }
181     }
182 
183     private long nextFireInterval(final CronExpression cronExpression) {
184         final Date now = new Date();
185         final Date fireDate = cronExpression.getNextValidTimeAfter(now);
186         return fireDate.getTime() - now.getTime();
187     }
188 
189 }