This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch 2.24.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/2.24.x by this push:
     new 421d8a797f Revert "Fix reloading of the configuration from HTTP(S)"
421d8a797f is described below

commit 421d8a797f48924e6eeaa88ce0d26bc3e9c583df
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Sun Sep 22 21:11:27 2024 +0200

    Revert "Fix reloading of the configuration from HTTP(S)"
    
    This reverts commit be24f928f837d6823cbb919a63d3fa5501fbe381.
---
 log4j-appserver/pom.xml                            |  10 -
 .../log4j/core/config/ConfigurationSourceTest.java |  62 +++--
 .../filter/HttpThreadContextMapFilterTest.java     | 202 +++++++++++++++++
 .../filter/MutableThreadContextMapFilterTest.java  | 250 +++++----------------
 .../log4j/core/net/UrlConnectionFactoryTest.java   | 213 ++++++++++--------
 .../logging/log4j/core/net/WireMockUtil.java       |  84 -------
 .../logging/log4j/core/util/HttpWatcherTest.java   | 160 -------------
 .../logging/log4j/core/util/WatchHttpTest.java     | 156 +++++++++++++
 .../logging/log4j/core/util/WatchManagerTest.java  | 177 +++++++--------
 .../src/test/resources/emptyConfig.json            |   4 +
 .../filter/MutableThreadContextMapFilterTest.xml   |  35 ---
 .../src/test/resources/filterConfig.json           |   6 +
 ...tionSourceTest.xml => log4j2-mutableFilter.xml} |  19 +-
 .../apache/logging/log4j/core/LoggerContext.java   |  19 +-
 .../log4j/core/config/AbstractConfiguration.java   |  17 +-
 .../log4j/core/config/ConfigurationSource.java     |  68 +++---
 .../logging/log4j/core/config/HttpWatcher.java     |  39 +---
 .../log4j/core/config/xml/XmlConfiguration.java    |  12 +-
 .../core/filter/MutableThreadContextMapFilter.java |  50 ++---
 .../logging/log4j/core/util/WatchManager.java      |  23 +-
 .../core/util/internal/HttpInputStreamUtil.java    |  96 ++------
 log4j-parent/pom.xml                               |  16 ++
 src/changelog/.2.x.x/2937-http-watcher.xml         |   8 -
 23 files changed, 810 insertions(+), 916 deletions(-)

diff --git a/log4j-appserver/pom.xml b/log4j-appserver/pom.xml
index 0b9c10b0dd..df0ce0067d 100644
--- a/log4j-appserver/pom.xml
+++ b/log4j-appserver/pom.xml
@@ -52,32 +52,22 @@
       org.apache.tomcat.juli;transitive=false,
       org.eclipse.jetty.util;transitive=false
     </bnd-extra-module-options>
-
-    <!-- Dependencies unique for this module -->
-    <jetty.version>9.4.56.v20240826</jetty.version>
-    <tomcat-juli.version>10.0.27</tomcat-juli.version>
   </properties>
 
   <dependencies>
-
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-util</artifactId>
-      <version>${jetty.version}</version>
       <scope>provided</scope>
     </dependency>
-
     <dependency>
       <groupId>org.apache.tomcat</groupId>
       <artifactId>tomcat-juli</artifactId>
-      <version>${tomcat-juli.version}</version>
       <scope>provided</scope>
     </dependency>
-
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
     </dependency>
-
   </dependencies>
 </project>
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationSourceTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationSourceTest.java
index 5001ad2d44..f17b3b2a55 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationSourceTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationSourceTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.logging.log4j.core.config;
 
-import static java.util.Objects.requireNonNull;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -33,28 +32,21 @@ import java.lang.management.OperatingSystemMXBean;
 import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
-import org.apache.commons.io.IOUtils;
 import org.apache.logging.log4j.core.net.UrlConnectionFactory;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
 
 public class ConfigurationSourceTest {
-    /**
-     * The path inside the jar created by {@link #prepareJarConfigURL} 
containing the configuration.
-     */
-    public static final String PATH_IN_JAR = "/config/console.xml";
-
-    private static final String CONFIG_FILE = 
"/config/ConfigurationSourceTest.xml";
-
-    @TempDir
-    private Path tempDir;
+    private static final Path JAR_FILE = Paths.get("target", "test-classes", 
"jarfile.jar");
+    private static final Path CONFIG_FILE = Paths.get("target", 
"test-classes", "log4j2-console.xml");
+    private static final byte[] buffer = new byte[1024];
 
     @Test
-    void testJira_LOG4J2_2770_byteArray() throws Exception {
+    public void testJira_LOG4J2_2770_byteArray() throws Exception {
         final ConfigurationSource configurationSource =
                 new ConfigurationSource(new ByteArrayInputStream(new byte[] 
{'a', 'b'}));
         assertNotNull(configurationSource.resetInputStream());
@@ -62,19 +54,20 @@ public class ConfigurationSourceTest {
 
     /**
      * Checks if the usage of 'jar:' URLs does not increase the file descriptor
-     * count, and the jar file can be deleted.
+     * count and the jar file can be deleted.
+     *
+     * @throws Exception
      */
     @Test
-    void testNoJarFileLeak() throws Exception {
-        final Path jarFile = prepareJarConfigURL(tempDir);
-        final URL jarConfigURL = new URL("jar:" + jarFile.toUri().toURL() + 
"!" + PATH_IN_JAR);
+    public void testNoJarFileLeak() throws Exception {
+        final URL jarConfigURL = prepareJarConfigURL();
         final long expected = getOpenFileDescriptorCount();
         
UrlConnectionFactory.createConnection(jarConfigURL).getInputStream().close();
         // This can only fail on UNIX
         assertEquals(expected, getOpenFileDescriptorCount());
         // This can only fail on Windows
         try {
-            Files.delete(jarFile);
+            Files.delete(JAR_FILE);
         } catch (IOException e) {
             fail(e);
         }
@@ -82,8 +75,7 @@ public class ConfigurationSourceTest {
 
     @Test
     public void testLoadConfigurationSourceFromJarFile() throws Exception {
-        final Path jarFile = prepareJarConfigURL(tempDir);
-        final URL jarConfigURL = new URL("jar:" + jarFile.toUri().toURL() + 
"!" + PATH_IN_JAR);
+        final URL jarConfigURL = prepareJarConfigURL();
         final long expectedFdCount = getOpenFileDescriptorCount();
         ConfigurationSource configSource = 
ConfigurationSource.fromUri(jarConfigURL.toURI());
         assertNotNull(configSource);
@@ -98,7 +90,7 @@ public class ConfigurationSourceTest {
         assertEquals(expectedFdCount, getOpenFileDescriptorCount());
         // This can only fail on Windows
         try {
-            Files.delete(jarFile);
+            Files.delete(JAR_FILE);
         } catch (IOException e) {
             fail(e);
         }
@@ -112,18 +104,22 @@ public class ConfigurationSourceTest {
         return 0L;
     }
 
-    public static Path prepareJarConfigURL(Path dir) throws IOException {
-        Path jarFile = dir.resolve("jarFile.jar");
-        final Manifest manifest = new Manifest();
-        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
-        try (final OutputStream os = Files.newOutputStream(jarFile);
-                final JarOutputStream jar = new JarOutputStream(os, manifest);
-                final InputStream config =
-                        
requireNonNull(ConfigurationSourceTest.class.getResourceAsStream(CONFIG_FILE))) 
{
-            final JarEntry jarEntry = new JarEntry("config/console.xml");
-            jar.putNextEntry(jarEntry);
-            IOUtils.copy(config, os);
+    public static URL prepareJarConfigURL() throws IOException {
+        if (!Files.exists(JAR_FILE)) {
+            final Manifest manifest = new Manifest();
+            manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
+            try (final OutputStream os = Files.newOutputStream(JAR_FILE);
+                    final JarOutputStream jar = new JarOutputStream(os, 
manifest);
+                    final InputStream config = 
Files.newInputStream(CONFIG_FILE)) {
+                final JarEntry jarEntry = new JarEntry("config/console.xml");
+                jar.putNextEntry(jarEntry);
+                int len;
+                while ((len = config.read(buffer)) != -1) {
+                    jar.write(buffer, 0, len);
+                }
+                jar.closeEntry();
+            }
         }
-        return jarFile;
+        return new URL("jar:" + JAR_FILE.toUri().toURL() + 
"!/config/console.xml");
     }
 }
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/HttpThreadContextMapFilterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/HttpThreadContextMapFilterTest.java
new file mode 100644
index 0000000000..6782948a43
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/HttpThreadContextMapFilterTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.filter;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Base64;
+import java.util.Enumeration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Assert;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junitpioneer.jupiter.RetryingTest;
+
+/**
+ * Unit test for simple App.
+ */
+public class HttpThreadContextMapFilterTest implements 
MutableThreadContextMapFilter.FilterConfigUpdateListener {
+
+    private static final String BASIC = "Basic ";
+    private static final String expectedCreds = "log4j:log4j";
+    private static Server server;
+    private static final Base64.Decoder decoder = Base64.getDecoder();
+    private static int port;
+    static final String CONFIG = "log4j2-mutableFilter.xml";
+    static LoggerContext loggerContext = null;
+    static final File targetFile = new 
File("target/test-classes/testConfig.json");
+    static final Path target = targetFile.toPath();
+    CountDownLatch updated = new CountDownLatch(1);
+
+    @BeforeAll
+    public static void startServer() throws Exception {
+        try {
+            server = new Server(0);
+            final ServletContextHandler context = new ServletContextHandler();
+            final ServletHolder defaultServ = new ServletHolder("default", 
TestServlet.class);
+            defaultServ.setInitParameter("resourceBase", 
System.getProperty("user.dir"));
+            defaultServ.setInitParameter("dirAllowed", "true");
+            context.addServlet(defaultServ, "/");
+            server.setHandler(context);
+
+            // Start Server
+            server.start();
+            port = ((ServerConnector) 
server.getConnectors()[0]).getLocalPort();
+            try {
+                Files.deleteIfExists(target);
+            } catch (IOException ioe) {
+                // Ignore this.
+            }
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            throw ex;
+        }
+    }
+
+    @AfterAll
+    public static void stopServer() throws Exception {
+        if (server != null) {
+            server.stop();
+        }
+    }
+
+    @AfterEach
+    public void after() {
+        try {
+            Files.deleteIfExists(target);
+        } catch (IOException ioe) {
+            // Ignore this.
+        }
+        if (loggerContext != null) {
+            loggerContext.stop();
+            loggerContext = null;
+        }
+    }
+
+    @RetryingTest(maxAttempts = 5, suspendForMs = 10)
+    public void filterTest() throws Exception {
+        System.setProperty("log4j2.Configuration.allowedProtocols", "http");
+        System.setProperty("logging.auth.username", "log4j");
+        System.setProperty("logging.auth.password", "log4j");
+        System.setProperty("configLocation", "http://localhost:"; + port + 
"/testConfig.json");
+        ThreadContext.put("loginId", "rgoers");
+        Path source = new 
File("target/test-classes/emptyConfig.json").toPath();
+        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+        final long fileTime = targetFile.lastModified() - 2000;
+        assertTrue(targetFile.setLastModified(fileTime));
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        final Appender app = 
loggerContext.getConfiguration().getAppender("List");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+        final MutableThreadContextMapFilter filter =
+                (MutableThreadContextMapFilter) 
loggerContext.getConfiguration().getFilter();
+        assertNotNull(filter);
+        filter.registerListener(this);
+        final Logger logger = loggerContext.getLogger("Test");
+        logger.debug("This is a test");
+        Assertions.assertEquals(0, ((ListAppender) app).getEvents().size());
+        source = new File("target/test-classes/filterConfig.json").toPath();
+        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+        assertNotEquals(fileTime, targetFile.lastModified());
+        if (!updated.await(5, TimeUnit.SECONDS)) {
+            fail("File update was not detected");
+        }
+        updated = new CountDownLatch(1);
+        logger.debug("This is a test");
+        Assertions.assertEquals(1, ((ListAppender) app).getEvents().size());
+        Assertions.assertTrue(Files.deleteIfExists(target));
+        if (!updated.await(5, TimeUnit.SECONDS)) {
+            fail("File update for delete was not detected");
+        }
+    }
+
+    public static class TestServlet extends DefaultServlet {
+
+        private static final long serialVersionUID = -2885158530511450659L;
+
+        @Override
+        protected void doGet(final HttpServletRequest request, final 
HttpServletResponse response)
+                throws ServletException, IOException {
+            final Enumeration<String> headers = 
request.getHeaders(HttpHeader.AUTHORIZATION.toString());
+            if (headers == null) {
+                response.sendError(401, "No Auth header");
+                return;
+            }
+            while (headers.hasMoreElements()) {
+                final String authData = headers.nextElement();
+                Assert.assertTrue("Not a Basic auth header", 
authData.startsWith(BASIC));
+                final String credentials = new 
String(decoder.decode(authData.substring(BASIC.length())));
+                if (!expectedCreds.equals(credentials)) {
+                    response.sendError(401, "Invalid credentials");
+                    return;
+                }
+            }
+            if (request.getServletPath().equals("/testConfig.json")) {
+                final File file = new 
File("target/test-classes/testConfig.json");
+                if (!file.exists()) {
+                    response.sendError(404, "File not found");
+                    return;
+                }
+                final long modifiedSince = 
request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.toString());
+                final long lastModified = (file.lastModified() / 1000) * 1000;
+                if (modifiedSince > 0 && lastModified <= modifiedSince) {
+                    response.setStatus(304);
+                    return;
+                }
+                response.setDateHeader(HttpHeader.LAST_MODIFIED.toString(), 
lastModified);
+                response.setContentLengthLong(file.length());
+                Files.copy(file.toPath(), response.getOutputStream());
+                response.getOutputStream().flush();
+                response.setStatus(200);
+            } else {
+                response.sendError(400, "Unsupported request");
+            }
+        }
+    }
+
+    @Override
+    public void onEvent() {
+        updated.countDown();
+    }
+}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilterTest.java
index a2d3919d1d..598ce46ac2 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilterTest.java
@@ -16,228 +16,94 @@
  */
 package org.apache.logging.log4j.core.filter;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.logging.log4j.core.net.WireMockUtil.createMapping;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
-import com.github.tomakehurst.wiremock.client.BasicCredentials;
-import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
-import com.github.tomakehurst.wiremock.junit5.WireMockTest;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
+import java.io.File;
+import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.temporal.ChronoUnit;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.config.Configurator;
 import org.apache.logging.log4j.core.test.appender.ListAppender;
-import org.apache.logging.log4j.test.TestProperties;
-import org.apache.logging.log4j.test.junit.SetTestProperty;
-import org.apache.logging.log4j.test.junit.UsingTestProperties;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.api.Assertions;
+import org.junitpioneer.jupiter.RetryingTest;
 
 /**
  * Unit test for simple App.
  */
-@SetTestProperty(key = "log4j2.configurationAllowedProtocols", value = 
"http,https")
-@SetTestProperty(key = "log4j2.configurationPassword", value = "log4j")
-@SetTestProperty(key = "log4j2.configurationUsername", value = "log4j")
-@UsingTestProperties
-@WireMockTest
-class MutableThreadContextMapFilterTest implements 
MutableThreadContextMapFilter.FilterConfigUpdateListener {
+public class MutableThreadContextMapFilterTest implements 
MutableThreadContextMapFilter.FilterConfigUpdateListener {
 
-    private static final BasicCredentials CREDENTIALS = new 
BasicCredentials("log4j", "log4j");
-    private static final String FILE_NAME = "testConfig.json";
-    private static final String URL_PATH = "/" + FILE_NAME;
-    private static final String JSON = "application/json";
-
-    private static final byte[] EMPTY_CONFIG = ("{" //
-                    + "  \"configs\":{}" //
-                    + "}")
-            .getBytes(UTF_8);
-    private static final byte[] FILTER_CONFIG = ("{" //
-                    + "  \"configs\": {" //
-                    + "    \"loginId\": [\"rgoers\", \"adam\"]," //
-                    + "    \"corpAcctNumber\": [\"30510263\"]" //
-                    + "  }" //
-                    + "}")
-            .getBytes(UTF_8);
-
-    private static final String CONFIG = 
"filter/MutableThreadContextMapFilterTest.xml";
-    private static LoggerContext loggerContext = null;
-    private final ReentrantLock lock = new ReentrantLock();
-    private final Condition filterUpdated = lock.newCondition();
-    private final Condition resultVerified = lock.newCondition();
-    private Exception exception;
+    static final String CONFIG = "log4j2-mutableFilter.xml";
+    static LoggerContext loggerContext = null;
+    static File targetFile = new File("target/test-classes/testConfig.json");
+    static Path target = targetFile.toPath();
+    CountDownLatch updated = new CountDownLatch(1);
 
     @AfterEach
-    void cleanup() {
-        exception = null;
-        ThreadContext.clearMap();
-        if (loggerContext != null) {
-            loggerContext.stop();
-            loggerContext = null;
+    public void after() {
+        try {
+            Files.deleteIfExists(target);
+        } catch (IOException ioe) {
+            // Ignore this.
         }
+        ThreadContext.clearMap();
+        loggerContext.stop();
+        loggerContext = null;
     }
 
-    @Test
-    void file_location_works(TestProperties properties, @TempDir Path dir) 
throws Exception {
-        // Set up the test file.
-        Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
-        Instant before = now.minus(1, ChronoUnit.MINUTES);
-        Instant after = now.plus(1, ChronoUnit.MINUTES);
-        Path testConfig = dir.resolve("testConfig.json");
-        properties.setProperty("configLocation", testConfig.toString());
-        try (final InputStream inputStream = new 
ByteArrayInputStream(EMPTY_CONFIG)) {
-            Files.copy(inputStream, testConfig);
-            Files.setLastModifiedTime(testConfig, FileTime.from(before));
-        }
-        // Setup Log4j
-        ConfigurationSource source =
-                ConfigurationSource.fromResource(CONFIG, 
getClass().getClassLoader());
-        Configuration configuration = 
ConfigurationFactory.getInstance().getConfiguration(null, source);
-        configuration.initialize(); // To create the components
-        final ListAppender app = configuration.getAppender("LIST");
-        assertThat(app).isNotNull();
-        final MutableThreadContextMapFilter filter = 
(MutableThreadContextMapFilter) configuration.getFilter();
+    @RetryingTest(maxAttempts = 5, suspendForMs = 10)
+    public void filterTest() throws Exception {
+        System.setProperty("configLocation", 
"target/test-classes/testConfig.json");
+        ThreadContext.put("loginId", "rgoers");
+        Path source = new 
File("target/test-classes/emptyConfig.json").toPath();
+        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+        final long fileTime = targetFile.lastModified() - 1000;
+        assertTrue(targetFile.setLastModified(fileTime));
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        final Appender app = 
loggerContext.getConfiguration().getAppender("List");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+        final MutableThreadContextMapFilter filter =
+                (MutableThreadContextMapFilter) 
loggerContext.getConfiguration().getFilter();
         assertNotNull(filter);
         filter.registerListener(this);
-
-        lock.lock();
-        try {
-            // Starts the configuration
-            loggerContext = 
Configurator.initialize(getClass().getClassLoader(), configuration);
-            assertNotNull(loggerContext);
-
-            final Logger logger = 
loggerContext.getLogger(MutableThreadContextMapFilterTest.class);
-
-            assertThat(filterUpdated.await(20, TimeUnit.SECONDS))
-                    .as("Initial configuration was loaded")
-                    .isTrue();
-            ThreadContext.put("loginId", "rgoers");
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).isEmpty();
-
-            // Prepare the second test case: updated config
-            try (final InputStream inputStream = new 
ByteArrayInputStream(FILTER_CONFIG)) {
-                Files.copy(inputStream, testConfig, 
StandardCopyOption.REPLACE_EXISTING);
-                Files.setLastModifiedTime(testConfig, FileTime.from(after));
+        final Logger logger = loggerContext.getLogger("Test");
+        logger.debug("This is a test");
+        Assertions.assertEquals(0, ((ListAppender) app).getEvents().size());
+        source = new File("target/test-classes/filterConfig.json").toPath();
+        String msg = null;
+        boolean copied = false;
+        for (int i = 0; i < 5 && !copied; ++i) {
+            Thread.sleep(100 + (100 * i));
+            try {
+                Files.copy(source, target, 
StandardCopyOption.REPLACE_EXISTING);
+                copied = true;
+            } catch (Exception ex) {
+                msg = ex.getMessage();
             }
-            resultVerified.signalAll();
-
-            assertThat(filterUpdated.await(20, TimeUnit.SECONDS))
-                    .as("Updated configuration was loaded")
-                    .isTrue();
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).hasSize(1);
-
-            // Prepare the third test case: removed config
-            Files.delete(testConfig);
-            resultVerified.signalAll();
-
-            assertThat(filterUpdated.await(20, TimeUnit.SECONDS))
-                    .as("Configuration removal was detected")
-                    .isTrue();
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).hasSize(1);
-            resultVerified.signalAll();
-        } finally {
-            lock.unlock();
         }
-        assertThat(exception).as("Asynchronous exception").isNull();
-    }
-
-    @Test
-    void http_location_works(TestProperties properties, WireMockRuntimeInfo 
info) throws Exception {
-        WireMock wireMock = info.getWireMock();
-        // Setup WireMock
-        // The HTTP Last-Modified header has a precision of 1 second
-        ZonedDateTime now = LocalDateTime.now().atZone(ZoneOffset.UTC);
-        ZonedDateTime before = now.minusMinutes(1);
-        ZonedDateTime after = now.plusMinutes(1);
-        properties.setProperty("configLocation", info.getHttpBaseUrl() + 
URL_PATH);
-        // Setup Log4j
-        ConfigurationSource source =
-                ConfigurationSource.fromResource(CONFIG, 
getClass().getClassLoader());
-        Configuration configuration = 
ConfigurationFactory.getInstance().getConfiguration(null, source);
-        configuration.initialize(); // To create the components
-        final ListAppender app = configuration.getAppender("LIST");
-        assertThat(app).isNotNull();
-        final MutableThreadContextMapFilter filter = 
(MutableThreadContextMapFilter) configuration.getFilter();
-        assertNotNull(filter);
-        filter.registerListener(this);
-        lock.lock();
-        try {
-            // Prepare the first test case: original empty config
-            wireMock.importStubMappings(createMapping(URL_PATH, CREDENTIALS, 
EMPTY_CONFIG, JSON, before));
-            // Starts the configuration
-            loggerContext = 
Configurator.initialize(getClass().getClassLoader(), configuration);
-            assertNotNull(loggerContext);
-
-            final Logger logger = 
loggerContext.getLogger(MutableThreadContextMapFilterTest.class);
-
-            assertThat(filterUpdated.await(2, TimeUnit.SECONDS))
-                    .as("Initial configuration was loaded")
-                    .isTrue();
-            ThreadContext.put("loginId", "rgoers");
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).isEmpty();
-
-            // Prepare the second test case: updated config
-            wireMock.removeMappings();
-            wireMock.importStubMappings(createMapping(URL_PATH, CREDENTIALS, 
FILTER_CONFIG, JSON, after));
-            resultVerified.signalAll();
-
-            assertThat(filterUpdated.await(2, TimeUnit.SECONDS))
-                    .as("Updated configuration was loaded")
-                    .isTrue();
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).hasSize(1);
-
-            // Prepare the third test case: removed config
-            wireMock.removeMappings();
-            resultVerified.signalAll();
-
-            assertThat(filterUpdated.await(2, TimeUnit.SECONDS))
-                    .as("Configuration removal was detected")
-                    .isTrue();
-            logger.debug("This is a test");
-            assertThat(app.getEvents()).hasSize(1);
-            resultVerified.signalAll();
-        } finally {
-            lock.unlock();
+        assertTrue(copied, "File not copied: " + msg);
+        assertNotEquals(fileTime, targetFile.lastModified());
+        if (!updated.await(5, TimeUnit.SECONDS)) {
+            fail("File update was not detected");
         }
-        assertThat(exception).as("Asynchronous exception").isNull();
+        logger.debug("This is a test");
+        Assertions.assertEquals(1, ((ListAppender) app).getEvents().size());
     }
 
     @Override
     public void onEvent() {
-        lock.lock();
-        try {
-            filterUpdated.signalAll();
-            resultVerified.await();
-        } catch (final InterruptedException e) {
-            exception = e;
-        } finally {
-            lock.unlock();
-        }
+        updated.countDown();
     }
 }
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/UrlConnectionFactoryTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/UrlConnectionFactoryTest.java
index 265c538da8..e6bb7d5928 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/UrlConnectionFactoryTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/UrlConnectionFactoryTest.java
@@ -16,24 +16,20 @@
  */
 package org.apache.logging.log4j.core.net;
 
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static java.util.Objects.requireNonNull;
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
-import static 
org.apache.logging.log4j.core.config.ConfigurationSourceTest.PATH_IN_JAR;
-import static org.apache.logging.log4j.core.net.WireMockUtil.createMapping;
-import static org.assertj.core.api.Assertions.assertThat;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
-import com.github.tomakehurst.wiremock.client.BasicCredentials;
-import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
-import com.github.tomakehurst.wiremock.junit5.WireMockTest;
 import com.sun.management.UnixOperatingSystemMXBean;
-import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.management.ManagementFactory;
@@ -42,102 +38,101 @@ import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.URL;
 import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.time.ZonedDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.apache.commons.io.IOUtils;
+import java.util.Base64;
+import java.util.Enumeration;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.config.ConfigurationSourceTest;
-import org.apache.logging.log4j.core.util.AuthorizationProvider;
-import org.apache.logging.log4j.test.junit.SetTestProperty;
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.junit.jupiter.api.AfterEach;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.condition.OS;
-import org.junit.jupiter.api.io.TempDir;
 import org.junitpioneer.jupiter.RetryingTest;
 
 /**
  * Tests the UrlConnectionFactory
  */
-@WireMockTest
-@SetTestProperty(key = "log4j2.configurationAllowedProtocols", value = 
"jar,http")
-class UrlConnectionFactoryTest {
+public class UrlConnectionFactoryTest {
 
     private static final Logger LOGGER = 
LogManager.getLogger(UrlConnectionFactoryTest.class);
+    private static final String BASIC = "Basic ";
+    private static final String expectedCreds = "testuser:password";
+    private static Server server;
+    private static final Base64.Decoder decoder = Base64.getDecoder();
+    private static int port;
+    private static final int BUF_SIZE = 1024;
 
-    private static final String URL_PATH = "/log4j2-config.xml";
-    private static final BasicCredentials CREDENTIALS = new 
BasicCredentials("testUser", "password");
-    private static final byte[] CONFIG_FILE_BODY;
-    private static final String CONTENT_TYPE = "application/xml";
-
-    static {
-        try (InputStream input = requireNonNull(
-                
UrlConnectionFactoryTest.class.getClassLoader().getResourceAsStream("log4j2-config.xml")))
 {
-            ByteArrayOutputStream output = new ByteArrayOutputStream();
-            IOUtils.copy(input, output);
-            CONFIG_FILE_BODY = output.toByteArray();
-        } catch (IOException e) {
-            throw new AssertionError(e);
+    @BeforeAll
+    public static void startServer() throws Exception {
+        try {
+            server = new Server(0);
+            final ServletContextHandler context = new ServletContextHandler();
+            final ServletHolder defaultServ = new ServletHolder("default", 
TestServlet.class);
+            defaultServ.setInitParameter("resourceBase", 
System.getProperty("user.dir"));
+            defaultServ.setInitParameter("dirAllowed", "true");
+            context.addServlet(defaultServ, "/");
+            server.setHandler(context);
+
+            // Start Server
+            server.start();
+            port = ((ServerConnector) 
server.getConnectors()[0]).getLocalPort();
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            throw ex;
         }
     }
 
-    @AfterEach
-    void cleanup(WireMockRuntimeInfo info) {
-        info.getWireMock().removeMappings();
+    @AfterAll
+    public static void stopServer() throws Exception {
+        server.stop();
     }
 
     @Test
-    @SetTestProperty(key = "log4j2.configurationUsername", value = "foo")
-    @SetTestProperty(key = "log4j2.configurationPassword", value = "bar")
-    void testBadCredentials(WireMockRuntimeInfo info) throws Exception {
-        WireMock wireMock = info.getWireMock();
-        // RFC 1123 format rounds to full seconds
-        ZonedDateTime now = 
ZonedDateTime.now().truncatedTo(ChronoUnit.SECONDS);
-        wireMock.importStubMappings(createMapping(URL_PATH, CREDENTIALS, 
CONFIG_FILE_BODY, CONTENT_TYPE, now));
-        final URI uri = new URI(info.getHttpBaseUrl() + URL_PATH);
+    public void testBadCrdentials() throws Exception {
+        System.setProperty("log4j2.Configuration.username", "foo");
+        System.setProperty("log4j2.Configuration.password", "bar");
+        System.setProperty("log4j2.Configuration.allowedProtocols", "http");
+        final URI uri = new URI("http://localhost:"; + port + 
"/log4j2-config.xml");
         final ConfigurationSource source = ConfigurationSource.fromUri(uri);
         assertNull(source, "A ConfigurationSource should not have been 
returned");
     }
 
     @Test
-    @SetTestProperty(key = "log4j2.configurationUsername", value = "testUser")
-    @SetTestProperty(key = "log4j2.configurationPassword", value = "password")
-    public void withAuthentication(WireMockRuntimeInfo info) throws Exception {
-        WireMock wireMock = info.getWireMock();
-        // RFC 1123 format rounds to full seconds
-        ZonedDateTime now = 
ZonedDateTime.now().truncatedTo(ChronoUnit.SECONDS);
-        wireMock.importStubMappings(createMapping(URL_PATH, CREDENTIALS, 
CONFIG_FILE_BODY, CONTENT_TYPE, now));
-        final URI uri = new URI(info.getHttpBaseUrl() + URL_PATH);
+    public void withAuthentication() throws Exception {
+        System.setProperty("log4j2.Configuration.username", "testuser");
+        System.setProperty("log4j2.Configuration.password", "password");
+        System.setProperty("log4j2.Configuration.allowedProtocols", "http");
+        final URI uri = new URI("http://localhost:"; + port + 
"/log4j2-config.xml");
         final ConfigurationSource source = ConfigurationSource.fromUri(uri);
         assertNotNull(source, "No ConfigurationSource returned");
         final InputStream is = source.getInputStream();
         assertNotNull(is, "No data returned");
         is.close();
         final long lastModified = source.getLastModified();
-        assertThat(lastModified).isEqualTo(now.toInstant().toEpochMilli());
         int result = verifyNotModified(uri, lastModified);
         assertEquals(SC_NOT_MODIFIED, result, "File was modified");
-
-        wireMock.removeMappings();
-        now = now.plusMinutes(5);
-        wireMock.importStubMappings(createMapping(URL_PATH, CREDENTIALS, 
CONFIG_FILE_BODY, CONTENT_TYPE, now));
+        final File file = new File("target/classes/log4j2-config.xml");
+        if (!file.setLastModified(System.currentTimeMillis())) {
+            fail("Unable to set last modified time");
+        }
         result = verifyNotModified(uri, lastModified);
         assertEquals(SC_OK, result, "File was not modified");
     }
 
     private int verifyNotModified(final URI uri, final long 
lastModifiedMillis) throws Exception {
-        AuthorizationProvider provider = 
ConfigurationFactory.authorizationProvider(PropertiesUtil.getProperties());
-        HttpURLConnection urlConnection =
-                UrlConnectionFactory.createConnection(uri.toURL(), 
lastModifiedMillis, null, provider);
+        final HttpURLConnection urlConnection =
+                UrlConnectionFactory.createConnection(uri.toURL(), 
lastModifiedMillis, null, null);
         urlConnection.connect();
 
         try {
@@ -150,39 +145,20 @@ class UrlConnectionFactoryTest {
 
     @RetryingTest(maxAttempts = 5, suspendForMs = 1000)
     @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Fails frequently on 
Windows (#2011)")
-    @SetTestProperty(key = "log4j2.configurationUsername", value = "testUser")
-    @SetTestProperty(key = "log4j2.configurationPassword", value = "password")
-    void testNoJarFileLeak(@TempDir Path dir, WireMockRuntimeInfo info) throws 
Exception {
-        Path jarFile = ConfigurationSourceTest.prepareJarConfigURL(dir);
+    public void testNoJarFileLeak() throws Exception {
+        ConfigurationSourceTest.prepareJarConfigURL();
+        final URL url = new 
File("target/test-classes/jarfile.jar").toURI().toURL();
         // Retrieve using 'file:'
-        URL jarUrl = new URL("jar:" + jarFile.toUri().toURL() + "!" + 
PATH_IN_JAR);
+        URL jarUrl = new URL("jar:" + url.toString() + "!/config/console.xml");
         long expected = getOpenFileDescriptorCount();
         UrlConnectionFactory.createConnection(jarUrl).getInputStream().close();
         assertEquals(expected, getOpenFileDescriptorCount());
-
-        // Prepare mock
-        ByteArrayOutputStream body = new ByteArrayOutputStream();
-        try (InputStream inputStream = Files.newInputStream(jarFile)) {
-            IOUtils.copy(inputStream, body);
-        }
-        WireMock wireMock = info.getWireMock();
-        wireMock.register(WireMock.get("/jarFile.jar")
-                .willReturn(
-                        
aResponse().withStatus(200).withBodyFile("jarFile.jar").withBody(body.toByteArray())));
         // Retrieve using 'http:'
-        jarUrl = new URL("jar:" + info.getHttpBaseUrl() + "/jarFile.jar!" + 
PATH_IN_JAR);
-        // URLConnection leaves JAR files in the temporary directory
-        Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
-        List<Path> expectedFiles;
-        try (Stream<Path> stream = Files.list(tmpDir)) {
-            expectedFiles = stream.collect(Collectors.toList());
-        }
+        jarUrl = new URL("jar:http://localhost:"; + port + 
"/jarfile.jar!/config/console.xml");
+        final File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+        expected = tmpDir.list().length;
         UrlConnectionFactory.createConnection(jarUrl).getInputStream().close();
-        List<Path> actualFiles;
-        try (Stream<Path> stream = Files.list(tmpDir)) {
-            actualFiles = stream.collect(Collectors.toList());
-        }
-        assertThat(actualFiles).containsExactlyElementsOf(expectedFiles);
+        assertEquals(expected, tmpDir.list().length, "File descriptor leak");
     }
 
     private long getOpenFileDescriptorCount() {
@@ -192,4 +168,53 @@ class UrlConnectionFactoryTest {
         }
         return 0L;
     }
+
+    public static class TestServlet extends DefaultServlet {
+
+        private static final long serialVersionUID = -2885158530511450659L;
+
+        @Override
+        protected void doGet(final HttpServletRequest request, final 
HttpServletResponse response)
+                throws ServletException, IOException {
+            final Enumeration<String> headers = 
request.getHeaders(HttpHeader.AUTHORIZATION.toString());
+            if (headers == null) {
+                response.sendError(SC_UNAUTHORIZED, "No Auth header");
+                return;
+            }
+            while (headers.hasMoreElements()) {
+                final String authData = headers.nextElement();
+                assertTrue(authData.startsWith(BASIC), "Not a Basic auth 
header");
+                final String credentials = new 
String(decoder.decode(authData.substring(BASIC.length())));
+                if (!expectedCreds.equals(credentials)) {
+                    response.sendError(SC_UNAUTHORIZED, "Invalid credentials");
+                    return;
+                }
+            }
+            final String servletPath = request.getServletPath();
+            if (servletPath != null) {
+                File file = new File("target/classes" + servletPath);
+                if (!file.exists()) {
+                    file = new File("target/test-classes" + servletPath);
+                }
+                if (!file.exists()) {
+                    response.sendError(SC_NOT_FOUND);
+                    return;
+                }
+                final long modifiedSince = 
request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.toString());
+                final long lastModified = (file.lastModified() / 1000) * 1000;
+                LOGGER.debug("LastModified: {}, modifiedSince: {}", 
lastModified, modifiedSince);
+                if (modifiedSince > 0 && lastModified <= modifiedSince) {
+                    response.setStatus(SC_NOT_MODIFIED);
+                    return;
+                }
+                response.setDateHeader(HttpHeader.LAST_MODIFIED.toString(), 
lastModified);
+                response.setContentLengthLong(file.length());
+                Files.copy(file.toPath(), response.getOutputStream());
+                response.getOutputStream().flush();
+                response.setStatus(SC_OK);
+            } else {
+                response.sendError(SC_BAD_REQUEST, "Unsupported request");
+            }
+        }
+    }
 }
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/WireMockUtil.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/WireMockUtil.java
deleted file mode 100644
index f865574232..0000000000
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/WireMockUtil.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.net;
-
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.absent;
-import static com.github.tomakehurst.wiremock.client.WireMock.after;
-import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
-import static com.github.tomakehurst.wiremock.client.WireMock.before;
-import static com.github.tomakehurst.wiremock.client.WireMock.equalToDateTime;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static com.github.tomakehurst.wiremock.client.WireMock.notContaining;
-import static com.github.tomakehurst.wiremock.stubbing.StubImport.stubImport;
-import static com.google.common.net.HttpHeaders.AUTHORIZATION;
-import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
-import static com.google.common.net.HttpHeaders.IF_MODIFIED_SINCE;
-import static com.google.common.net.HttpHeaders.LAST_MODIFIED;
-
-import com.github.tomakehurst.wiremock.client.BasicCredentials;
-import com.github.tomakehurst.wiremock.stubbing.StubImport;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-public class WireMockUtil {
-
-    private static final DateTimeFormatter formatter = 
DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC);
-
-    /**
-     * Establishes a set of mapping to serve a file
-     *
-     * @param urlPath The URL path of the served file
-     * @param credentials The credentials to use for authentication
-     * @param body The body of the file
-     * @param contentType The MIME content type of the file
-     * @param lastModified The last modification date of the file
-     * @return A set of mappings
-     */
-    public static StubImport createMapping(
-            String urlPath, BasicCredentials credentials, byte[] body, String 
contentType, ZonedDateTime lastModified) {
-        int idx = urlPath.lastIndexOf('/');
-        String fileName = idx == -1 ? urlPath : urlPath.substring(idx + 1);
-        return stubImport()
-                // Lack of authentication data
-                .stub(get(anyUrl())
-                        .withHeader(AUTHORIZATION, absent())
-                        
.willReturn(aResponse().withStatus(401).withStatusMessage("Not Authenticated")))
-                // Wrong authentication data
-                .stub(get(anyUrl())
-                        .withHeader(AUTHORIZATION, 
notContaining(credentials.asAuthorizationHeaderValue()))
-                        
.willReturn(aResponse().withStatus(403).withStatusMessage("Not Authorized")))
-                // Serves the file
-                .stub(get(urlPath)
-                        .withBasicAuth(credentials.username, 
credentials.password)
-                        .withHeader(IF_MODIFIED_SINCE, 
before(lastModified).or(absent()))
-                        .willReturn(aResponse()
-                                .withStatus(200)
-                                .withBodyFile(fileName)
-                                .withBody(body)
-                                .withHeader(LAST_MODIFIED, 
formatter.format(lastModified))
-                                .withHeader(CONTENT_TYPE, contentType)))
-                // The file was not updated since lastModified
-                .stub(get(urlPath)
-                        .withBasicAuth(credentials.username, 
credentials.password)
-                        .withHeader(IF_MODIFIED_SINCE, 
after(lastModified).or(equalToDateTime(lastModified)))
-                        .willReturn(
-                                
aResponse().withStatus(304).withHeader(LAST_MODIFIED, 
formatter.format(lastModified))))
-                .build();
-    }
-}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/HttpWatcherTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/HttpWatcherTest.java
deleted file mode 100644
index 774f36d5aa..0000000000
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/HttpWatcherTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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 static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
-import com.github.tomakehurst.wiremock.junit5.WireMockTest;
-import com.github.tomakehurst.wiremock.stubbing.StubMapping;
-import java.net.URL;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import org.apache.logging.log4j.core.config.AbstractConfiguration;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationListener;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.HttpWatcher;
-import org.apache.logging.log4j.core.config.Reconfigurable;
-import org.apache.logging.log4j.core.net.UrlConnectionFactory;
-import org.apache.logging.log4j.test.junit.SetTestProperty;
-import org.junit.jupiter.api.Test;
-
-/**
- * Test the WatchManager
- */
-@SetTestProperty(key = UrlConnectionFactory.ALLOWED_PROTOCOLS, value = 
"http,https")
-@WireMockTest
-class HttpWatcherTest {
-
-    private static final DateTimeFormatter formatter = 
DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC);
-    private static final String XML = "application/xml";
-
-    @Test
-    void testModified(final WireMockRuntimeInfo info) throws Exception {
-        final WireMock wireMock = info.getWireMock();
-
-        final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
-        List<ConfigurationListener> listeners = singletonList(new 
TestConfigurationListener(queue, "log4j-test1.xml"));
-        // HTTP Last-Modified is in seconds
-        Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
-        Instant previous = now.minus(5, ChronoUnit.MINUTES);
-        final URL url = new URL(info.getHttpBaseUrl() + "/log4j-test1.xml");
-        final Configuration configuration = createConfiguration(url);
-
-        final StubMapping stubMapping = 
wireMock.register(get("/log4j-test1.xml")
-                .willReturn(aResponse()
-                        .withBodyFile("log4j-test1.xml")
-                        .withStatus(200)
-                        .withHeader("Last-Modified", 
formatter.format(previous))
-                        .withHeader("Content-Type", XML)));
-        Watcher watcher = new HttpWatcher(configuration, null, listeners, 
previous.toEpochMilli());
-        watcher.watching(new Source(url));
-        try {
-            assertThat(watcher.isModified()).as("File was modified").isTrue();
-            assertThat(watcher.getLastModified()).as("File modification 
time").isEqualTo(previous.toEpochMilli());
-            // Check if listeners are correctly called
-            // Note: listeners are called asynchronously
-            watcher.modified();
-            String str = queue.poll(1, TimeUnit.SECONDS);
-            assertThat(str).isEqualTo("log4j-test1.xml");
-            ConfigurationSource configurationSource = 
configuration.getConfigurationSource();
-            // Check that the last modified time of the ConfigurationSource 
was modified as well
-            // See: https://github.com/apache/logging-log4j2/issues/2937
-            assertThat(configurationSource.getLastModified())
-                    .as("Last modification time of current 
ConfigurationSource")
-                    .isEqualTo(0L);
-            configurationSource = configurationSource.resetInputStream();
-            assertThat(configurationSource.getLastModified())
-                    .as("Last modification time of next ConfigurationSource")
-                    .isEqualTo(previous.toEpochMilli());
-        } finally {
-            wireMock.removeStubMapping(stubMapping);
-        }
-    }
-
-    @Test
-    void testNotModified(final WireMockRuntimeInfo info) throws Exception {
-        final WireMock wireMock = info.getWireMock();
-
-        final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
-        final List<ConfigurationListener> listeners =
-                singletonList(new TestConfigurationListener(queue, 
"log4j-test2.xml"));
-        // HTTP Last-Modified is in seconds
-        Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
-        Instant previous = now.minus(5, ChronoUnit.MINUTES);
-        final URL url = new URL(info.getHttpBaseUrl() + "/log4j-test2.xml");
-        final Configuration configuration = createConfiguration(url);
-
-        final StubMapping stubMapping = 
wireMock.register(get("/log4j-test2.xml")
-                .willReturn(aResponse()
-                        .withStatus(304)
-                        .withHeader("Last-Modified", formatter.format(now) + " 
GMT")
-                        .withHeader("Content-Type", XML)));
-        Watcher watcher = new HttpWatcher(configuration, null, listeners, 
previous.toEpochMilli());
-        watcher.watching(new Source(url));
-        try {
-            assertThat(watcher.isModified()).as("File was modified").isFalse();
-            // If the file was not modified, neither should be the last 
modification time
-            
assertThat(watcher.getLastModified()).isEqualTo(previous.toEpochMilli());
-            // Check that the last modified time of the ConfigurationSource 
was not modified either
-            ConfigurationSource configurationSource = 
configuration.getConfigurationSource();
-            assertThat(configurationSource.getLastModified())
-                    .as("Last modification time of current 
ConfigurationSource")
-                    .isEqualTo(0L);
-            configurationSource = configurationSource.resetInputStream();
-            assertThat(configurationSource.getLastModified())
-                    .as("Last modification time of next ConfigurationSource")
-                    .isEqualTo(0L);
-        } finally {
-            wireMock.removeStubMapping(stubMapping);
-        }
-    }
-
-    // Creates a configuration with a predefined configuration source
-    private static Configuration createConfiguration(URL url) {
-        ConfigurationSource configurationSource = new ConfigurationSource(new 
Source(url), new byte[0], 0L);
-        return new AbstractConfiguration(null, configurationSource) {};
-    }
-
-    private static class TestConfigurationListener implements 
ConfigurationListener {
-        private final Queue<String> queue;
-        private final String name;
-
-        public TestConfigurationListener(final Queue<String> queue, final 
String name) {
-            this.queue = queue;
-            this.name = name;
-        }
-
-        @Override
-        public void onChange(final Reconfigurable reconfigurable) {
-            // System.out.println("Reconfiguration detected for " + name);
-            queue.add(name);
-        }
-    }
-}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchHttpTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchHttpTest.java
new file mode 100644
index 0000000000..c433f83486
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchHttpTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
+import com.github.tomakehurst.wiremock.junit5.WireMockTest;
+import com.github.tomakehurst.wiremock.stubbing.StubMapping;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Queue;
+import java.util.TimeZone;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationListener;
+import org.apache.logging.log4j.core.config.ConfigurationScheduler;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
+import org.apache.logging.log4j.core.config.HttpWatcher;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.net.UrlConnectionFactory;
+import org.apache.logging.log4j.core.util.datetime.FastDateFormat;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test the WatchManager
+ */
+@SetTestProperty(key = UrlConnectionFactory.ALLOWED_PROTOCOLS, value = 
"http,https")
+@WireMockTest
+public class WatchHttpTest {
+
+    private static final String FORCE_RUN_KEY = 
WatchHttpTest.class.getSimpleName() + ".forceRun";
+    private final String file = "log4j-test1.xml";
+    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+    private static FastDateFormat formatter = FastDateFormat.getInstance("EEE, 
dd MMM yyyy HH:mm:ss", UTC);
+    private static final String XML = "application/xml";
+
+    private static final boolean IS_WINDOWS = 
PropertiesUtil.getProperties().isOsWindows();
+
+    @Test
+    public void testWatchManager(final WireMockRuntimeInfo info) throws 
Exception {
+        assumeTrue(!IS_WINDOWS || Boolean.getBoolean(FORCE_RUN_KEY));
+        final WireMock wireMock = info.getWireMock();
+
+        final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
+        final List<ConfigurationListener> listeners = new ArrayList<>();
+        listeners.add(new TestConfigurationListener(queue, "log4j-test1.xml"));
+        final Calendar now = Calendar.getInstance(UTC);
+        final Calendar previous = now;
+        previous.add(Calendar.MINUTE, -5);
+        final Configuration configuration = new DefaultConfiguration();
+        final URL url = new URL(info.getHttpBaseUrl() + "/log4j-test1.xml");
+        final StubMapping stubMapping = 
wireMock.register(get(urlPathEqualTo("/log4j-test1.xml"))
+                .willReturn(aResponse()
+                        .withBodyFile(file)
+                        .withStatus(200)
+                        .withHeader("Last-Modified", 
formatter.format(previous) + " GMT")
+                        .withHeader("Content-Type", XML)));
+        final ConfigurationScheduler scheduler = new ConfigurationScheduler();
+        scheduler.incrementScheduledItems();
+        final WatchManager watchManager = new WatchManager(scheduler);
+        watchManager.setIntervalSeconds(1);
+        scheduler.start();
+        watchManager.start();
+        try {
+            watchManager.watch(
+                    new Source(url), new HttpWatcher(configuration, null, 
listeners, previous.getTimeInMillis()));
+            final String str = queue.poll(3, TimeUnit.SECONDS);
+            assertNotNull("File change not detected", str);
+        } finally {
+            watchManager.stop();
+            scheduler.stop();
+            wireMock.removeStubMapping(stubMapping);
+        }
+    }
+
+    @Test
+    public void testNotModified(final WireMockRuntimeInfo info) throws 
Exception {
+        assumeTrue(!IS_WINDOWS || Boolean.getBoolean(FORCE_RUN_KEY));
+        final WireMock wireMock = info.getWireMock();
+
+        final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
+        final List<ConfigurationListener> listeners = new ArrayList<>();
+        listeners.add(new TestConfigurationListener(queue, "log4j-test2.xml"));
+        final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+        final Calendar now = Calendar.getInstance(timeZone);
+        final Calendar previous = now;
+        previous.add(Calendar.MINUTE, -5);
+        final Configuration configuration = new DefaultConfiguration();
+        final URL url = new URL(info.getHttpBaseUrl() + "/log4j-test2.xml");
+        final StubMapping stubMapping = 
wireMock.register(get(urlPathEqualTo("/log4j-test2.xml"))
+                .willReturn(aResponse()
+                        .withBodyFile(file)
+                        .withStatus(304)
+                        .withHeader("Last-Modified", formatter.format(now) + " 
GMT")
+                        .withHeader("Content-Type", XML)));
+        final ConfigurationScheduler scheduler = new ConfigurationScheduler();
+        scheduler.incrementScheduledItems();
+        final WatchManager watchManager = new WatchManager(scheduler);
+        watchManager.setIntervalSeconds(1);
+        scheduler.start();
+        watchManager.start();
+        try {
+            watchManager.watch(
+                    new Source(url), new HttpWatcher(configuration, null, 
listeners, previous.getTimeInMillis()));
+            final String str = queue.poll(2, TimeUnit.SECONDS);
+            assertNull("File changed.", str);
+        } finally {
+            watchManager.stop();
+            scheduler.stop();
+            wireMock.removeStubMapping(stubMapping);
+        }
+    }
+
+    private static class TestConfigurationListener implements 
ConfigurationListener {
+        private final Queue<String> queue;
+        private final String name;
+
+        public TestConfigurationListener(final Queue<String> queue, final 
String name) {
+            this.queue = queue;
+            this.name = name;
+        }
+
+        @Override
+        public void onChange(final Reconfigurable reconfigurable) {
+            // System.out.println("Reconfiguration detected for " + name);
+            queue.add(name);
+        }
+    }
+}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java
index 49269d7134..68b3baaca6 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/WatchManagerTest.java
@@ -18,12 +18,6 @@ package org.apache.logging.log4j.core.util;
 
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -36,9 +30,6 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import org.apache.logging.log4j.core.config.ConfigurationScheduler;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
@@ -49,110 +40,106 @@ import org.junit.jupiter.api.condition.OS;
  */
 @DisabledOnOs(OS.WINDOWS)
 @EnabledIfSystemProperty(named = "WatchManagerTest.forceRun", matches = "true")
-class WatchManagerTest {
+public class WatchManagerTest {
 
     private final String testFile = "target/testWatchFile";
     private final String originalFile = "target/test-classes/log4j-test1.xml";
     private final String newFile = "target/test-classes/log4j-test1.yaml";
 
-    private ConfigurationScheduler scheduler;
-    private WatchManager watchManager;
-
-    @BeforeEach
-    void setUp() {
-        scheduler = new ConfigurationScheduler();
+    @Test
+    public void testWatchManager() throws Exception {
+        final ConfigurationScheduler scheduler = new ConfigurationScheduler();
         scheduler.incrementScheduledItems();
-        watchManager = new WatchManager(scheduler);
+        final WatchManager watchManager = new WatchManager(scheduler);
         watchManager.setIntervalSeconds(1);
         scheduler.start();
         watchManager.start();
-    }
-
-    @AfterEach
-    void tearDown() {
-        watchManager.stop();
-        scheduler.stop();
-        watchManager = null;
-        scheduler = null;
-    }
-
-    @Test
-    void testWatchManager() throws Exception {
-        final File sourceFile = new File(originalFile);
-        Path source = Paths.get(sourceFile.toURI());
-        try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
-            Files.copy(source, targetStream);
+        try {
+            final File sourceFile = new File(originalFile);
+            Path source = Paths.get(sourceFile.toURI());
+            try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
+                Files.copy(source, targetStream);
+            }
+            final File updateFile = new File(newFile);
+            final File targetFile = new File(testFile);
+            final 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);
+            final File f = queue.poll(1, TimeUnit.SECONDS);
+            assertNotNull(f, "File change not detected");
+        } finally {
+            watchManager.stop();
+            scheduler.stop();
         }
-        final File updateFile = new File(newFile);
-        final File targetFile = new File(testFile);
-        final 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);
-        final File f = queue.poll(1, TimeUnit.SECONDS);
-        assertNotNull(f, "File change not detected");
     }
 
     @Test
-    void testWatchManagerReset() throws Exception {
-        final File sourceFile = new File(originalFile);
-        Path source = Paths.get(sourceFile.toURI());
-        try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
-            Files.copy(source, targetStream);
-        }
-        final File updateFile = new File(newFile);
-        final File targetFile = new File(testFile);
-        final BlockingQueue<File> queue = new LinkedBlockingQueue<>();
-        watchManager.watchFile(targetFile, new TestWatcher(queue));
-        watchManager.stop();
-        Thread.sleep(1000);
-        source = Paths.get(updateFile.toURI());
-        Files.copy(source, Paths.get(targetFile.toURI()), 
StandardCopyOption.REPLACE_EXISTING);
-        watchManager.reset();
+    public void testWatchManagerReset() throws Exception {
+        final ConfigurationScheduler scheduler = new ConfigurationScheduler();
+        scheduler.incrementScheduledItems();
+        final WatchManager watchManager = new WatchManager(scheduler);
+        watchManager.setIntervalSeconds(1);
+        scheduler.start();
         watchManager.start();
-        Thread.sleep(1000);
-        final File f = queue.poll(1, TimeUnit.SECONDS);
-        assertNull(f, "File change detected");
-    }
-
-    @Test
-    void testWatchManagerResetFile() throws Exception {
-        final File sourceFile = new File(originalFile);
-        Path source = Paths.get(sourceFile.toURI());
-        try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
-            Files.copy(source, targetStream);
+        try {
+            final File sourceFile = new File(originalFile);
+            Path source = Paths.get(sourceFile.toURI());
+            try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
+                Files.copy(source, targetStream);
+            }
+            final File updateFile = new File(newFile);
+            final File targetFile = new File(testFile);
+            final BlockingQueue<File> queue = new LinkedBlockingQueue<>();
+            watchManager.watchFile(targetFile, new TestWatcher(queue));
+            watchManager.stop();
+            Thread.sleep(1000);
+            source = Paths.get(updateFile.toURI());
+            Files.copy(source, Paths.get(targetFile.toURI()), 
StandardCopyOption.REPLACE_EXISTING);
+            watchManager.reset();
+            watchManager.start();
+            Thread.sleep(1000);
+            final File f = queue.poll(1, TimeUnit.SECONDS);
+            assertNull(f, "File change detected");
+        } finally {
+            watchManager.stop();
+            scheduler.stop();
         }
-        final File updateFile = new File(newFile);
-        final File targetFile = new File(testFile);
-        final BlockingQueue<File> queue = new LinkedBlockingQueue<>();
-        watchManager.watchFile(targetFile, new TestWatcher(queue));
-        watchManager.stop();
-        Thread.sleep(1000);
-        source = Paths.get(updateFile.toURI());
-        Files.copy(source, Paths.get(targetFile.toURI()), 
StandardCopyOption.REPLACE_EXISTING);
-        watchManager.reset(targetFile);
-        watchManager.start();
-        Thread.sleep(1000);
-        final File f = queue.poll(1, TimeUnit.SECONDS);
-        assertNull(f, "File change detected");
     }
 
-    /**
-     * Verify the
-     */
     @Test
-    void testWatchManagerCallsWatcher() {
-        Watcher watcher = mock(Watcher.class);
-        when(watcher.isModified()).thenReturn(false);
-        watchManager.watch(new Source(ConfigurationSource.NULL_SOURCE), 
watcher);
-        verify(watcher, timeout(2000)).isModified();
-        verify(watcher, never()).modified();
-        when(watcher.isModified()).thenReturn(true);
-        clearInvocations(watcher);
-        verify(watcher, timeout(2000)).isModified();
-        verify(watcher).modified();
+    public void testWatchManagerResetFile() throws Exception {
+        final ConfigurationScheduler scheduler = new ConfigurationScheduler();
+        scheduler.incrementScheduledItems();
+        final WatchManager watchManager = new WatchManager(scheduler);
+        watchManager.setIntervalSeconds(1);
+        scheduler.start();
+        watchManager.start();
+        try {
+            final File sourceFile = new File(originalFile);
+            Path source = Paths.get(sourceFile.toURI());
+            try (final FileOutputStream targetStream = new 
FileOutputStream(testFile)) {
+                Files.copy(source, targetStream);
+            }
+            final File updateFile = new File(newFile);
+            final File targetFile = new File(testFile);
+            final BlockingQueue<File> queue = new LinkedBlockingQueue<>();
+            watchManager.watchFile(targetFile, new TestWatcher(queue));
+            watchManager.stop();
+            Thread.sleep(1000);
+            source = Paths.get(updateFile.toURI());
+            Files.copy(source, Paths.get(targetFile.toURI()), 
StandardCopyOption.REPLACE_EXISTING);
+            watchManager.reset(targetFile);
+            watchManager.start();
+            Thread.sleep(1000);
+            final File f = queue.poll(1, TimeUnit.SECONDS);
+            assertNull(f, "File change detected");
+        } finally {
+            watchManager.stop();
+            scheduler.stop();
+        }
     }
 
     private static class TestWatcher implements FileWatcher {
diff --git a/log4j-core-test/src/test/resources/emptyConfig.json 
b/log4j-core-test/src/test/resources/emptyConfig.json
new file mode 100644
index 0000000000..37086f2b1f
--- /dev/null
+++ b/log4j-core-test/src/test/resources/emptyConfig.json
@@ -0,0 +1,4 @@
+{
+  "configs": {
+  }
+}
\ No newline at end of file
diff --git 
a/log4j-core-test/src/test/resources/filter/MutableThreadContextMapFilterTest.xml
 
b/log4j-core-test/src/test/resources/filter/MutableThreadContextMapFilterTest.xml
deleted file mode 100644
index 34eb059c4e..0000000000
--- 
a/log4j-core-test/src/test/resources/filter/MutableThreadContextMapFilterTest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ 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 xmlns="https://logging.apache.org/xml/ns";
-               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-               xsi:schemaLocation="
-                   https://logging.apache.org/xml/ns
-                   https://logging.apache.org/xml/ns/log4j-config-2.xsd";>
-  <MutableThreadContextMapFilter configLocation="${test:configLocation}"
-                                 pollInterval="1"
-                                 onMatch="ACCEPT"
-                                 onMismatch="NEUTRAL"/>
-  <Appenders>
-    <List name="LIST"/>
-  </Appenders>
-  <Loggers>
-    <Root level="ERROR">
-      <AppenderRef ref="LIST"/>
-    </Root>
-  </Loggers>
-</Configuration>
diff --git a/log4j-core-test/src/test/resources/filterConfig.json 
b/log4j-core-test/src/test/resources/filterConfig.json
new file mode 100644
index 0000000000..91c8143ec2
--- /dev/null
+++ b/log4j-core-test/src/test/resources/filterConfig.json
@@ -0,0 +1,6 @@
+{
+  "configs": {
+    "loginId": ["rgoers", "adam"],
+    "corpAcctNumber": ["30510263"]
+  }
+}
\ No newline at end of file
diff --git 
a/log4j-core-test/src/test/resources/config/ConfigurationSourceTest.xml 
b/log4j-core-test/src/test/resources/log4j2-mutableFilter.xml
similarity index 69%
rename from 
log4j-core-test/src/test/resources/config/ConfigurationSourceTest.xml
rename to log4j-core-test/src/test/resources/log4j2-mutableFilter.xml
index 54c7f846f3..2f4e391d13 100644
--- a/log4j-core-test/src/test/resources/config/ConfigurationSourceTest.xml
+++ b/log4j-core-test/src/test/resources/log4j2-mutableFilter.xml
@@ -15,18 +15,21 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<Configuration xmlns="https://logging.apache.org/xml/ns"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-               xsi:schemaLocation="
-                   https://logging.apache.org/xml/ns
-                   https://logging.apache.org/xml/ns/log4j-config-2.xsd";>
+<Configuration name="ConfigTest" status="ERROR">
+  <MutableThreadContextMapFilter  configLocation="${sys:configLocation}" 
pollInterval="1" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
   <Appenders>
-    <Console name="Console">
-      <PatternLayout/>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
     </Console>
+    <List name="List">
+    </List>
   </Appenders>
   <Loggers>
-    <Root level="TRACE">
-      <AppenderRef ref="Console"/>
+    <Logger name="Test" level="error">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
     </Root>
   </Loggers>
 </Configuration>
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index 88f66ac364..ee8e7c6a3c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -93,7 +93,7 @@ public class LoggerContext extends AbstractLifeCycle
     private volatile Configuration configuration = new DefaultConfiguration();
 
     private static final String EXTERNAL_CONTEXT_KEY = 
"__EXTERNAL_CONTEXT_KEY__";
-    private final ConcurrentMap<String, Object> externalMap = new 
ConcurrentHashMap<>();
+    private ConcurrentMap<String, Object> externalMap = new 
ConcurrentHashMap<>();
     private String contextName;
     private volatile URI configLocation;
     private Cancellable shutdownCallback;
@@ -128,7 +128,9 @@ public class LoggerContext extends AbstractLifeCycle
      */
     public LoggerContext(final String name, final Object externalContext, 
final URI configLocn) {
         this.contextName = name;
-        if (externalContext != null) {
+        if (externalContext == null) {
+            externalMap.remove(EXTERNAL_CONTEXT_KEY);
+        } else {
             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
         }
         this.configLocation = configLocn;
@@ -147,7 +149,9 @@ public class LoggerContext extends AbstractLifeCycle
             justification = "The configLocn comes from a secure source (Log4j 
properties)")
     public LoggerContext(final String name, final Object externalContext, 
final String configLocn) {
         this.contextName = name;
-        if (externalContext != null) {
+        if (externalContext == null) {
+            externalMap.remove(EXTERNAL_CONTEXT_KEY);
+        } else {
             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
         }
         if (configLocn != null) {
@@ -168,7 +172,7 @@ public class LoggerContext extends AbstractLifeCycle
         if (listeners == null) {
             synchronized (this) {
                 if (listeners == null) {
-                    listeners = new CopyOnWriteArrayList<>();
+                    listeners = new 
CopyOnWriteArrayList<LoggerContextShutdownAware>();
                 }
             }
         }
@@ -279,7 +283,7 @@ public class LoggerContext extends AbstractLifeCycle
      * @param config The new Configuration.
      */
     public void start(final Configuration config) {
-        LOGGER.info("Starting {}[name={}] with configuration {}...", 
getClass().getSimpleName(), getName(), config);
+        LOGGER.debug("Starting LoggerContext[name={}, {}] with configuration 
{}...", getName(), this, config);
         if (configLock.tryLock()) {
             try {
                 if (this.isInitialized() || this.isStopped()) {
@@ -293,7 +297,7 @@ public class LoggerContext extends AbstractLifeCycle
             }
         }
         setConfiguration(config);
-        LOGGER.info("{}[name={}] started with configuration {}.", 
getClass().getSimpleName(), getName(), config);
+        LOGGER.debug("LoggerContext[name={}, {}] started OK with configuration 
{}.", getName(), this, config);
     }
 
     private void setUpShutdownHook() {
@@ -308,6 +312,7 @@ public class LoggerContext extends AbstractLifeCycle
                     this.shutdownCallback = ((ShutdownCallbackRegistry) 
factory).addShutdownCallback(new Runnable() {
                         @Override
                         public void run() {
+                            @SuppressWarnings("resource")
                             final LoggerContext context = LoggerContext.this;
                             LOGGER.debug(
                                     SHUTDOWN_HOOK_MARKER,
@@ -709,7 +714,7 @@ public class LoggerContext extends AbstractLifeCycle
      */
     private void reconfigure(final URI configURI) {
         final Object externalContext = externalMap.get(EXTERNAL_CONTEXT_KEY);
-        final ClassLoader cl = externalContext instanceof ClassLoader ? 
(ClassLoader) externalContext : null;
+        final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? 
(ClassLoader) externalContext : null;
         LOGGER.debug(
                 "Reconfiguration started for context[name={}] at URI {} ({}) 
with optional ClassLoader: {}",
                 contextName,
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 4b160f98ef..3c5069f766 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
@@ -281,9 +281,9 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
                 watchManager.setIntervalSeconds(monitorIntervalSeconds);
                 if (configSource.getFile() != null) {
                     final Source cfgSource = new Source(configSource);
-                    final long lastModified = 
configSource.getFile().lastModified();
+                    final long lastModifeid = 
configSource.getFile().lastModified();
                     final ConfigurationFileWatcher watcher =
-                            new ConfigurationFileWatcher(this, reconfigurable, 
listeners, lastModified);
+                            new ConfigurationFileWatcher(this, reconfigurable, 
listeners, lastModifeid);
                     watchManager.watch(cfgSource, watcher);
                 } else if (configSource.getURL() != null) {
                     monitorSource(reconfigurable, configSource);
@@ -318,13 +318,9 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
         if (getState() == State.INITIALIZING) {
             initialize();
         }
-        LOGGER.info("Starting configuration {}...", this);
+        LOGGER.debug("Starting configuration {}", this);
         this.setStarting();
         if (watchManager.getIntervalSeconds() >= 0) {
-            LOGGER.info(
-                    "Start watching for changes to {} every {} seconds",
-                    getConfigurationSource(),
-                    watchManager.getIntervalSeconds());
             watchManager.start();
         }
         if (hasAsyncLoggers()) {
@@ -342,7 +338,7 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
             root.start(); // LOG4J2-336
         }
         super.start();
-        LOGGER.info("Configuration {} started.", this);
+        LOGGER.debug("Started configuration {} OK.", this);
     }
 
     private boolean hasAsyncLoggers() {
@@ -362,9 +358,9 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
      */
     @Override
     public boolean stop(final long timeout, final TimeUnit timeUnit) {
-        LOGGER.info("Stopping configuration {}...", this);
         this.setStopping();
         super.stop(timeout, timeUnit, false);
+        LOGGER.trace("Stopping {}...", this);
 
         // Stop the components that are closest to the application first:
         // 1. Notify all LoggerConfigs' ReliabilityStrategy that the 
configuration will be stopped.
@@ -460,7 +456,7 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
             advertiser.unadvertise(advertisement);
         }
         setStopped();
-        LOGGER.info("Configuration {} stopped.", this);
+        LOGGER.debug("Stopped {} OK", this);
         return true;
     }
 
@@ -488,7 +484,6 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
         // default does nothing, subclasses do work.
     }
 
-    @SuppressWarnings("deprecation")
     protected Level getDefaultStatus() {
         final PropertiesUtil properties = PropertiesUtil.getProperties();
         String statusLevel = 
properties.getStringProperty(StatusLogger.DEFAULT_STATUS_LISTENER_LEVEL);
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
index 0368c5d705..fbcf278a07 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
@@ -33,12 +33,10 @@ import java.net.URLConnection;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Objects;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.net.UrlConnectionFactory;
 import org.apache.logging.log4j.core.util.FileUtils;
 import org.apache.logging.log4j.core.util.Loader;
 import org.apache.logging.log4j.core.util.Source;
-import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.Constants;
 import org.apache.logging.log4j.util.LoaderUtil;
 
@@ -47,8 +45,6 @@ import org.apache.logging.log4j.util.LoaderUtil;
  */
 public class ConfigurationSource {
 
-    private static final Logger LOGGER = StatusLogger.getLogger();
-
     /**
      * ConfigurationSource to use with Configurations that do not require a 
"real" configuration source.
      */
@@ -62,7 +58,7 @@ public class ConfigurationSource {
 
     private final InputStream stream;
     private volatile byte[] data;
-    private final Source source;
+    private volatile Source source;
     private final long lastModified;
     // Set when the configuration has been updated so reset can use it for the 
next lastModified timestamp.
     private volatile long modifiedMillis;
@@ -84,7 +80,7 @@ public class ConfigurationSource {
         } catch (Exception ex) {
             // There is a problem with the file. It will be handled somewhere 
else.
         }
-        this.modifiedMillis = this.lastModified = modified;
+        this.lastModified = modified;
     }
 
     /**
@@ -104,7 +100,7 @@ public class ConfigurationSource {
         } catch (Exception ex) {
             // There is a problem with the file. It will be handled somewhere 
else.
         }
-        this.modifiedMillis = this.lastModified = modified;
+        this.lastModified = modified;
     }
 
     /**
@@ -115,7 +111,10 @@ public class ConfigurationSource {
      * @param url the URL where the input stream originated
      */
     public ConfigurationSource(final InputStream stream, final URL url) {
-        this(stream, url, 0);
+        this.stream = Objects.requireNonNull(stream, "stream is null");
+        this.data = null;
+        this.lastModified = 0;
+        this.source = new Source(url);
     }
 
     /**
@@ -129,7 +128,7 @@ public class ConfigurationSource {
     public ConfigurationSource(final InputStream stream, final URL url, final 
long lastModified) {
         this.stream = Objects.requireNonNull(stream, "stream is null");
         this.data = null;
-        this.modifiedMillis = this.lastModified = lastModified;
+        this.lastModified = lastModified;
         this.source = new Source(url);
     }
 
@@ -155,15 +154,19 @@ public class ConfigurationSource {
         Objects.requireNonNull(source, "source is null");
         this.data = Objects.requireNonNull(data, "data is null");
         this.stream = new ByteArrayInputStream(data);
-        this.modifiedMillis = this.lastModified = lastModified;
+        this.lastModified = lastModified;
         this.source = source;
     }
 
     private ConfigurationSource(final byte[] data, final URL url, final long 
lastModified) {
         this.data = Objects.requireNonNull(data, "data is null");
         this.stream = new ByteArrayInputStream(data);
-        this.modifiedMillis = this.lastModified = lastModified;
-        this.source = url == null ? null : new Source(url);
+        this.lastModified = lastModified;
+        if (url == null) {
+            this.data = data;
+        } else {
+            this.source = new Source(url);
+        }
     }
 
     /**
@@ -196,8 +199,16 @@ public class ConfigurationSource {
         return source == null ? null : source.getFile();
     }
 
+    private boolean isFile() {
+        return source == null ? false : source.getFile() != null;
+    }
+
+    private boolean isURL() {
+        return source == null ? false : source.getURI() != null;
+    }
+
     private boolean isLocation() {
-        return source != null && source.getLocation() != null;
+        return source == null ? false : source.getLocation() != null;
     }
 
     /**
@@ -211,11 +222,11 @@ public class ConfigurationSource {
     }
 
     /**
-     * @deprecated Not used internally, no replacement.
+     * @deprecated Not used internally, no replacement. TODO remove and make 
source final.
      */
     @Deprecated
-    public void setSource(final Source ignored) {
-        LOGGER.warn("Ignoring call of deprecated method 
`ConfigurationSource#setSource()`.");
+    public void setSource(final Source source) {
+        this.source = source;
     }
 
     public void setData(final byte[] data) {
@@ -269,23 +280,16 @@ public class ConfigurationSource {
      */
     public ConfigurationSource resetInputStream() throws IOException {
         if (source != null && data != null) {
-            return new ConfigurationSource(source, data, modifiedMillis);
-        }
-        File file = getFile();
-        if (file != null) {
-            return new 
ConfigurationSource(Files.newInputStream(file.toPath()), getFile());
-        }
-        URL url = getURL();
-        if (url != null && data != null) {
+            return new ConfigurationSource(source, data, this.lastModified);
+        } else if (isFile()) {
+            return new ConfigurationSource(new FileInputStream(getFile()), 
getFile());
+        } else if (isURL() && data != null) {
             // Creates a ConfigurationSource without accessing the URL since 
the data was provided.
-            return new ConfigurationSource(data, url, modifiedMillis);
-        }
-        URI uri = getURI();
-        if (uri != null) {
-            return fromUri(uri);
-        }
-        if (data != null) {
-            return new ConfigurationSource(data, null, modifiedMillis);
+            return new ConfigurationSource(data, getURL(), modifiedMillis == 0 
? lastModified : modifiedMillis);
+        } else if (isURL()) {
+            return fromUri(getURI());
+        } else if (data != null) {
+            return new ConfigurationSource(data, null, lastModified);
         }
         return null;
     }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
index b6d7f57d66..08e840d93b 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
@@ -16,19 +16,16 @@
  */
 package org.apache.logging.log4j.core.config;
 
-import static java.util.Objects.requireNonNull;
-import static 
org.apache.logging.log4j.core.util.internal.HttpInputStreamUtil.readStream;
-import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
-
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.time.Instant;
 import java.util.List;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
 import org.apache.logging.log4j.core.util.AbstractWatcher;
 import org.apache.logging.log4j.core.util.AuthorizationProvider;
 import org.apache.logging.log4j.core.util.Source;
@@ -47,6 +44,7 @@ public class HttpWatcher extends AbstractWatcher {
 
     private final Logger LOGGER = StatusLogger.getLogger();
 
+    private final SslConfiguration sslConfiguration;
     private AuthorizationProvider authorizationProvider;
     private URL url;
     private volatile long lastModifiedMillis;
@@ -59,6 +57,7 @@ public class HttpWatcher extends AbstractWatcher {
             final List<ConfigurationListener> configurationListeners,
             final long lastModifiedMillis) {
         super(configuration, reconfigurable, configurationListeners);
+        sslConfiguration = SslConfigurationFactory.getSslConfiguration();
         this.lastModifiedMillis = lastModifiedMillis;
     }
 
@@ -104,50 +103,34 @@ public class HttpWatcher extends AbstractWatcher {
         try {
             final LastModifiedSource source = new 
LastModifiedSource(url.toURI(), lastModifiedMillis);
             final HttpInputStreamUtil.Result result = 
HttpInputStreamUtil.getInputStream(source, authorizationProvider);
-            // Update lastModifiedMillis
-            // https://github.com/apache/logging-log4j2/issues/2937
-            lastModifiedMillis = source.getLastModified();
-            // The result of the HTTP/HTTPS request is already logged at 
`DEBUG` by `HttpInputStreamUtil`
-            // We only log the important events at `INFO` or more.
             switch (result.getStatus()) {
                 case NOT_MODIFIED: {
+                    LOGGER.debug("Configuration Not Modified");
                     return false;
                 }
                 case SUCCESS: {
                     final ConfigurationSource configSource = 
getConfiguration().getConfigurationSource();
                     try {
-                        // In this case `result.getInputStream()` is not null.
-                        
configSource.setData(readStream(requireNonNull(result.getInputStream())));
+                        
configSource.setData(HttpInputStreamUtil.readStream(result.getInputStream()));
                         
configSource.setModifiedMillis(source.getLastModified());
-                        LOGGER.info(
-                                "{} resource at {} was modified on {}",
-                                () -> toRootUpperCase(url.getProtocol()),
-                                () -> url.toExternalForm(),
-                                () -> 
Instant.ofEpochMilli(source.getLastModified()));
+                        LOGGER.debug("Content was modified for {}", 
url.toString());
                         return true;
                     } catch (final IOException e) {
-                        // Dead code since result.getInputStream() is a 
ByteArrayInputStream
-                        LOGGER.error("Error accessing configuration at {}", 
url.toExternalForm(), e);
+                        LOGGER.error("Error accessing configuration at {}: 
{}", url, e.getMessage());
                         return false;
                     }
                 }
                 case NOT_FOUND: {
-                    LOGGER.warn(
-                            "{} resource at {} was not found",
-                            () -> toRootUpperCase(url.getProtocol()),
-                            () -> url.toExternalForm());
+                    LOGGER.info("Unable to locate configuration at {}", 
url.toString());
                     return false;
                 }
                 default: {
-                    LOGGER.warn(
-                            "Unexpected error retrieving {} resource at {}",
-                            () -> toRootUpperCase(url.getProtocol()),
-                            () -> url.toExternalForm());
+                    LOGGER.warn("Unexpected error accessing configuration at 
{}", url.toString());
                     return false;
                 }
             }
         } catch (final URISyntaxException ex) {
-            LOGGER.error("Bad configuration file URL {}", 
url.toExternalForm(), ex);
+            LOGGER.error("Bad configuration URL: {}, {}", url.toString(), 
ex.getMessage());
             return false;
         }
     }
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 31e42d843f..48502de22e 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
@@ -18,9 +18,9 @@ package org.apache.logging.log4j.core.config.xml;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.ByteArrayInputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -73,6 +73,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
             justification = "The `newDocumentBuilder` method disables DTD 
processing.")
     public XmlConfiguration(final LoggerContext loggerContext, final 
ConfigurationSource configSource) {
         super(loggerContext, configSource);
+        final File configFile = configSource.getFile();
         byte[] buffer = null;
 
         try {
@@ -174,7 +175,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
      *
      * @param xIncludeAware enabled XInclude
      * @return a new DocumentBuilder
-     * @throws ParserConfigurationException if a DocumentBuilder cannot be 
created, which satisfies the configuration requested.
+     * @throws ParserConfigurationException
      */
     static DocumentBuilder newDocumentBuilder(final boolean xIncludeAware) 
throws ParserConfigurationException {
         final DocumentBuilderFactory factory = 
DocumentBuilderFactory.newInstance();
@@ -240,7 +241,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
             return;
         }
         constructHierarchy(rootNode, rootElement);
-        if (!status.isEmpty()) {
+        if (status.size() > 0) {
             for (final Status s : status) {
                 LOGGER.error("Error processing element {} ({}): {}", s.name, 
s.element, s.errorType);
             }
@@ -294,7 +295,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
         }
 
         final String text = buffer.toString().trim();
-        if (!text.isEmpty() || (!node.hasChildren() && !node.isRoot())) {
+        if (text.length() > 0 || (!node.hasChildren() && !node.isRoot())) {
             node.setValue(text);
         }
     }
@@ -336,8 +337,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
 
     @Override
     public String toString() {
-        return getClass().getSimpleName() + "[location=" + 
getConfigurationSource() + ", lastModified="
-                + 
Instant.ofEpochMilli(getConfigurationSource().getLastModified()) + "]";
+        return getClass().getSimpleName() + "[location=" + 
getConfigurationSource() + "]";
     }
 
     /**
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java
index c7c9137715..3ceb4599b7 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java
@@ -20,9 +20,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.InputStream;
 import java.net.URI;
-import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -65,9 +65,6 @@ import org.apache.logging.log4j.util.PropertiesUtil;
 @PerformanceSensitive("allocation")
 public class MutableThreadContextMapFilter extends AbstractFilter {
 
-    private static final String HTTP = "http";
-    private static final String HTTPS = "https";
-
     private static final ObjectMapper MAPPER =
             new 
ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false);
     private static final KeyValuePair[] EMPTY_ARRAY = {};
@@ -367,29 +364,23 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
         @Override
         public void run() {
             final ConfigResult result = getConfig(source, 
authorizationProvider);
-            switch (result.status) {
-                case SUCCESS:
-                    filter = ThreadContextMapFilter.createFilter(result.pairs, 
"or", getOnMatch(), getOnMismatch());
-                    LOGGER.info("MutableThreadContextMapFilter configuration 
was updated: {}", filter.toString());
-                    break;
-                case NOT_FOUND:
-                    if (!(filter instanceof NoOpFilter)) {
-                        LOGGER.info("MutableThreadContextMapFilter 
configuration was removed");
-                        filter = new NoOpFilter();
-                    }
-                    break;
-                case EMPTY:
-                    LOGGER.debug("MutableThreadContextMapFilter configuration 
is empty");
+            if (result.status == Status.SUCCESS) {
+                filter = ThreadContextMapFilter.createFilter(result.pairs, 
"or", getOnMatch(), getOnMismatch());
+                LOGGER.info("Filter configuration was updated: {}", 
filter.toString());
+                for (FilterConfigUpdateListener listener : listeners) {
+                    listener.onEvent();
+                }
+            } else if (result.status == Status.NOT_FOUND) {
+                if (!(filter instanceof NoOpFilter)) {
+                    LOGGER.info("Filter configuration was removed");
                     filter = new NoOpFilter();
-                    break;
-            }
-            switch (result.status) {
-                case SUCCESS:
-                case NOT_FOUND:
-                case EMPTY:
                     for (FilterConfigUpdateListener listener : listeners) {
                         listener.onEvent();
                     }
+                }
+            } else if (result.status == Status.EMPTY) {
+                LOGGER.debug("Filter configuration is empty");
+                filter = new NoOpFilter();
             }
         }
     }
@@ -398,7 +389,7 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
             value = "PATH_TRAVERSAL_IN",
             justification = "The location of the file comes from a 
configuration value.")
     private static LastModifiedSource getSource(final String configLocation) {
-        LastModifiedSource source;
+        LastModifiedSource source = null;
         try {
             final URI uri = new URI(configLocation);
             if (uri.getScheme() != null) {
@@ -417,15 +408,14 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
             final LastModifiedSource source, final AuthorizationProvider 
authorizationProvider) {
         final File inputFile = source.getFile();
         InputStream inputStream = null;
-        HttpInputStreamUtil.Result result;
+        HttpInputStreamUtil.Result result = null;
         final long lastModified = source.getLastModified();
-        URI uri = source.getURI();
         if (inputFile != null && inputFile.exists()) {
             try {
                 final long modified = inputFile.lastModified();
                 if (modified > lastModified) {
                     source.setLastModified(modified);
-                    inputStream = Files.newInputStream(inputFile.toPath());
+                    inputStream = new FileInputStream(inputFile);
                     result = new HttpInputStreamUtil.Result(Status.SUCCESS);
                 } else {
                     result = new 
HttpInputStreamUtil.Result(Status.NOT_MODIFIED);
@@ -433,7 +423,7 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
             } catch (Exception ex) {
                 result = new HttpInputStreamUtil.Result(Status.ERROR);
             }
-        } else if (uri != null && (HTTP.equalsIgnoreCase(uri.getScheme()) || 
HTTPS.equalsIgnoreCase(uri.getScheme()))) {
+        } else if (source.getURI() != null) {
             try {
                 result = HttpInputStreamUtil.getInputStream(source, 
authorizationProvider);
                 inputStream = result.getInputStream();
@@ -450,7 +440,7 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
                 final KeyValuePairConfig keyValuePairConfig = 
MAPPER.readValue(inputStream, KeyValuePairConfig.class);
                 if (keyValuePairConfig != null) {
                     final Map<String, String[]> configs = 
keyValuePairConfig.getConfigs();
-                    if (configs != null && !configs.isEmpty()) {
+                    if (configs != null && configs.size() > 0) {
                         final List<KeyValuePair> pairs = new ArrayList<>();
                         for (Map.Entry<String, String[]> entry : 
configs.entrySet()) {
                             final String key = entry.getKey();
@@ -462,7 +452,7 @@ public class MutableThreadContextMapFilter extends 
AbstractFilter {
                                 }
                             }
                         }
-                        if (!pairs.isEmpty()) {
+                        if (pairs.size() > 0) {
                             configResult.pairs = pairs.toArray(EMPTY_ARRAY);
                             configResult.status = Status.SUCCESS;
                         } else {
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
index 7165bfe6f1..15e9009b75 100644
--- 
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
@@ -20,7 +20,6 @@ import aQute.bnd.annotation.Cardinality;
 import aQute.bnd.annotation.Resolution;
 import aQute.bnd.annotation.spi.ServiceConsumer;
 import java.io.File;
-import java.time.Instant;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -50,9 +49,8 @@ import org.apache.logging.log4j.util.ServiceLoaderUtil;
 @ServiceConsumer(value = WatchEventService.class, resolution = 
Resolution.OPTIONAL, cardinality = Cardinality.MULTIPLE)
 public class WatchManager extends AbstractLifeCycle {
 
-    private static final class ConfigurationMonitor {
+    private final class ConfigurationMonitor {
         private final Watcher watcher;
-        // Only used for logging
         private volatile long lastModifiedMillis;
 
         public ConfigurationMonitor(final long lastModifiedMillis, final 
Watcher watcher) {
@@ -78,6 +76,7 @@ public class WatchManager extends AbstractLifeCycle {
         private static final long LOW_MASK = 0xffffffffL;
         private static final long MID_MASK = 0xffff00000000L;
         private static final long HIGH_MASK = 0xfff000000000000L;
+        private static final int NODE_SIZE = 8;
         private static final int SHIFT_2 = 16;
         private static final int SHIFT_4 = 32;
         private static final int SHIFT_6 = 48;
@@ -85,6 +84,8 @@ public class WatchManager extends AbstractLifeCycle {
         private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 
0x01b21dd213814000L;
         private static final AtomicInteger COUNT = new AtomicInteger(0);
         private static final long TYPE1 = 0x1000L;
+        private static final byte VARIANT = (byte) 0x80;
+        private static final int SEQUENCE_MASK = 0x3FFF;
 
         public static UUID get() {
             final long time =
@@ -111,11 +112,15 @@ public class WatchManager extends AbstractLifeCycle {
                 final ConfigurationMonitor monitor = entry.getValue();
                 if (monitor.getWatcher().isModified()) {
                     final long lastModified = 
monitor.getWatcher().getLastModified();
-                    logger.info(
-                            "Configuration source at {} was modified on {}, 
previous modification was on {}",
-                            () -> source,
-                            () -> Instant.ofEpochMilli(lastModified),
-                            () -> 
Instant.ofEpochMilli(monitor.lastModifiedMillis));
+                    if (logger.isInfoEnabled()) {
+                        logger.info(
+                                "Source '{}' was modified on {} ({}), previous 
modification was on {} ({})",
+                                source,
+                                millisToString(lastModified),
+                                lastModified,
+                                millisToString(monitor.lastModifiedMillis),
+                                monitor.lastModifiedMillis);
+                    }
                     monitor.lastModifiedMillis = lastModified;
                     monitor.getWatcher().modified();
                 }
@@ -195,7 +200,7 @@ public class WatchManager extends AbstractLifeCycle {
     }
 
     public boolean hasEventListeners() {
-        return !eventServiceList.isEmpty();
+        return eventServiceList.size() > 0;
     }
 
     private String millisToString(final long millis) {
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/HttpInputStreamUtil.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/HttpInputStreamUtil.java
index 2c25ed2d25..c146958f9f 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/HttpInputStreamUtil.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/HttpInputStreamUtil.java
@@ -16,24 +16,17 @@
  */
 package org.apache.logging.log4j.core.util.internal;
 
-import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
-import java.time.Instant;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.config.ConfigurationException;
 import org.apache.logging.log4j.core.net.UrlConnectionFactory;
 import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
 import org.apache.logging.log4j.core.util.AuthorizationProvider;
-import org.apache.logging.log4j.core.util.Source;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.Supplier;
-import org.jspecify.annotations.NullMarked;
-import org.jspecify.annotations.Nullable;
 
 /**
  * Utility method for reading data from an HTTP InputStream.
@@ -43,21 +36,10 @@ public final class HttpInputStreamUtil {
     private static final Logger LOGGER = StatusLogger.getLogger();
     private static final int NOT_MODIFIED = 304;
     private static final int NOT_AUTHORIZED = 401;
-    private static final int FORBIDDEN = 403;
     private static final int NOT_FOUND = 404;
     private static final int OK = 200;
     private static final int BUF_SIZE = 1024;
 
-    /**
-     * Retrieves an HTTP resource if it has been modified.
-     * <p>
-     *     Side effects: if the request is successful, the last modified time 
of the {@code source}
-     *     parameter is modified.
-     * </p>
-     * @param source The location of the HTTP resource
-     * @param authorizationProvider The authentication data for the HTTP 
request
-     * @return A {@link Result} object containing the status code and body of 
the response
-     */
     public static Result getInputStream(
             final LastModifiedSource source, final AuthorizationProvider 
authorizationProvider) {
         final Result result = new Result();
@@ -73,16 +55,12 @@ public final class HttpInputStreamUtil {
                 final int code = connection.getResponseCode();
                 switch (code) {
                     case NOT_MODIFIED: {
-                        LOGGER.debug(
-                                "{} resource {}: not modified since {}",
-                                formatProtocol(source),
-                                () -> source,
-                                () -> Instant.ofEpochMilli(lastModified));
+                        LOGGER.debug("Configuration not modified");
                         result.status = Status.NOT_MODIFIED;
                         return result;
                     }
                     case NOT_FOUND: {
-                        LOGGER.debug("{} resource {}: not found", 
formatProtocol(source), () -> source);
+                        LOGGER.debug("Unable to access {}: Not Found", 
source.toString());
                         result.status = Status.NOT_FOUND;
                         return result;
                     }
@@ -90,65 +68,45 @@ public final class HttpInputStreamUtil {
                         try (final InputStream is = 
connection.getInputStream()) {
                             
source.setLastModified(connection.getLastModified());
                             LOGGER.debug(
-                                    "{} resource {}: last modified on {}",
-                                    formatProtocol(source),
-                                    () -> source,
-                                    () -> 
Instant.ofEpochMilli(connection.getLastModified()));
+                                    "Content was modified for {}. previous 
lastModified: {}, new lastModified: {}",
+                                    source.toString(),
+                                    lastModified,
+                                    connection.getLastModified());
                             result.status = Status.SUCCESS;
-                            result.bytes = readStream(is);
+                            result.inputStream = new 
ByteArrayInputStream(readStream(is));
                             return result;
                         } catch (final IOException e) {
                             try (final InputStream es = 
connection.getErrorStream()) {
-                                if (LOGGER.isDebugEnabled()) {
-                                    LOGGER.debug(
-                                            "Error accessing {} resource at 
{}: {}",
-                                            formatProtocol(source).get(),
-                                            source,
-                                            readStream(es),
-                                            e);
-                                }
+                                LOGGER.info(
+                                        "Error accessing configuration at {}: 
{}", source.toString(), readStream(es));
                             } catch (final IOException ioe) {
-                                LOGGER.debug(
-                                        "Error accessing {} resource at {}",
-                                        formatProtocol(source),
-                                        () -> source,
-                                        () -> e);
+                                LOGGER.error(
+                                        "Error accessing configuration at {}: 
{}", source.toString(), e.getMessage());
                             }
-                            throw new ConfigurationException("Unable to access 
" + source, e);
+                            throw new ConfigurationException("Unable to access 
" + source.toString(), e);
                         }
                     }
                     case NOT_AUTHORIZED: {
-                        throw new ConfigurationException("Authentication 
required for " + source);
-                    }
-                    case FORBIDDEN: {
-                        throw new ConfigurationException("Access denied to " + 
source);
+                        throw new ConfigurationException("Authorization 
failed");
                     }
                     default: {
                         if (code < 0) {
-                            LOGGER.debug("{} resource {}: invalid response 
code", formatProtocol(source), source);
+                            LOGGER.info("Invalid response code returned");
                         } else {
-                            LOGGER.debug(
-                                    "{} resource {}: unexpected response code 
{}",
-                                    formatProtocol(source),
-                                    source,
-                                    code);
+                            LOGGER.info("Unexpected response code returned 
{}", code);
                         }
-                        throw new ConfigurationException("Unable to access " + 
source);
+                        throw new ConfigurationException("Unable to access " + 
source.toString());
                     }
                 }
             } finally {
                 connection.disconnect();
             }
         } catch (IOException e) {
-            LOGGER.debug("Error accessing {} resource at {}", 
formatProtocol(source), source, e);
-            throw new ConfigurationException("Unable to access " + source, e);
+            LOGGER.warn("Error accessing {}: {}", source.toString(), 
e.getMessage());
+            throw new ConfigurationException("Unable to access " + 
source.toString(), e);
         }
     }
 
-    private static Supplier<String> formatProtocol(Source source) {
-        return () -> toRootUpperCase(source.getURI().getScheme());
-    }
-
     public static byte[] readStream(final InputStream is) throws IOException {
         final ByteArrayOutputStream result = new ByteArrayOutputStream();
         final byte[] buffer = new byte[BUF_SIZE];
@@ -159,29 +117,19 @@ public final class HttpInputStreamUtil {
         return result.toByteArray();
     }
 
-    @NullMarked
     public static class Result {
 
-        private byte @Nullable [] bytes = null;
+        private InputStream inputStream;
         private Status status;
 
-        public Result() {
-            this(Status.ERROR);
-        }
+        public Result() {}
 
         public Result(final Status status) {
             this.status = status;
         }
 
-        /**
-         * Returns the data if the status is {@link Status#SUCCESS}.
-         * <p>
-         *     In any other case the result is {@code null}.
-         * </p>
-         * @return The contents of the HTTP response or null if empty.
-         */
-        public @Nullable InputStream getInputStream() {
-            return bytes != null ? new ByteArrayInputStream(bytes) : null;
+        public InputStream getInputStream() {
+            return inputStream;
         }
 
         public Status getStatus() {
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 1faa00a710..e41e585c50 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -105,6 +105,7 @@
     <jconsole.version>1.7.0</jconsole.version>
     <jctools.version>4.0.5</jctools.version>
     <jeromq.version>0.6.0</jeromq.version>
+    <jetty.version>9.4.55.v20240627</jetty.version>
     <jmdns.version>3.5.12</jmdns.version>
     <jmh.version>1.37</jmh.version>
     <json-unit.version>2.40.1</json-unit.version>
@@ -130,6 +131,7 @@
     <spring-boot.version>2.7.18</spring-boot.version>
     <spring-framework.version>5.3.39</spring-framework.version>
     <system-stubs.version>2.0.3</system-stubs.version>
+    <tomcat-juli.version>10.0.27</tomcat-juli.version>
     <velocity.version>1.7</velocity.version>
     <wiremock.version>2.35.2</wiremock.version>
     <xmlunit.version>2.10.0</xmlunit.version>
@@ -195,6 +197,14 @@
         <scope>import</scope>
       </dependency>
 
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-bom</artifactId>
+        <version>${jetty.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
       <dependency>
         <groupId>org.junit</groupId>
         <artifactId>junit-bom</artifactId>
@@ -776,6 +786,12 @@
         <version>${system-stubs.version}</version>
       </dependency>
 
+      <dependency>
+        <groupId>org.apache.tomcat</groupId>
+        <artifactId>tomcat-juli</artifactId>
+        <version>${tomcat-juli.version}</version>
+      </dependency>
+
       <dependency>
         <groupId>org.apache.velocity</groupId>
         <artifactId>velocity</artifactId>
diff --git a/src/changelog/.2.x.x/2937-http-watcher.xml 
b/src/changelog/.2.x.x/2937-http-watcher.xml
deleted file mode 100644
index ec3e1a1426..0000000000
--- a/src/changelog/.2.x.x/2937-http-watcher.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-       xmlns="https://logging.apache.org/xml/ns";
-       xsi:schemaLocation="https://logging.apache.org/xml/ns 
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd";
-       type="fixed">
-  <issue id="2937" 
link="https://github.com/apache/logging-log4j2/issues/2937"/>
-  <description format="asciidoc">Fix reloading of the configuration from an 
HTTP(S) source</description>
-</entry>

Reply via email to