Skip to content

Commit

Permalink
Revert "Fix reloading of the configuration from HTTP(S)"
Browse files Browse the repository at this point in the history
This reverts commit be24f92.
  • Loading branch information
ppkarwasz committed Sep 22, 2024
1 parent 149dbe7 commit 421d8a7
Show file tree
Hide file tree
Showing 23 changed files with 810 additions and 916 deletions.
10 changes: 0 additions & 10 deletions log4j-appserver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,57 +32,50 @@
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());
}

/**
* 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);
}
}

@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);
Expand All @@ -98,7 +90,7 @@ public void testLoadConfigurationSourceFromJarFile() throws Exception {
assertEquals(expectedFdCount, getOpenFileDescriptorCount());
// This can only fail on Windows
try {
Files.delete(jarFile);
Files.delete(JAR_FILE);
} catch (IOException e) {
fail(e);
}
Expand All @@ -112,18 +104,22 @@ private long getOpenFileDescriptorCount() {
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");
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit 421d8a7

Please sign in to comment.