UpdaterRunnable.java
/*
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2026, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v2.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.classic.blackbox.joran;
import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone;
import java.io.File;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.fail;
class UpdaterRunnable extends RunnableWithCounterAndDone {
private final ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest;
File configFile;
ReconfigureOnChangeTaskTest.UpdateType updateType;
// it actually takes time for Windows to propagate file modification changes
// values below 100 milliseconds can be problematic the same propagation
// latency occurs in Linux but is even larger (>600 ms)
// final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 60;
final int sleepBetweenUpdates = 100;
UpdaterRunnable(ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest, File configFile, ReconfigureOnChangeTaskTest.UpdateType updateType) {
this.reconfigureOnChangeTaskTest = reconfigureOnChangeTaskTest;
this.configFile = configFile;
this.updateType = updateType;
}
UpdaterRunnable(ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest, File configFile) {
this(reconfigureOnChangeTaskTest, configFile, ReconfigureOnChangeTaskTest.UpdateType.TOUCH);
}
public void run() {
while (!isDone()) {
try {
Thread.sleep(sleepBetweenUpdates);
} catch (InterruptedException e) {
}
if (isDone()) {
reconfigureOnChangeTaskTest.addInfo("Exiting Updater.run()", this);
return;
}
counter++;
reconfigureOnChangeTaskTest.addInfo("Touching [" + configFile + "]", this);
switch (updateType) {
case TOUCH:
touchFile();
break;
case MALFORMED:
try {
malformedUpdate();
} catch (IOException e) {
e.printStackTrace();
fail("malformedUpdate failed");
}
break;
case MALFORMED_INNER:
try {
malformedInnerUpdate();
} catch (IOException e) {
e.printStackTrace();
fail("malformedInnerUpdate failed");
}
}
}
reconfigureOnChangeTaskTest.addInfo("Exiting Updater.run()", this);
}
private void malformedUpdate() throws IOException {
reconfigureOnChangeTaskTest.writeToFile(configFile,
"<configuration scan=\"true\" scanPeriod=\"50 millisecond\">\n" + " <root level=\"ERROR\">\n" + "</configuration>");
}
private void malformedInnerUpdate() throws IOException {
reconfigureOnChangeTaskTest.writeToFile(configFile, "<included>\n" + " <root>\n" + "</included>");
}
void touchFile() {
boolean result = configFile.setLastModified(System.currentTimeMillis());
if (!result)
reconfigureOnChangeTaskTest.addWarn(this.getClass().getName() + ".touchFile on " + configFile.toString() + " FAILED", this);
}
}