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 java.util.Date;
20  import java.util.concurrent.Callable;
21  import java.util.concurrent.ScheduledExecutorService;
22  import java.util.concurrent.ScheduledFuture;
23  import java.util.concurrent.ScheduledThreadPoolExecutor;
24  import java.util.concurrent.TimeUnit;
25  
26  import org.apache.logging.log4j.core.AbstractLifeCycle;
27  import org.apache.logging.log4j.core.util.CronExpression;
28  import org.apache.logging.log4j.core.util.Log4jThreadFactory;
29  
30  /**
31   *
32   */
33  public class ConfigurationScheduler extends AbstractLifeCycle {
34  
35      private static final String SIMPLE_NAME = "Log4j2 " + ConfigurationScheduler.class.getSimpleName();
36      private static final int MAX_SCHEDULED_ITEMS = 5;
37      private ScheduledExecutorService executorService;
38  
39      private int scheduledItems = 0;
40  
41      @Override
42      public void start() {
43          super.start();
44          if (scheduledItems > 0) {
45              LOGGER.debug("{} starting {} threads", scheduledItems, SIMPLE_NAME);
46              scheduledItems = Math.min(scheduledItems, MAX_SCHEDULED_ITEMS);
47              executorService = new ScheduledThreadPoolExecutor(scheduledItems,
48                      Log4jThreadFactory.createDaemonThreadFactory("Scheduled"));
49          } else {
50              LOGGER.debug("{}: No scheduled items", SIMPLE_NAME);
51          }
52      }
53  
54      @Override
55      public boolean stop(final long timeout, final TimeUnit timeUnit) {
56          setStopping();
57          if (executorService != null) {
58              LOGGER.debug("{} shutting down threads in {}", SIMPLE_NAME, executorService);
59              executorService.shutdown();
60          }
61          setStopped();
62          return true;
63      }
64  
65      /**
66       * Increment the number of threads in the pool.
67       */
68      public void incrementScheduledItems() {
69          if (!isStarted()) {
70              ++scheduledItems;
71          } else {
72              LOGGER.error("{} attempted to increment scheduled items after start", SIMPLE_NAME);
73          }
74      }
75  
76      /**
77       * Decrement the number of threads in the pool
78       */
79      public void decrementScheduledItems() {
80          if (!isStarted() && scheduledItems > 0) {
81              --scheduledItems;
82          }
83      }
84  
85      /**
86       * Creates and executes a ScheduledFuture that becomes enabled after the given delay.
87       * @param <V> The result type returned by this Future
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 Date fireDate = cronExpression.getNextValidTimeAfter(new Date());
119         final CronRunnable runnable = new CronRunnable(command, cronExpression);
120         final ScheduledFuture<?> future = schedule(runnable, nextFireInterval(fireDate), TimeUnit.MILLISECONDS);
121         final CronScheduledFuture<?> cronScheduledFuture = new CronScheduledFuture<>(future, fireDate);
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(final Runnable command, final long initialDelay, final long period, final 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(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) {
153         return executorService.scheduleWithFixedDelay(command, initialDelay, delay, unit);
154     }
155 
156     public long nextFireInterval(final Date fireDate) {
157         return fireDate.getTime() - new Date().getTime();
158     }
159 
160     public class CronRunnable implements Runnable {
161 
162         private final CronExpression cronExpression;
163         private final Runnable runnable;
164         private CronScheduledFuture<?> scheduledFuture;
165 
166         public CronRunnable(final Runnable runnable, final CronExpression cronExpression) {
167             this.cronExpression = cronExpression;
168             this.runnable = runnable;
169         }
170 
171         public void setScheduledFuture(final CronScheduledFuture<?> future) {
172             this.scheduledFuture = future;
173         }
174 
175         @Override
176         public void run() {
177             try {
178                 runnable.run();
179             } catch(final Throwable ex) {
180                 LOGGER.error("{} caught error running command", SIMPLE_NAME, ex);
181             } finally {
182                 final Date fireDate = cronExpression.getNextValidTimeAfter(new Date());
183                 final ScheduledFuture<?> future = schedule(this, nextFireInterval(fireDate), TimeUnit.MILLISECONDS);
184                 scheduledFuture.reset(future, fireDate);
185             }
186         }
187     }
188 
189 }