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);
+    }
+}

Reply via email to