LOG4J2-435 added support for ScriptCondition in Delete action Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/1bc44b66 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/1bc44b66 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/1bc44b66
Branch: refs/heads/master Commit: 1bc44b66b86db518df9ec7b7176227a3d6915244 Parents: c01f5bb Author: rpopma <[email protected]> Authored: Sat Dec 5 19:55:30 2015 +0900 Committer: rpopma <[email protected]> Committed: Sat Dec 5 19:55:30 2015 +0900 ---------------------------------------------------------------------- .../appender/rolling/action/DeleteAction.java | 78 +++++++++++-- .../rolling/action/ScriptCondition.java | 117 +++++++++++++++++++ 2 files changed, 188 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1bc44b66/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeleteAction.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeleteAction.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeleteAction.java index 126fb5d..c2c385f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeleteAction.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeleteAction.java @@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.appender.rolling.action; import java.io.IOException; import java.nio.file.FileVisitor; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -39,6 +40,7 @@ public class DeleteAction extends AbstractPathAction { private final PathSorter pathSorter; private final boolean testMode; + private final ScriptCondition scriptCondition; /** * Creates a new DeleteAction that starts scanning for files to delete from the specified base path. @@ -54,13 +56,16 @@ public class DeleteAction extends AbstractPathAction { * @param sorter sorts * @param pathConditions an array of path filters (if more than one, they all need to accept a path before it is * deleted). + * @param scriptCondition */ DeleteAction(final String basePath, final boolean followSymbolicLinks, final int maxDepth, final boolean testMode, - final PathSorter sorter, final PathCondition[] pathConditions, final StrSubstitutor subst) { + final PathSorter sorter, final PathCondition[] pathConditions, final ScriptCondition scriptCondition, + final StrSubstitutor subst) { super(basePath, followSymbolicLinks, maxDepth, pathConditions, subst); this.testMode = testMode; this.pathSorter = Objects.requireNonNull(sorter, "sorter"); - if (pathConditions == null || pathConditions.length == 0) { + this.scriptCondition = scriptCondition; + if (scriptCondition == null && (pathConditions == null || pathConditions.length == 0)) { LOGGER.error("Missing Delete conditions: unconditional Delete not supported"); throw new IllegalArgumentException("Unconditional Delete not supported"); } @@ -69,15 +74,66 @@ public class DeleteAction extends AbstractPathAction { /* * (non-Javadoc) * + * @see org.apache.logging.log4j.core.appender.rolling.action.AbstractPathAction#execute() + */ + @Override + public boolean execute() throws IOException { + if (scriptCondition != null) { + return executeScript(); + } else { + return super.execute(); + } + } + + private boolean executeScript() throws IOException { + final List<PathWithAttributes> selectedForDeletion = callScript(); + if (selectedForDeletion == null) { + LOGGER.trace("Script returned null list (no files to delete)"); + return true; + } + deleteSelectedFiles(selectedForDeletion); + return true; + } + + private List<PathWithAttributes> callScript() throws IOException { + final List<PathWithAttributes> sortedPaths = getSortedPaths(); + trace("Sorted paths:", sortedPaths); + final List<PathWithAttributes> result = scriptCondition.selectFilesToDelete(getBasePath(), sortedPaths); + return result; + } + + private void deleteSelectedFiles(final List<PathWithAttributes> selectedForDeletion) throws IOException { + trace("Paths the script selected for deletion:", selectedForDeletion); + for (final PathWithAttributes pathWithAttributes : selectedForDeletion) { + final Path path = pathWithAttributes == null ? null : pathWithAttributes.getPath(); + if (isTestMode()) { + LOGGER.info("Deleting {} (TEST MODE: file not actually deleted)", path); + } else { + delete(path); + } + } + } + + /** + * Deletes the specified file. + * + * @param path the file to delete + * @throws IOException if a problem occurred deleting the file + */ + protected void delete(final Path path) throws IOException { + LOGGER.trace("Deleting {}", path); + Files.deleteIfExists(path); + } + + /* + * (non-Javadoc) + * * @see org.apache.logging.log4j.core.appender.rolling.action.AbstractPathAction#execute(FileVisitor) */ @Override public boolean execute(final FileVisitor<Path> visitor) throws IOException { final List<PathWithAttributes> sortedPaths = getSortedPaths(); - LOGGER.trace("Sorted paths:"); - for (PathWithAttributes pathWithAttributes : sortedPaths) { - LOGGER.trace(pathWithAttributes); - } + trace("Sorted paths:", sortedPaths); for (PathWithAttributes element : sortedPaths) { try { @@ -91,6 +147,13 @@ public class DeleteAction extends AbstractPathAction { return true; // do not abort rollover even if processing failed } + private void trace(final String label, final List<PathWithAttributes> sortedPaths) { + LOGGER.trace(label); + for (final PathWithAttributes pathWithAttributes : sortedPaths) { + LOGGER.trace(pathWithAttributes); + } + } + /** * Returns a sorted list of all files up to maxDepth under the basePath. * @@ -145,10 +208,11 @@ public class DeleteAction extends AbstractPathAction { @PluginAttribute(value = "testMode", defaultBoolean = false) final boolean testMode, @PluginElement("PathSorter") final PathSorter sorterParameter, @PluginElement("PathConditions") final PathCondition[] pathConditions, + @PluginElement("ScriptCondition") final ScriptCondition scriptCondition, @PluginConfiguration final Configuration config) { // @formatter:on final PathSorter sorter = sorterParameter == null ? new PathSortByModificationTime(true) : sorterParameter; - return new DeleteAction(basePath, followLinks, maxDepth, testMode, sorter, pathConditions, + return new DeleteAction(basePath, followLinks, maxDepth, testMode, sorter, pathConditions, scriptCondition, config.getStrSubstitutor()); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1bc44b66/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ScriptCondition.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ScriptCondition.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ScriptCondition.java new file mode 100644 index 0000000..c1d4bea --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ScriptCondition.java @@ -0,0 +1,117 @@ +/* + * 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.nio.file.Path; +import java.util.List; +import java.util.Objects; + +import javax.script.SimpleBindings; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.script.AbstractScript; +import org.apache.logging.log4j.core.script.ScriptRef; +import org.apache.logging.log4j.status.StatusLogger; + +/** + * A condition of the {@link DeleteAction} where a user-provided script selects the files to delete from a provided + * list. The specified script may be a {@link Script}, a {@link ScriptFile} or a {@link ScriptRef}. + * + * @see #createCondition(AbstractScript, Configuration) + */ +@Plugin(name = "ScriptCondition", category = "Core", printObject = true) +public class ScriptCondition { + private static Logger LOGGER = StatusLogger.getLogger(); + + private final AbstractScript script; + private final Configuration configuration; + + /** + * Constructs a new ScriptCondition. + * + * @param script the script that can select files to delete + * @param configuration configuration containing the StrSubstitutor passed to the script + */ + public ScriptCondition(final AbstractScript script, final Configuration configuration) { + this.script = Objects.requireNonNull(script, "script"); + this.configuration = Objects.requireNonNull(configuration, "configuration"); + if (!(script instanceof ScriptRef)) { + configuration.getScriptManager().addScript(script); + } + } + + /** + * Executes the script + * + * @param baseDir + * @param candidates + * @return + */ + @SuppressWarnings("unchecked") + public List<PathWithAttributes> selectFilesToDelete(final Path basePath, final List<PathWithAttributes> candidates) { + final SimpleBindings bindings = new SimpleBindings(); + bindings.put("basePath", basePath); + bindings.put("pathList", candidates); + bindings.putAll(configuration.getProperties()); + bindings.put("substitutor", configuration.getStrSubstitutor()); + bindings.put("LOGGER", LOGGER); + final Object object = configuration.getScriptManager().execute(script.getName(), bindings); + return (List<PathWithAttributes>) object; + } + + /** + * Creates the ScriptCondition. + * + * @param script The script to run. This may be a {@link Script}, a {@link ScriptFile} or a {@link ScriptRef}. The + * script must return a {@code List<PathWithAttributes>}. When the script is executed, it is provided the + * following bindings: + * <ul> + * <li>basePath - the directory from where the {@link DeleteAction Delete} action started scanning for + * files to delete. Can be used to relativize the paths in the pathList.</li> + * <li>pathList - a {@code java.util.List} containing {@link PathWithAttribute} objects. (The script is + * free to modify and return this list.)</li> + * <li>substitutor - a {@link StrSubstitutor} that can be used to look up variables embedded in the base + * dir or other properties + * <li>LOGGER - the {@link StatusLogger} that can be used to log events during script execution + * <li>any properties declared in the configuration</li> + * </ul> + * @param configuration the configuration + * @return A ScriptCondition. + */ + @PluginFactory + public static ScriptCondition createCondition(@PluginElement("Script") final AbstractScript script, + @PluginConfiguration final Configuration configuration) { + + if (script == null) { + LOGGER.error("A Script, ScriptFile or ScriptRef element must be provided for this ScriptCondition"); + return null; + } + if (script instanceof ScriptRef) { + if (configuration.getScriptManager().getScript(script.getName()) == null) { + LOGGER.error("ScriptCondition: No script with name {} has been declared.", script.getName()); + return null; + } + } + return new ScriptCondition(script, configuration); + } +}
