blob: f8ddf486a53e0eeefe2bc4810d24671de8b78392 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.appender.rolling.action;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
/**
* Abstract action for processing files that are accepted by the specified PathFilters.
*/
public abstract class AbstractPathAction extends AbstractAction {
private final String basePathString;
private final Set<FileVisitOption> options;
private final int maxDepth;
private final List<PathCondition> pathConditions;
private final StrSubstitutor subst;
/**
* Creates a new AbstractPathAction that starts scanning for files to process from the specified base path.
*
* @param basePath base path from where to start scanning for files to process.
* @param followSymbolicLinks whether to follow symbolic links. Default is false.
* @param maxDepth The maxDepth parameter is the maximum number of levels of directories to visit. A value of 0
* means that only the starting file is visited, unless denied by the security manager. A value of
* MAX_VALUE may be used to indicate that all levels should be visited.
* @param pathFilters an array of path filters (if more than one, they all need to accept a path before it is
* processed).
*/
protected AbstractPathAction(final String basePath, final boolean followSymbolicLinks, final int maxDepth,
final PathCondition[] pathFilters, final StrSubstitutor subst) {
this.basePathString = basePath;
this.options = followSymbolicLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS)
: Collections.<FileVisitOption> emptySet();
this.maxDepth = maxDepth;
this.pathConditions = Arrays.asList(Arrays.copyOf(pathFilters, pathFilters.length));
this.subst = subst;
}
@Override
public boolean execute() throws IOException {
return execute(createFileVisitor(getBasePath(), pathConditions));
}
public boolean execute(final FileVisitor<Path> visitor) throws IOException {
final long start = System.nanoTime();
LOGGER.debug("Starting {}", this);
Files.walkFileTree(getBasePath(), options, maxDepth, visitor);
final double duration = System.nanoTime() - start;
LOGGER.debug("{} complete in {} seconds", getClass().getSimpleName(), duration / TimeUnit.SECONDS.toNanos(1));
// TODO return (visitor.success || ignoreProcessingFailure)
return true; // do not abort rollover even if processing failed
}
/**
* Creates a new {@code FileVisitor<Path>} to pass to the {@link Files#walkFileTree(Path, Set, int, FileVisitor)}
* method when the {@link #execute()} method is invoked.
* <p>
* The visitor is responsible for processing the files it encounters that are accepted by all filters.
*
* @param visitorBaseDir base dir from where to start scanning for files to process
* @param conditions filters that determine if a file should be processed
* @return a new {@code FileVisitor<Path>}
*/
protected abstract FileVisitor<Path> createFileVisitor(final Path visitorBaseDir,
final List<PathCondition> conditions);
/**
* Returns the base path from where to start scanning for files to delete. Lookups are resolved, so if the
* configuration was <code>&lt;Delete basePath="${sys:user.home}/abc" /&gt;</code> then this method returns a path
* to the "abc" file or directory in the user's home directory.
*
* @return the base path (all lookups resolved)
*/
public Path getBasePath() {
return Paths.get(subst.replace(getBasePathString()));
}
/**
* Returns the base path as it was specified in the configuration. Lookups are not resolved.
*
* @return the base path as it was specified in the configuration
*/
public String getBasePathString() {
return basePathString;
}
public StrSubstitutor getStrSubstitutor() {
return subst;
}
/**
* Returns whether to follow symbolic links or not.
*
* @return the options
*/
public Set<FileVisitOption> getOptions() {
return Collections.unmodifiableSet(options);
}
/**
* Returns whether to follow symbolic links or not.
*
* @return whether to follow symbolic links or not
*/
public boolean isFollowSymbolicLinks() {
return options.contains(FileVisitOption.FOLLOW_LINKS);
}
/**
* Returns the the maximum number of directory levels to visit.
*
* @return the maxDepth
*/
public int getMaxDepth() {
return maxDepth;
}
/**
* Returns the list of PathCondition objects.
*
* @return the pathFilters
*/
public List<PathCondition> getPathConditions() {
return Collections.unmodifiableList(pathConditions);
}
@Override
public String toString() {
return getClass().getSimpleName() + "[basePath=" + getBasePath() + ", options=" + options + ", maxDepth="
+ maxDepth + ", conditions=" + pathConditions + "]";
}
}