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.appender.rolling;
018
019import java.text.ParseException;
020import java.util.Date;
021import java.util.Objects;
022import java.util.concurrent.TimeUnit;
023
024import org.apache.logging.log4j.core.Core;
025import org.apache.logging.log4j.core.LogEvent;
026import org.apache.logging.log4j.core.config.Configuration;
027import org.apache.logging.log4j.core.config.ConfigurationScheduler;
028import org.apache.logging.log4j.core.config.CronScheduledFuture;
029import org.apache.logging.log4j.core.config.Scheduled;
030import org.apache.logging.log4j.core.config.plugins.Plugin;
031import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
032import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
033import org.apache.logging.log4j.core.config.plugins.PluginFactory;
034import org.apache.logging.log4j.core.util.CronExpression;
035
036/**
037 * Rolls a file over based on a cron schedule.
038 */
039@Plugin(name = "CronTriggeringPolicy", category = Core.CATEGORY_NAME, printObject = true)
040@Scheduled
041public final class CronTriggeringPolicy extends AbstractTriggeringPolicy {
042
043    private static final String defaultSchedule = "0 0 0 * * ?";
044    private RollingFileManager manager;
045    private final CronExpression cronExpression;
046    private final Configuration configuration;
047    private final boolean checkOnStartup;
048    private volatile Date lastRollDate;
049    private CronScheduledFuture<?> future;
050
051    private CronTriggeringPolicy(final CronExpression schedule, final boolean checkOnStartup,
052            final Configuration configuration) {
053        this.cronExpression = Objects.requireNonNull(schedule, "schedule");
054        this.configuration = Objects.requireNonNull(configuration, "configuration");
055        this.checkOnStartup = checkOnStartup;
056    }
057
058    /**
059     * Initializes the policy.
060     *
061     * @param aManager
062     *            The RollingFileManager.
063     */
064    @Override
065    public void initialize(final RollingFileManager aManager) {
066        this.manager = aManager;
067        final Date now = new Date();
068        final Date lastRollForFile = cronExpression.getPrevFireTime(new Date(this.manager.getFileTime()));
069        final Date lastRegularRoll = cronExpression.getPrevFireTime(new Date());
070        aManager.getPatternProcessor().setCurrentFileTime(lastRegularRoll.getTime());
071        LOGGER.debug("LastRollForFile {}, LastRegularRole {}", lastRollForFile, lastRegularRoll);
072        aManager.getPatternProcessor().setPrevFileTime(lastRegularRoll.getTime());
073        aManager.getPatternProcessor().setTimeBased(true);
074        if (checkOnStartup && lastRollForFile != null && lastRegularRoll != null &&
075                lastRollForFile.before(lastRegularRoll)) {
076            lastRollDate = lastRollForFile;
077            rollover();
078        }
079
080        final ConfigurationScheduler scheduler = configuration.getScheduler();
081        if (!scheduler.isExecutorServiceSet()) {
082            // make sure we have a thread pool
083            scheduler.incrementScheduledItems();
084        }
085        if (!scheduler.isStarted()) {
086            scheduler.start();
087        }
088        lastRollDate = lastRegularRoll;
089        future = scheduler.scheduleWithCron(cronExpression, now, new CronTrigger());
090        LOGGER.debug(scheduler.toString());
091    }
092
093    /**
094     * Determines whether a rollover should occur.
095     *
096     * @param event
097     *            A reference to the currently event.
098     * @return true if a rollover should occur.
099     */
100    @Override
101    public boolean isTriggeringEvent(final LogEvent event) {
102        return false;
103    }
104
105    public CronExpression getCronExpression() {
106        return cronExpression;
107    }
108
109    /**
110     * Creates a ScheduledTriggeringPolicy.
111     *
112     * @param configuration
113     *            the Configuration.
114     * @param evaluateOnStartup
115     *            check if the file should be rolled over immediately.
116     * @param schedule
117     *            the cron expression.
118     * @return a ScheduledTriggeringPolicy.
119     */
120    @PluginFactory
121    public static CronTriggeringPolicy createPolicy(@PluginConfiguration final Configuration configuration,
122            @PluginAttribute("evaluateOnStartup") final String evaluateOnStartup,
123            @PluginAttribute("schedule") final String schedule) {
124        CronExpression cronExpression;
125        final boolean checkOnStartup = Boolean.parseBoolean(evaluateOnStartup);
126        if (schedule == null) {
127            LOGGER.info("No schedule specified, defaulting to Daily");
128            cronExpression = getSchedule(defaultSchedule);
129        } else {
130            cronExpression = getSchedule(schedule);
131            if (cronExpression == null) {
132                LOGGER.error("Invalid expression specified. Defaulting to Daily");
133                cronExpression = getSchedule(defaultSchedule);
134            }
135        }
136        return new CronTriggeringPolicy(cronExpression, checkOnStartup, configuration);
137    }
138
139    private static CronExpression getSchedule(final String expression) {
140        try {
141            return new CronExpression(expression);
142        } catch (final ParseException pe) {
143            LOGGER.error("Invalid cron expression - " + expression, pe);
144            return null;
145        }
146    }
147
148    private void rollover() {
149        manager.rollover(cronExpression.getPrevFireTime(new Date()), lastRollDate);
150        if (future != null) {
151            lastRollDate = future.getFireTime();
152        }
153    }
154
155    @Override
156    public boolean stop(final long timeout, final TimeUnit timeUnit) {
157        setStopping();
158        final boolean stopped = stop(future);
159        setStopped();
160        return stopped;
161    }
162
163    @Override
164    public String toString() {
165        return "CronTriggeringPolicy(schedule=" + cronExpression.getCronExpression() + ")";
166    }
167
168    private class CronTrigger implements Runnable {
169
170        @Override
171        public void run() {
172            rollover();
173        }
174    }
175}