001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015 ForgeRock AS.
015 */
016package org.forgerock.audit.retention;
017
018import static org.forgerock.audit.events.handlers.FileBasedEventHandlerConfiguration.FileRotation.DEFAULT_ROTATION_FILE_SUFFIX;
019
020import java.io.File;
021import java.nio.file.Path;
022import java.util.Arrays;
023import java.util.Collections;
024import java.util.LinkedList;
025import java.util.List;
026
027import org.forgerock.audit.util.LastModifiedTimeFileComparator;
028import org.joda.time.LocalDateTime;
029import org.joda.time.format.DateTimeFormat;
030import org.joda.time.format.DateTimeFormatter;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Creates a time stamp based file naming policy. Rotated files are renamed with a given prefix and a timestamp suffix.
036 */
037public class TimeStampFileNamingPolicy implements FileNamingPolicy {
038
039    private static final Logger logger = LoggerFactory.getLogger(TimeStampFileNamingPolicy.class);
040
041    private final File initialFile;
042    private DateTimeFormatter suffixDateFormat;
043    private final String prefix;
044    private final TimestampFilenameFilter timestampFilenameFilter;
045    private final LastModifiedTimeFileComparator lastModifiedTimeFileComparator = new LastModifiedTimeFileComparator();
046
047    /**
048     * Constructs a TimeStampFileNaming policy with a given initial file, a timestamp format, and a prefix string.
049     * @param initialFile The initial file that will be archived.
050     * @param timeStampFormat The timestamp format to append to the archived files. Should be a format that is
051     *                        understood by {@link DateTimeFormat}.
052     * @param prefix The prefix to prefix to the archived files.
053     */
054    public TimeStampFileNamingPolicy(final File initialFile, final String timeStampFormat, final String prefix) {
055        this.initialFile = initialFile;
056        this.prefix = prefix;
057
058        if (timeStampFormat != null && timeStampFormat.trim().length() > 0) {
059            try {
060                suffixDateFormat = DateTimeFormat.forPattern(timeStampFormat);
061            } catch (IllegalArgumentException iae) {
062                logger.info("Date format invalid: {}", timeStampFormat, iae);
063            }
064        }
065        if (suffixDateFormat == null) {
066            // fallback to a default date format, so the filenames will differ
067            suffixDateFormat = DateTimeFormat.forPattern(DEFAULT_ROTATION_FILE_SUFFIX);
068        }
069        this.timestampFilenameFilter = new TimestampFilenameFilter(initialFile, prefix, suffixDateFormat);
070    }
071
072    /**
073     * Gets the initial file.
074     * @return The initial file.
075     */
076    @Override
077    public File getInitialName() {
078        return initialFile;
079    }
080
081    /**
082     * Gets the next name for this {@link FileNamingPolicy}. The next name will be formatted with prefix first,
083     * then the initial filename and finally the timestamp will be appended.
084     * @return The next archived file according to this {@link FileNamingPolicy}.
085     */
086    @Override
087    public File getNextName() {
088        final StringBuilder newFileName = new StringBuilder();
089        final Path path = initialFile.toPath();
090
091        if (prefix != null) {
092            newFileName.append(prefix);
093        }
094
095        newFileName.append(path.getFileName());
096
097        if (suffixDateFormat != null) {
098            newFileName.append(LocalDateTime.now().toString(suffixDateFormat));
099        }
100
101        return path.resolveSibling(newFileName.toString()).toFile();
102    }
103
104    /**
105     * List the files in the initial file directory that match the prefix, name and suffix format.
106     * {@inheritDoc}
107     */
108    @Override
109    public List<File> listFiles() {
110        List<File> fileList =
111                new LinkedList<>(Arrays.asList(initialFile.getParentFile().listFiles(timestampFilenameFilter)));
112        // make sure the files are sorted from oldest to newest.
113        Collections.sort(fileList, Collections.reverseOrder(lastModifiedTimeFileComparator));
114        return fileList;
115    }
116}