1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache license, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the license for the specific language governing permissions and
15 * limitations under the license.
16 */
17 package org.apache.logging.log4j.core.appender.rolling.action;
18
19 import java.nio.file.FileSystem;
20 import java.nio.file.FileSystems;
21 import java.nio.file.Path;
22 import java.nio.file.PathMatcher;
23 import java.nio.file.attribute.BasicFileAttributes;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.regex.Pattern;
28
29 import org.apache.logging.log4j.Logger;
30 import org.apache.logging.log4j.core.Core;
31 import org.apache.logging.log4j.core.config.plugins.Plugin;
32 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
33 import org.apache.logging.log4j.core.config.plugins.PluginElement;
34 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
35 import org.apache.logging.log4j.status.StatusLogger;
36
37 /**
38 * PathCondition that accepts files for deletion if their relative path matches either a glob pattern or a regular
39 * expression. If both a regular expression and a glob pattern are specified the glob pattern is used and the regular
40 * expression is ignored.
41 * <p>
42 * The regular expression is a pattern as defined by the {@link Pattern} class. A glob is a simplified pattern
43 * expression described in {@link FileSystem#getPathMatcher(String)}.
44 */
45 @Plugin(name = "IfFileName", category = Core.CATEGORY_NAME, printObject = true)
46 public final class IfFileName implements PathCondition {
47 private static final Logger LOGGER = StatusLogger.getLogger();
48 private final PathMatcher pathMatcher;
49 private final String syntaxAndPattern;
50 private final PathCondition[] nestedConditions;
51
52 /**
53 * Constructs a FileNameFilter filter. If both a regular expression and a glob pattern are specified the glob
54 * pattern is used and the regular expression is ignored.
55 *
56 * @param glob the baseDir-relative path pattern of the files to delete (may contain '*' and '?' wildcarts)
57 * @param regex the regular expression that matches the baseDir-relative path of the file(s) to delete
58 * @param nestedConditions nested conditions to evaluate if this condition accepts a path
59 */
60 private IfFileName(final String glob, final String regex, final PathCondition[] nestedConditions) {
61 if (regex == null && glob == null) {
62 throw new IllegalArgumentException("Specify either a path glob or a regular expression. "
63 + "Both cannot be null.");
64 }
65 this.syntaxAndPattern = createSyntaxAndPatternString(glob, regex);
66 this.pathMatcher = FileSystems.getDefault().getPathMatcher(syntaxAndPattern);
67 this.nestedConditions = nestedConditions == null ? new PathCondition[0] : Arrays.copyOf(nestedConditions,
68 nestedConditions.length);
69 }
70
71 static String createSyntaxAndPatternString(final String glob, final String regex) {
72 if (glob != null) {
73 return glob.startsWith("glob:") ? glob : "glob:" + glob;
74 }
75 return regex.startsWith("regex:") ? regex : "regex:" + regex;
76 }
77
78 /**
79 * Returns the baseDir-relative path pattern of the files to delete. The returned string takes the form
80 * {@code syntax:pattern} where syntax is one of "glob" or "regex" and the pattern is either a {@linkplain Pattern
81 * regular expression} or a simplified pattern expression described under "glob" in
82 * {@link FileSystem#getPathMatcher(String)}.
83 *
84 * @return relative path of the file(s) to delete (may contain regular expression or wildcarts)
85 */
86 public String getSyntaxAndPattern() {
87 return syntaxAndPattern;
88 }
89
90 public List<PathCondition> getNestedConditions() {
91 return Collections.unmodifiableList(Arrays.asList(nestedConditions));
92 }
93
94 /*
95 * (non-Javadoc)
96 *
97 * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path,
98 * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes)
99 */
100 @Override
101 public boolean accept(final Path basePath, final Path relativePath, final BasicFileAttributes attrs) {
102 final boolean result = pathMatcher.matches(relativePath);
103
104 final String match = result ? "matches" : "does not match";
105 final String accept = result ? "ACCEPTED" : "REJECTED";
106 LOGGER.trace("IfFileName {}: '{}' {} relative path '{}'", accept, syntaxAndPattern, match, relativePath);
107 if (result) {
108 return IfAll.accept(nestedConditions, basePath, relativePath, attrs);
109 }
110 return result;
111 }
112
113 /*
114 * (non-Javadoc)
115 *
116 * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#beforeFileTreeWalk()
117 */
118 @Override
119 public void beforeFileTreeWalk() {
120 IfAll.beforeFileTreeWalk(nestedConditions);
121 }
122
123 /**
124 * Creates a IfFileName condition that returns true if either the specified
125 * {@linkplain FileSystem#getPathMatcher(String) glob pattern} or the regular expression matches the relative path.
126 * If both a regular expression and a glob pattern are specified the glob pattern is used and the regular expression
127 * is ignored.
128 *
129 * @param glob the baseDir-relative path pattern of the files to delete (may contain '*' and '?' wildcarts)
130 * @param regex the regular expression that matches the baseDir-relative path of the file(s) to delete
131 * @param nestedConditions nested conditions to evaluate if this condition accepts a path
132 * @return A IfFileName condition.
133 * @see FileSystem#getPathMatcher(String)
134 */
135 @PluginFactory
136 public static IfFileName createNameCondition(
137 // @formatter:off
138 @PluginAttribute("glob") final String glob,
139 @PluginAttribute("regex") final String regex,
140 @PluginElement("PathConditions") final PathCondition... nestedConditions) {
141 // @formatter:on
142 return new IfFileName(glob, regex, nestedConditions);
143 }
144
145 @Override
146 public String toString() {
147 final String nested = nestedConditions.length == 0 ? "" : " AND " + Arrays.toString(nestedConditions);
148 return "IfFileName(" + syntaxAndPattern + nested + ")";
149 }
150 }