Skip to content

Commit d17ee13

Browse files
committed
Clean up log4j migration code, add more tests
Signed-off-by: Mitch Gaffigan <mitch.gaffigan@comcast.net>
1 parent 66d7b8f commit d17ee13

4 files changed

Lines changed: 152 additions & 59 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2026 Mitch Gaffigan <mitch@gaffigan.net>
3+
4+
package com.mirth.connect.server.launcher;
5+
6+
import java.io.File;
7+
import java.io.IOException;
8+
import java.nio.charset.StandardCharsets;
9+
import java.util.List;
10+
11+
import org.apache.commons.io.FileUtils;
12+
13+
public class Log4jMigrations {
14+
private static final String INVALID_LOG4J_PROPERTY = "dir.logs";
15+
16+
private Log4jMigrations() {}
17+
18+
public static void migrateConfiguration(File propertiesFile) {
19+
if (!propertiesFile.exists() || !propertiesFile.isFile()) {
20+
return;
21+
}
22+
23+
try {
24+
List<String> lines = FileUtils.readLines(propertiesFile, StandardCharsets.UTF_8);
25+
if (stripDirLogs(lines)) {
26+
FileUtils.writeLines(propertiesFile, StandardCharsets.UTF_8.name(), lines, System.lineSeparator(), false);
27+
}
28+
} catch (IOException e) {
29+
System.err.println("Failed to migrate Log4j configuration: " + propertiesFile.getAbsolutePath());
30+
e.printStackTrace();
31+
}
32+
}
33+
34+
private static boolean stripDirLogs(List<String> lines) {
35+
return lines.removeIf(line -> isPropertyLine(line, INVALID_LOG4J_PROPERTY));
36+
}
37+
38+
private static boolean isPropertyLine(String line, String propertyName) {
39+
String trimmedLine = line.trim();
40+
int equalsIndex = trimmedLine.indexOf('=');
41+
return equalsIndex >= 0 && trimmedLine.substring(0, equalsIndex).trim().equals(propertyName);
42+
}
43+
}

server/src/com/mirth/connect/server/launcher/MirthLauncher.java

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import java.io.IOException;
1616
import java.net.URL;
1717
import java.net.URLClassLoader;
18-
import java.nio.charset.StandardCharsets;
1918
import java.util.ArrayList;
2019
import java.util.Collection;
2120
import java.util.List;
@@ -47,7 +46,6 @@ public class MirthLauncher {
4746
private static final String[] LOG4J_JAR_FILES = { "./server-lib/log4j/log4j-core-2.25.3.jar",
4847
"./server-lib/log4j/log4j-api-2.25.3.jar",
4948
"./server-lib/log4j/log4j-1.2-api-2.25.3.jar" };
50-
private static final String INVALID_LOG4J_PROPERTY = "dir.logs";
5149

5250
private static String appDataDir = null;
5351

@@ -56,7 +54,7 @@ public class MirthLauncher {
5654
public static void main(String[] args) {
5755
JarFile mirthClientCoreJarFile = null;
5856
try {
59-
sanitizeLog4jConfiguration(new File(LOG4J_PROPERTIES_FILE));
57+
Log4jMigrations.migrateConfiguration(new File(LOG4J_PROPERTIES_FILE));
6058

6159
List<URL> classpathUrls = new ArrayList<>();
6260
// Always add log4j
@@ -133,28 +131,6 @@ public static void main(String[] args) {
133131
}
134132
}
135133

136-
private static void sanitizeLog4jConfiguration(File propertiesFile) {
137-
if (!propertiesFile.exists() || !propertiesFile.isFile()) {
138-
return;
139-
}
140-
141-
try {
142-
List<String> lines = FileUtils.readLines(propertiesFile, StandardCharsets.UTF_8);
143-
if (lines.removeIf(MirthLauncher::isInvalidLog4jPropertyLine)) {
144-
FileUtils.writeLines(propertiesFile, StandardCharsets.UTF_8.name(), lines, System.lineSeparator(), false);
145-
}
146-
} catch (IOException e) {
147-
System.err.println("Failed to sanitize Log4j configuration: " + propertiesFile.getAbsolutePath());
148-
e.printStackTrace();
149-
}
150-
}
151-
152-
private static boolean isInvalidLog4jPropertyLine(String line) {
153-
String trimmedLine = line.trim();
154-
int equalsIndex = trimmedLine.indexOf('=');
155-
return equalsIndex >= 0 && trimmedLine.substring(0, equalsIndex).trim().equals(INVALID_LOG4J_PROPERTY);
156-
}
157-
158134
// if we have an uninstall file, uninstall the listed extensions
159135
private static void uninstallPendingExtensions() throws Exception {
160136
File extensionsDir = new File(EXTENSIONS_DIR);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2026 Mitch Gaffigan <mitch@gaffigan.net>
3+
4+
package com.mirth.connect.server.launcher;
5+
6+
import static org.junit.Assert.assertEquals;
7+
import static org.junit.Assert.assertFalse;
8+
import static org.junit.Assert.assertTrue;
9+
10+
import java.io.ByteArrayOutputStream;
11+
import java.io.File;
12+
import java.io.PrintStream;
13+
import java.nio.charset.StandardCharsets;
14+
import java.nio.file.Files;
15+
import java.nio.file.Path;
16+
import java.nio.file.attribute.FileTime;
17+
import java.nio.file.attribute.PosixFilePermission;
18+
import java.util.EnumSet;
19+
import java.util.Set;
20+
21+
import org.junit.Assume;
22+
import org.junit.Test;
23+
24+
public class Log4jMigrationsTest {
25+
26+
@Test
27+
public void testMigrateLog4jRemovesDirLogs() throws Exception {
28+
File file = File.createTempFile("log4j2", ".properties");
29+
file.deleteOnExit();
30+
Files.writeString(file.toPath(), String.join(System.lineSeparator(),
31+
"rootLogger = ERROR,stdout,fout",
32+
"dir.logs = logs",
33+
"property.log.dir = logs",
34+
"appender.rolling.fileName = ${log.dir}/mirth.log"), StandardCharsets.UTF_8);
35+
36+
Log4jMigrations.migrateConfiguration(file);
37+
38+
String fileContents = Files.readString(file.toPath(), StandardCharsets.UTF_8);
39+
assertFalse(fileContents.contains("dir.logs"));
40+
assertTrue(fileContents.contains("property.log.dir = logs"));
41+
assertTrue(fileContents.contains("appender.rolling.fileName = ${log.dir}/mirth.log"));
42+
}
43+
44+
@Test
45+
public void testMigrateLog4jDoesNotRewriteCleanFile() throws Exception {
46+
File file = File.createTempFile("log4j2-clean", ".properties");
47+
file.deleteOnExit();
48+
String contents = String.join(System.lineSeparator(),
49+
"rootLogger = ERROR,stdout,fout",
50+
"property.log.dir = logs",
51+
"appender.rolling.fileName = ${log.dir}/mirth.log");
52+
Files.writeString(file.toPath(), contents, StandardCharsets.UTF_8);
53+
54+
FileTime expectedModifiedTime = FileTime.fromMillis(1_700_000_000_000L);
55+
Files.setLastModifiedTime(file.toPath(), expectedModifiedTime);
56+
57+
Log4jMigrations.migrateConfiguration(file);
58+
59+
assertEquals(expectedModifiedTime, Files.getLastModifiedTime(file.toPath()));
60+
assertEquals(contents, Files.readString(file.toPath(), StandardCharsets.UTF_8));
61+
}
62+
63+
@Test
64+
public void testMigrateLog4jFailsGracefullyWithReadOnlyFile() throws Exception {
65+
File file = File.createTempFile("log4j2-readonly", ".properties");
66+
file.deleteOnExit();
67+
String contents = String.join(System.lineSeparator(),
68+
"rootLogger = ERROR,stdout,fout",
69+
"dir.logs = logs",
70+
"property.log.dir = logs");
71+
Path path = file.toPath();
72+
Files.writeString(path, contents, StandardCharsets.UTF_8);
73+
74+
Assume.assumeTrue(Files.getFileStore(path).supportsFileAttributeView("posix"));
75+
76+
ByteArrayOutputStream errBytes = new ByteArrayOutputStream();
77+
PrintStream originalErr = System.err;
78+
Set<PosixFilePermission> originalPermissions = Files.getPosixFilePermissions(path);
79+
80+
try {
81+
Files.setPosixFilePermissions(path, EnumSet.of(PosixFilePermission.OWNER_READ));
82+
assertFalse(Files.isWritable(path));
83+
System.setErr(new PrintStream(errBytes, true, StandardCharsets.UTF_8.name()));
84+
85+
Log4jMigrations.migrateConfiguration(file);
86+
} finally {
87+
Files.setPosixFilePermissions(path, originalPermissions);
88+
System.setErr(originalErr);
89+
}
90+
91+
String errOutput = errBytes.toString(StandardCharsets.UTF_8.name());
92+
assertTrue(errOutput.contains("Failed to migrate Log4j configuration"));
93+
assertEquals(contents, Files.readString(path, StandardCharsets.UTF_8));
94+
}
95+
96+
@Test
97+
public void testMigrateLog4jIgnoresMissingFile() throws Exception {
98+
File parentDir = Files.createTempDirectory("log4j2-missing").toFile();
99+
parentDir.deleteOnExit();
100+
File file = new File(parentDir, "missing-log4j2.properties");
101+
102+
assertFalse(file.exists());
103+
104+
Log4jMigrations.migrateConfiguration(file);
105+
106+
assertFalse(file.exists());
107+
}
108+
}

server/test/com/mirth/connect/server/launcher/MirthLauncherTest.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)