LOG4J2-1136 - Add file watching for scripts
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/f2d06757 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/f2d06757 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/f2d06757 Branch: refs/heads/LOG4J2-1136 Commit: f2d0675758217df94a7e4c059615ae6bc9524ae1 Parents: 2be1f0f Author: Ralph Goers <[email protected]> Authored: Sat Oct 3 22:07:10 2015 -0700 Committer: Ralph Goers <[email protected]> Committed: Sat Oct 3 23:15:31 2015 -0700 ---------------------------------------------------------------------- .../core/config/AbstractConfiguration.java | 30 +++++- .../core/config/builder/api/package-info.java | 20 ++++ .../config/builder/impl/BuiltConfiguration.java | 8 +- .../core/config/builder/impl/package-info.java | 20 ++++ .../core/config/json/JsonConfiguration.java | 7 +- .../core/config/properties/package-info.java | 20 ++++ .../log4j/core/config/xml/XmlConfiguration.java | 7 +- .../logging/log4j/core/script/ScriptFile.java | 70 +++++++++++--- .../log4j/core/script/ScriptManager.java | 62 ++++++++++++- .../logging/log4j/core/script/package-info.java | 20 ++++ .../core/util/ExtensionLanguageMapping.java | 71 ++++++++++++++ .../logging/log4j/core/util/FileUtils.java | 12 +++ .../logging/log4j/core/util/FileWatcher.java | 27 ++++++ .../logging/log4j/core/util/WatchManager.java | 98 ++++++++++++++++++++ .../log4j/core/util/WatchManagerTest.java | 87 +++++++++++++++++ .../test/resources/log4j-scriptFile-filters.xml | 4 +- 16 files changed, 538 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java index 7d6535e..5481e55 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java @@ -32,6 +32,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -59,6 +61,7 @@ import org.apache.logging.log4j.core.selector.ContextSelector; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.NameUtil; +import org.apache.logging.log4j.core.util.WatchManager; import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.util.PropertiesUtil; @@ -118,6 +121,8 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<>(); private final ConfigurationSource configurationSource; private ScriptManager scriptManager; + private ScheduledExecutorService executorService; + private final WatchManager watchManager = new WatchManager(); /** * Constructor. @@ -128,6 +133,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement pluginManager = new PluginManager(Node.CATEGORY); rootNode = new Node(); setState(State.INITIALIZING); + } @Override @@ -150,13 +156,25 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement return scriptManager; } + public WatchManager getWatchManager() { + return watchManager; + } + + public ScheduledExecutorService getExecutorService() { + return executorService; + } + /** * Initialize the configuration. */ @Override public void initialize() { LOGGER.debug("Initializing configuration {}", this); - scriptManager = new ScriptManager(); + if (watchManager.getIntervalSeconds() > 0) { + executorService = new ScheduledThreadPoolExecutor(1); + watchManager.setExecutorService(executorService); + } + scriptManager = new ScriptManager(watchManager); pluginManager.collectPlugins(pluginPackages); final PluginManager levelPlugins = new PluginManager(Level.CATEGORY); levelPlugins.collectPlugins(pluginPackages); @@ -190,6 +208,9 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement } LOGGER.debug("Starting configuration {}", this); this.setStarting(); + if (watchManager.getIntervalSeconds() > 0) { + watchManager.start(); + } final Set<LoggerConfig> alreadyStarted = new HashSet<>(); for (final LoggerConfig logger : loggers.values()) { logger.start(); @@ -297,6 +318,13 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement } LOGGER.trace("AbstractConfiguration stopped {} LoggerConfigs.", loggerCount); + if (watchManager.isStarted()) { + watchManager.stop(); + } + if (executorService != null) { + executorService.shutdown(); + } + // AsyncLoggerConfigHelper decreases its ref count when an AsyncLoggerConfig is stopped. // Stopping the same AsyncLoggerConfig twice results in an incorrect ref count and // the shared Disruptor may be shut down prematurely, resulting in NPE or other errors. http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java new file mode 100644 index 0000000..64dd1b5 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * Configuration Builder API. + */ +package org.apache.logging.log4j.core.config.builder.api; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java index 06135e8..7db9721 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java @@ -141,8 +141,12 @@ public class BuiltConfiguration extends AbstractConfiguration { final ConfigurationSource configSource = getConfigurationSource(); if (configSource != null) { final File configFile = configSource.getFile(); - if (intervalSeconds > 0 && configFile != null) { - monitor = new FileConfigurationMonitor((Reconfigurable)this, configFile, listeners, intervalSeconds); + if (intervalSeconds > 0) { + getWatchManager().setIntervalSeconds(intervalSeconds); + if (configFile != null) { + monitor = new FileConfigurationMonitor((Reconfigurable)this, configFile, listeners, + intervalSeconds); + } } } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java new file mode 100644 index 0000000..985e84a --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * Configuration Builder Implementation. + */ +package org.apache.logging.log4j.core.config.builder.impl; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java index 1fc3e50..bc08922 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java @@ -87,8 +87,11 @@ public class JsonConfiguration extends AbstractConfiguration implements Reconfig setName(value); } else if ("monitorInterval".equalsIgnoreCase(key)) { final int intervalSeconds = Integer.parseInt(value); - if (intervalSeconds > 0 && configFile != null) { - monitor = new FileConfigurationMonitor(this, configFile, listeners, intervalSeconds); + if (intervalSeconds > 0) { + getWatchManager().setIntervalSeconds(intervalSeconds); + if (configFile != null) { + monitor = new FileConfigurationMonitor(this, configFile, listeners, intervalSeconds); + } } } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "application/json"); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java new file mode 100644 index 0000000..727a7cb --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * Configuration using Properties files. + */ +package org.apache.logging.log4j.core.config.properties; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java index af13000..8008e62 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java @@ -132,8 +132,11 @@ public class XmlConfiguration extends AbstractConfiguration implements Reconfigu schemaResource = value; } else if ("monitorInterval".equalsIgnoreCase(key)) { final int intervalSeconds = Integer.parseInt(value); - if (intervalSeconds > 0 && configFile != null) { - monitor = new FileConfigurationMonitor(this, configFile, listeners, intervalSeconds); + if (intervalSeconds > 0) { + getWatchManager().setIntervalSeconds(intervalSeconds); + if (configFile != null) { + monitor = new FileConfigurationMonitor(this, configFile, listeners, intervalSeconds); + } } } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "text/xml"); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptFile.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptFile.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptFile.java index 6ff6e27..f1bf300 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptFile.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptFile.java @@ -1,3 +1,19 @@ +/* + * 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.script; import java.io.File; @@ -7,12 +23,15 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Paths; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Node; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.util.ExtensionLanguageMapping; import org.apache.logging.log4j.core.util.FileUtils; import org.apache.logging.log4j.core.util.IOUtils; import org.apache.logging.log4j.core.util.NetUtils; @@ -25,40 +44,67 @@ import org.apache.logging.log4j.status.StatusLogger; public class ScriptFile extends AbstractScript { private static final Logger logger = StatusLogger.getLogger(); + private final Path filePath; + private final boolean isWatched; + + + public ScriptFile(Path filePath, String language, boolean isWatched, String scriptText) { + super(filePath.toString(), language, scriptText); + this.filePath = filePath; + this.isWatched = isWatched; + } + + public Path getPath() { + return this.filePath; + } - public ScriptFile(String name, String language, String scriptText) { - super(name, language, scriptText); + public boolean isWatched() { + return isWatched; } @PluginFactory public static ScriptFile createScript( // @formatter:off - @PluginAttribute("name") final String name, @PluginAttribute("language") String language, @PluginAttribute("path") final String filePathOrUri, + @PluginAttribute("isWatched") final Boolean isWatched, @PluginAttribute("charset") final Charset charset) { // @formatter:on - if (language == null) { - logger.info("No script language supplied, defaulting to {}", DEFAULT_LANGUAGE); - language = DEFAULT_LANGUAGE; - } if (filePathOrUri == null) { - logger.error("No script path provided for ScriptFile {}", name); + logger.error("No script path provided for ScriptFile"); return null; } - final Charset actualCharset = charset == null ? Charset.defaultCharset() : charset; final URI uri = NetUtils.toURI(filePathOrUri); final File file = FileUtils.fileFromUri(uri); + if (language == null && file != null) { + String fileExtension = FileUtils.getFileExtension(file); + if (fileExtension != null) { + ExtensionLanguageMapping mapping = ExtensionLanguageMapping.getByExtension(fileExtension); + if (mapping != null) { + language = mapping.getLanguage(); + } + } + } + if (language == null) { + logger.info("No script language supplied, defaulting to {}", DEFAULT_LANGUAGE); + language = DEFAULT_LANGUAGE; + } + + final Charset actualCharset = charset == null ? Charset.defaultCharset() : charset; String scriptText; try (final Reader reader = new InputStreamReader( file != null ? new FileInputStream(file) : uri.toURL().openStream(), actualCharset)) { scriptText = IOUtils.toString(reader); } catch (IOException e) { - logger.error("{}: name={}, language={}, path={}, actualCharset={}", e.getClass().getSimpleName(), name, + logger.error("{}: language={}, path={}, actualCharset={}", e.getClass().getSimpleName(), language, filePathOrUri, actualCharset); return null; } - return new ScriptFile(name, language, scriptText); - + Path path = file != null ? Paths.get(file.toURI()) : Paths.get(uri); + if (path == null) { + logger.error("Unable to convert {} to a Path", uri.toString()); + return null; + } + return new ScriptFile(path, language, isWatched == null ? Boolean.FALSE : isWatched, scriptText); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptManager.java index f189f9e..b7b701a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/ScriptManager.java @@ -17,6 +17,8 @@ package org.apache.logging.log4j.core.script; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.util.FileWatcher; +import org.apache.logging.log4j.core.util.WatchManager; import org.apache.logging.log4j.status.StatusLogger; import javax.script.Bindings; @@ -26,6 +28,8 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import java.io.File; +import java.nio.file.Path; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -33,15 +37,18 @@ import java.util.concurrent.ConcurrentMap; /** * Manages the scripts use by the Configuration. */ -public class ScriptManager { +public class ScriptManager implements FileWatcher { private static final String KEY_THREADING = "THREADING"; private static final Logger logger = StatusLogger.getLogger(); + private static final long serialVersionUID = -2534169384971965196L; private final ScriptEngineManager manager = new ScriptEngineManager(); private final ConcurrentMap<String, ScriptRunner> scripts = new ConcurrentHashMap<>(); private final String languages; + private final WatchManager watchManager; - public ScriptManager() { + public ScriptManager(WatchManager watchManager) { + this.watchManager = watchManager; final List<ScriptEngineFactory> factories = manager.getEngineFactories(); if (logger.isDebugEnabled()) { final StringBuilder sb = new StringBuilder(); @@ -86,7 +93,7 @@ public class ScriptManager { final ScriptEngine engine = manager.getEngineByName(script.getLanguage()); if (engine == null) { logger.error("No ScriptEngine found for language " + script.getLanguage() + ". Available languages are: " + - languages); + languages); return; } if (engine.getFactory().getParameter(KEY_THREADING) == null) { @@ -94,6 +101,31 @@ public class ScriptManager { } else { scripts.put(script.getName(), new MainScriptRunner(engine, script)); } + + if (script instanceof ScriptFile) { + ScriptFile scriptFile = (ScriptFile)script; + Path path = scriptFile.getPath(); + if (scriptFile.isWatched() && path != null) { + watchManager.watchFile(path.toFile(), this); + } + } + } + + @Override + public void fileModified(final File file) { + ScriptRunner runner = scripts.get(file.toString()); + if (runner == null) { + logger.info("{} is not a running script"); + return; + } + ScriptEngine engine = runner.getScriptEngine(); + AbstractScript script = runner.getScript(); + if (engine.getFactory().getParameter(KEY_THREADING) == null) { + scripts.put(script.getName(), new ThreadLocalScriptRunner(script)); + } else { + scripts.put(script.getName(), new MainScriptRunner(engine, script)); + } + } public Object execute(final String name, final Bindings bindings) { @@ -108,6 +140,10 @@ public class ScriptManager { private interface ScriptRunner { public Object execute(Bindings bindings); + + public AbstractScript getScript(); + + public ScriptEngine getScriptEngine(); } private class MainScriptRunner implements ScriptRunner { @@ -135,6 +171,11 @@ public class ScriptManager { } @Override + public ScriptEngine getScriptEngine() { + return this.scriptEngine; + } + + @Override public Object execute(final Bindings bindings) { if (compiledScript != null) { try { @@ -152,6 +193,10 @@ public class ScriptManager { } } + @Override + public AbstractScript getScript() { + return script; + } } private class ThreadLocalScriptRunner implements ScriptRunner { @@ -172,6 +217,15 @@ public class ScriptManager { public Object execute(final Bindings bindings) { return runners.get().execute(bindings); } - } + @Override + public AbstractScript getScript() { + return script; + } + + @Override + public ScriptEngine getScriptEngine() { + return runners.get().getScriptEngine(); + } + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/script/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/script/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/package-info.java new file mode 100644 index 0000000..4964b30 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/script/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * Log4j 2 Script support. + */ +package org.apache.logging.log4j.core.script; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ExtensionLanguageMapping.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ExtensionLanguageMapping.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ExtensionLanguageMapping.java new file mode 100644 index 0000000..90a0d61 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ExtensionLanguageMapping.java @@ -0,0 +1,71 @@ +/* + * 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.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public enum ExtensionLanguageMapping { + JS("js", "JavaScript"), JAVASCRIPT("javascript", "JavaScript"), GVY("gvy", "Groovy"), + GROOVY("groovy", "Groovy"), BSH("bsh", "beanshell"), BEANSHELL("beanshell", "beanshell"), + JY("jy", "jython"), JYTHON("jython", "jython"), FTL("ftl", "freemarker"), + FREEMARKER("freemarker", "freemarker"), VM("vm", "velocity"), VELOCITY("velocity", "velocity"), + AWK("awk", "awk"), EJS("ejs", "ejs"), TCL("tcl", "tcl"), HS("hs", "jaskell"), JELLY("jelly", "jelly"), + JEP("jep", "jep"), JEXL("jexl", "jexl"), JEXL2("jexl2", "jexl2"), + RB("rb", "ruby"), RUBY("ruby", "ruby"), JUDO("judo", "judo"), JUDI("judi", "judo"), SCALA("scala", "scala"), + CLJ("clj", "Clojure"); + + + private final String extension; + private final String language; + + private ExtensionLanguageMapping(String extension, String language) { + this.extension = extension; + this.language = language; + } + + public String getExtension() { + return this.extension; + } + + public String getLanguage() { + return this.language; + } + + public static ExtensionLanguageMapping getByExtension(String extension) { + for (ExtensionLanguageMapping mapping : values()) { + if (mapping.extension.equals(extension)) { + return mapping; + } + } + return null; + } + + public static List<ExtensionLanguageMapping> getByLanguage(String language) { + List<ExtensionLanguageMapping> list = new ArrayList<>(); + for (ExtensionLanguageMapping mapping : values()) { + if (mapping.language.equals(language)) { + list.add(mapping); + } + } + return list; + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileUtils.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileUtils.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileUtils.java index c14954a..d8c77fe 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileUtils.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileUtils.java @@ -24,6 +24,9 @@ import java.net.URI; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; @@ -93,6 +96,15 @@ public final class FileUtils { return url != null && (url.getProtocol().equals(PROTOCOL_FILE) || url.getProtocol().equals(JBOSS_FILE)); } + public static String getFileExtension(File file) { + String fileName = file.getName(); + if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) { + return fileName.substring(fileName.lastIndexOf(".") + 1); + } else { + return null; + } + } + /** * Asserts that the given directory exists and creates it if necessary. * http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileWatcher.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileWatcher.java new file mode 100644 index 0000000..5c1679b --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/FileWatcher.java @@ -0,0 +1,27 @@ +/* + * 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.util; + +import java.io.File; + +/** + * Watches for changes in a Path and performs an action when the file is modified. + */ +public interface FileWatcher<T> { + + void fileModified(File file); +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java new file mode 100644 index 0000000..6b6880d --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java @@ -0,0 +1,98 @@ +/* + * 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.util; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.AbstractLifeCycle; +import org.apache.logging.log4j.status.StatusLogger; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Manages FileWatchers. + */ +public class WatchManager extends AbstractLifeCycle { + + private static final long serialVersionUID = 8998356999926962686L; + private static Logger logger = StatusLogger.getLogger(); + private final ConcurrentMap<File, FileMonitor> watchers = new ConcurrentHashMap<>(); + private ScheduledExecutorService executorService; + private int intervalSeconds = 0; + private ScheduledFuture<?> future; + + public void setExecutorService(ScheduledExecutorService executorService) { + this.executorService = executorService; + } + + public void setIntervalSeconds(int intervalSeconds) { + this.intervalSeconds = intervalSeconds; + } + + public int getIntervalSeconds() { + return this.intervalSeconds; + } + + public void start() { + super.start(); + if (intervalSeconds > 0) { + future = executorService.scheduleWithFixedDelay(new WatchWorker(), intervalSeconds, intervalSeconds, + TimeUnit.SECONDS); + } + } + + public void stop() { + future.cancel(true); + super.stop(); + } + + public void watchFile(File file, FileWatcher watcher) { + watchers.put(file, new FileMonitor(file.lastModified(), watcher)); + + } + + private class WatchWorker implements Runnable { + + public void run() { + for (Map.Entry<File, FileMonitor> entry : watchers.entrySet()) { + File file = entry.getKey(); + FileMonitor fileMonitor = entry.getValue(); + long lastModfied = file.lastModified(); + if (lastModfied > fileMonitor.lastModified) { + logger.info("File {} was modified", file.toString()); + fileMonitor.lastModified = lastModfied; + fileMonitor.fileWatcher.fileModified(file); + } + } + } + } + + private class FileMonitor { + private final FileWatcher fileWatcher; + private long lastModified; + + public FileMonitor(long lastModified, FileWatcher fileWatcher) { + this.fileWatcher = fileWatcher; + this.lastModified = lastModified; + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java new file mode 100644 index 0000000..900494f --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java @@ -0,0 +1,87 @@ +/* + * 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.util; + +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; + +/** + * Test the WatchManager + */ +public class WatchManagerTest { + + private String testFile = "target/testWatchFile"; + private String originalFile = "target/test-classes/log4j-test1.xml"; + private String newFile = "target/test-classes/log4j-test1.yaml"; + + @Test + public void testWatchManager() throws Exception { + ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1); + WatchManager watchManager = new WatchManager(); + watchManager.setIntervalSeconds(1); + watchManager.setExecutorService(executorService); + watchManager.start(); + try { + File sourceFile = new File(originalFile); + FileOutputStream targetStream = new FileOutputStream(testFile); + File updateFile = new File(newFile); + Path source = Paths.get(sourceFile.toURI()); + Files.copy(source, targetStream); + File targetFile = new File(testFile); + BlockingQueue<File> queue = new LinkedBlockingQueue<>(); + watchManager.watchFile(targetFile, new TestWatcher(queue)); + Thread.sleep(1000); + source = Paths.get(updateFile.toURI()); + Files.copy(source, Paths.get(targetFile.toURI()), StandardCopyOption.REPLACE_EXISTING); + Thread.sleep(1000); + File f = queue.poll(1, TimeUnit.SECONDS); + assertNotNull("File change not detected", f); + } finally { + watchManager.stop(); + executorService.shutdown(); + } + } + + private class TestWatcher implements FileWatcher { + + private final Queue<File> queue; + + public TestWatcher(Queue<File> queue) { + this.queue = queue; + } + + @Override + public void fileModified(File file) { + System.out.println(file.toString() + " was modified"); + queue.add(file); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f2d06757/log4j-core/src/test/resources/log4j-scriptFile-filters.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-scriptFile-filters.xml b/log4j-core/src/test/resources/log4j-scriptFile-filters.xml index 0fef7b5..694da53 100644 --- a/log4j-core/src/test/resources/log4j-scriptFile-filters.xml +++ b/log4j-core/src/test/resources/log4j-scriptFile-filters.xml @@ -25,14 +25,14 @@ <Logger name="TestJavaScriptFilter" level="trace" additivity="false"> <AppenderRef ref="List"> <ScriptFilter onMatch="ACCEPT" onMisMatch="DENY"> - <ScriptFile name="JavascriptFilter" language="JavaScript" path="src/test/resources/scripts/filter.js" charset="UTF-8" /> + <ScriptFile language="JavaScript" path="src/test/resources/scripts/filter.js" charset="UTF-8" /> </ScriptFilter> </AppenderRef> </Logger> <Logger name="TestGroovyFilter" level="trace" additivity="false"> <AppenderRef ref="List"> <ScriptFilter onMatch="ACCEPT" onMisMatch="DENY"> - <ScriptFile name="GroovyFilter" language="groovy" path="src/test/resources/scripts/filter.groovy" charset="UTF-8" /> + <ScriptFile path="src/test/resources/scripts/filter.groovy" charset="UTF-8" /> </ScriptFilter> </AppenderRef> </Logger>
