Improve automatic configuration upgrades

This commit is contained in:
filoghost 2020-07-23 16:57:51 +02:00
parent d13f3ec68f
commit 1e0c4caecc
15 changed files with 250 additions and 175 deletions

View File

@ -166,10 +166,16 @@ public class ChestCommands extends BaseJavaPlugin {
return errorCollector; return errorCollector;
} }
UpgradesExecutor upgradeExecutor = new UpgradesExecutor(configManager);
try { try {
new UpgradesExecutor(configManager).run(isFreshInstall, errorCollector); boolean allUpgradesSuccessful = upgradeExecutor.run(isFreshInstall, errorCollector);
if (!allUpgradesSuccessful) {
errorCollector.add(ErrorMessages.Upgrade.failedSomeUpgrades);
}
} catch (UpgradeExecutorException e) { } catch (UpgradeExecutorException e) {
errorCollector.add(ErrorMessages.Upgrade.genericExecutorError, e); errorCollector.add(ErrorMessages.Upgrade.genericExecutorError, e);
errorCollector.add(ErrorMessages.Upgrade.failedSomeUpgrades);
} }
settings = configManager.tryLoadSettings(errorCollector); settings = configManager.tryLoadSettings(errorCollector);

View File

@ -0,0 +1,36 @@
package me.filoghost.chestcommands.legacy;
import me.filoghost.chestcommands.util.Preconditions;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Backup {
private final Path dataFolder;
private final Path backupFolder;
public Backup(Path dataFolder, String backupName) {
this.dataFolder = dataFolder;
this.backupFolder = dataFolder.resolve("backups").resolve(backupName);
}
public static Backup newTimestampedBackup(Path dataFolder) {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd-HH.mm"));
String backupName = "backup_" + date;
return new Backup(dataFolder, backupName);
}
public void backupFile(Path fileToBackup) throws IOException {
Preconditions.checkArgument(fileToBackup.startsWith(dataFolder), "file is not inside data folder");
Path destination = backupFolder.resolve(dataFolder.relativize(fileToBackup));
Files.createDirectories(destination.getParent());
if (!Files.isRegularFile(destination)) {
Files.copy(fileToBackup, destination);
}
}
}

View File

@ -0,0 +1,111 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.filoghost.chestcommands.legacy;
import me.filoghost.chestcommands.config.ConfigManager;
import me.filoghost.chestcommands.config.framework.ConfigLoader;
import me.filoghost.chestcommands.legacy.upgrade.LangUpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.MenuNodeExpandUpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.MenuNodeRenameUpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.PlaceholdersYamlUpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.SettingsUpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.UpgradeTask;
import me.filoghost.chestcommands.legacy.upgrade.UpgradeTaskException;
import me.filoghost.chestcommands.logging.ErrorMessages;
import me.filoghost.chestcommands.util.collection.CollectionUtils;
import me.filoghost.chestcommands.util.logging.Log;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
public enum Upgrade {
V4_MENUS_RENAME("v4.0-menus-rename", (configManager) -> {
List<Path> menuFiles = getMenuFiles(configManager);
return CollectionUtils.transform(menuFiles,
MenuNodeRenameUpgradeTask::new);
}),
V4_MENUS_REFORMAT("v4.0-menus-reformat", (configManager) -> {
String legacyCommandSeparator = readLegacyCommandSeparator(configManager);
List<Path> menuFiles = getMenuFiles(configManager);
return CollectionUtils.transform(menuFiles,
file -> new MenuNodeExpandUpgradeTask(configManager, file, legacyCommandSeparator));
}),
V4_CONFIG("v4.0-config", (configManager) -> {
return Collections.singletonList(new SettingsUpgradeTask(configManager));
}),
V4_PLACEHOLDERS("v4.0-placeholders", (configManager) -> {
return Collections.singletonList(new PlaceholdersYamlUpgradeTask(configManager));
}),
V4_LANG("v4.0-lang", (configManager) -> {
return Collections.singletonList(new LangUpgradeTask(configManager));
});
private final String id;
private final UpgradeTasksSupplier upgradeTasksSupplier;
Upgrade(String id, UpgradeTasksSupplier upgradeTasksSupplier) {
this.id = id;
this.upgradeTasksSupplier = upgradeTasksSupplier;
}
public String getID() {
return id;
}
public List<UpgradeTask> createUpgradeTasks(ConfigManager configManager) throws UpgradeTaskException {
return upgradeTasksSupplier.getTasks(configManager);
}
private static List<Path> getMenuFiles(ConfigManager configManager) throws UpgradeTaskException {
try {
return configManager.getMenuPaths();
} catch (IOException e) {
throw new UpgradeTaskException(ErrorMessages.Upgrade.menuListIOException, e);
}
}
private static String readLegacyCommandSeparator(ConfigManager configManager) {
ConfigLoader settingsConfigLoader = configManager.getConfigLoader("config.yml");
if (!settingsConfigLoader.fileExists()) {
return null;
}
try {
return settingsConfigLoader.load().getString("multiple-commands-separator");
} catch (Throwable t) {
Log.warning("Failed to load \"" + settingsConfigLoader.getFile() + "\", assuming default command separator \";\".");
return null;
}
}
@FunctionalInterface
interface UpgradeTasksSupplier {
List<UpgradeTask> getTasks(ConfigManager configManager) throws UpgradeTaskException;
}
}

View File

@ -42,19 +42,19 @@ public class UpgradesDoneRegistry {
} }
public void setAllDone() { public void setAllDone() {
for (UpgradeID upgrade : UpgradeID.values()) { for (Upgrade upgrade : Upgrade.values()) {
setDone(upgrade); setDone(upgrade);
} }
} }
public void setDone(UpgradeID upgrade) { public void setDone(Upgrade upgrade) {
if (upgradesDone.add(upgrade.stringID)) { if (upgradesDone.add(upgrade.getID())) {
needSave = true; needSave = true;
} }
} }
public boolean isDone(UpgradeID upgrade) { public boolean isDone(Upgrade upgrade) {
return upgradesDone.contains(upgrade.stringID); return upgradesDone.contains(upgrade.getID());
} }
public void save() throws IOException { public void save() throws IOException {
@ -70,20 +70,4 @@ public class UpgradesDoneRegistry {
} }
} }
public enum UpgradeID {
V4_MENU_REPLACE("v4.0-menus-rename"),
V4_MENUS_REFORMAT("v4.0-menus-reformat"),
V4_CONFIG("v4.0-config"),
V4_PLACEHOLDERS("v4.0-placeholders"),
V4_LANG("v4.0-lang");
private final String stringID;
UpgradeID(String stringID) {
this.stringID = stringID;
}
}
} }

View File

@ -15,40 +15,28 @@
package me.filoghost.chestcommands.legacy; package me.filoghost.chestcommands.legacy;
import me.filoghost.chestcommands.config.ConfigManager; import me.filoghost.chestcommands.config.ConfigManager;
import me.filoghost.chestcommands.config.framework.ConfigLoader; import me.filoghost.chestcommands.legacy.upgrade.UpgradeTask;
import me.filoghost.chestcommands.legacy.UpgradesDoneRegistry.UpgradeID; import me.filoghost.chestcommands.legacy.upgrade.UpgradeTaskException;
import me.filoghost.chestcommands.legacy.upgrade.LangUpgrade;
import me.filoghost.chestcommands.legacy.upgrade.MenuNodeExpandUpgrade;
import me.filoghost.chestcommands.legacy.upgrade.MenuNodeRenameUpgrade;
import me.filoghost.chestcommands.legacy.upgrade.PlaceholdersYamlUpgrade;
import me.filoghost.chestcommands.legacy.upgrade.SettingsUpgrade;
import me.filoghost.chestcommands.legacy.upgrade.Upgrade;
import me.filoghost.chestcommands.legacy.upgrade.UpgradeException;
import me.filoghost.chestcommands.logging.ErrorMessages; import me.filoghost.chestcommands.logging.ErrorMessages;
import me.filoghost.chestcommands.util.collection.CollectionUtils;
import me.filoghost.chestcommands.util.logging.ErrorCollector; import me.filoghost.chestcommands.util.logging.ErrorCollector;
import me.filoghost.chestcommands.util.logging.Log; import me.filoghost.chestcommands.util.logging.Log;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class UpgradesExecutor { public class UpgradesExecutor {
private final ConfigManager configManager; private final ConfigManager configManager;
private Set<Path> failedUpgrades; private boolean allUpgradesSuccessful;
private UpgradesDoneRegistry upgradesDoneRegistry; private UpgradesDoneRegistry upgradesDoneRegistry;
public UpgradesExecutor(ConfigManager configManager) { public UpgradesExecutor(ConfigManager configManager) {
this.configManager = configManager; this.configManager = configManager;
} }
public void run(boolean isFreshInstall, ErrorCollector errorCollector) throws UpgradeExecutorException { public boolean run(boolean isFreshInstall, ErrorCollector errorCollector) throws UpgradeExecutorException {
this.failedUpgrades = new HashSet<>(); this.allUpgradesSuccessful = true;
Path upgradesDoneFile = configManager.getRootDataFolder().resolve(".upgrades-done"); Path upgradesDoneFile = configManager.getRootDataFolder().resolve(".upgrades-done");
try { try {
@ -61,99 +49,64 @@ public class UpgradesExecutor {
if (isFreshInstall) { if (isFreshInstall) {
// Mark all currently existing upgrades as already done, assuming default configuration files are up to date // Mark all currently existing upgrades as already done, assuming default configuration files are up to date
upgradesDoneRegistry.setAllDone(); upgradesDoneRegistry.setAllDone();
} else { } else {
String legacyCommandSeparator; // Run missing upgrades
if (!upgradesDoneRegistry.isDone(UpgradeID.V4_MENUS_REFORMAT)) { Backup backup = Backup.newTimestampedBackup(configManager.getRootDataFolder());
legacyCommandSeparator = readLegacyCommandSeparator(); runMissingUpgrades(backup, errorCollector);
} else {
legacyCommandSeparator = null;
}
runIfNecessary(UpgradeID.V4_CONFIG, () -> new SettingsUpgrade(configManager), errorCollector);
runIfNecessary(UpgradeID.V4_PLACEHOLDERS, () -> new PlaceholdersYamlUpgrade(configManager), errorCollector);
runIfNecessary(UpgradeID.V4_LANG, () -> new LangUpgrade(configManager), errorCollector);
try {
List<Path> menuFiles = configManager.getMenuPaths();
runIfNecessaryMultiple(UpgradeID.V4_MENU_REPLACE, () -> {
return CollectionUtils.transform(menuFiles,
MenuNodeRenameUpgrade::new);
}, errorCollector);
runIfNecessaryMultiple(UpgradeID.V4_MENUS_REFORMAT, () -> {
return CollectionUtils.transform(menuFiles,
file -> new MenuNodeExpandUpgrade(configManager, file, legacyCommandSeparator));
}, errorCollector);
} catch (IOException e) {
errorCollector.add(ErrorMessages.Upgrade.menuListIOException, e);
failedUpgrades.add(configManager.getMenusFolder());
}
} }
try { try {
upgradesDoneRegistry.save(); upgradesDoneRegistry.save();
} catch (IOException e) { } catch (IOException e) {
// Upgrades can't proceed if metadata file is not read correctly // Upgrades can't proceed if metadata file is not saved correctly
throw new UpgradeExecutorException(ErrorMessages.Upgrade.metadataSaveError(upgradesDoneFile), e); throw new UpgradeExecutorException(ErrorMessages.Upgrade.metadataSaveError(upgradesDoneFile), e);
} }
// Success only if no upgrade failed return allUpgradesSuccessful;
if (!failedUpgrades.isEmpty()) { }
throw new UpgradeExecutorException(ErrorMessages.Upgrade.failedUpgradesList(failedUpgrades));
private void runMissingUpgrades(Backup backup, ErrorCollector errorCollector) {
for (Upgrade upgrade : Upgrade.values()) {
if (!upgradesDoneRegistry.isDone(upgrade)) {
boolean allTasksSuccessful = tryRunUpgradeTasks(upgrade, backup, errorCollector);
// Consider an upgrade "done" if all its tasks were completed successfully
if (allTasksSuccessful) {
upgradesDoneRegistry.setDone(upgrade);
} else {
allUpgradesSuccessful = false;
}
}
} }
} }
private String readLegacyCommandSeparator() {
ConfigLoader settingsConfigLoader = configManager.getConfigLoader("config.yml");
if (!settingsConfigLoader.fileExists()) { private boolean tryRunUpgradeTasks(Upgrade upgrade, Backup backup, ErrorCollector errorCollector) {
return null; boolean allTasksSuccessful = true;
}
List<UpgradeTask> upgradeTasks;
try { try {
return settingsConfigLoader.load().getString("multiple-commands-separator"); upgradeTasks = upgrade.createUpgradeTasks(configManager);
} catch (Throwable t) { } catch (UpgradeTaskException e) {
Log.warning("Failed to load \"" + settingsConfigLoader.getFile() + "\", assuming default command separator \";\"."); errorCollector.add(ErrorMessages.Upgrade.failedToPrepareUpgradeTasks, e);
return null; return false;
}
}
private void runIfNecessary(UpgradeID upgradeID, Supplier<Upgrade> upgradeTask, ErrorCollector errorCollector) {
runIfNecessaryMultiple(upgradeID, () -> Collections.singletonList(upgradeTask.get()), errorCollector);
}
private void runIfNecessaryMultiple(UpgradeID upgradeID, Supplier<List<? extends Upgrade>> upgradeTasks, ErrorCollector errorCollector) {
if (upgradesDoneRegistry.isDone(upgradeID)) {
return;
} }
boolean failedAnyUpgrade = false; for (UpgradeTask upgradeTask : upgradeTasks) {
for (Upgrade upgradeTask : upgradeTasks.get()) {
try { try {
boolean modified = upgradeTask.backupAndUpgradeIfNecessary(); boolean modified = upgradeTask.runAndBackupIfNecessary(backup);
if (modified) { if (modified) {
Log.info( Log.info("Automatically upgraded configuration file \"" + upgradeTask.getUpgradedFile() + "\". "
"Automatically upgraded configuration file \""
+ upgradeTask.getUpgradedFile() + "\" with newer configuration nodes. "
+ "A backup of the old file has been saved."); + "A backup of the old file has been saved.");
} }
} catch (UpgradeException e) { } catch (UpgradeTaskException e) {
failedAnyUpgrade = true; allTasksSuccessful = false;
failedUpgrades.add(upgradeTask.getOriginalFile());
errorCollector.add(ErrorMessages.Upgrade.failedSingleUpgrade(upgradeTask.getOriginalFile()), e); errorCollector.add(ErrorMessages.Upgrade.failedSingleUpgrade(upgradeTask.getOriginalFile()), e);
} }
} }
// Upgrade ID is considered complete only if all relative upgrades tasks are successful return allTasksSuccessful;
if (!failedAnyUpgrade) {
upgradesDoneRegistry.setDone(upgradeID);
}
} }
} }

View File

@ -18,9 +18,9 @@ import me.filoghost.chestcommands.config.ConfigManager;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class LangUpgrade extends RegexUpgrade { public class LangUpgradeTask extends RegexUpgradeTask {
public LangUpgrade(ConfigManager configManager) { public LangUpgradeTask(ConfigManager configManager) {
super(configManager.getRootDataFolder().resolve("lang.yml")); super(configManager.getRootDataFolder().resolve("lang.yml"));
addRegexReplacer(Pattern.compile(Pattern.quote("{datavalue}")), matcher -> "{durability}"); addRegexReplacer(Pattern.compile(Pattern.quote("{datavalue}")), matcher -> "{durability}");

View File

@ -30,13 +30,13 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class MenuNodeExpandUpgrade extends Upgrade { public class MenuNodeExpandUpgradeTask extends UpgradeTask {
private final ConfigLoader menuConfigLoader; private final ConfigLoader menuConfigLoader;
private final String legacyCommandSeparator; private final String legacyCommandSeparator;
private Config updatedConfig; private Config updatedConfig;
public MenuNodeExpandUpgrade(ConfigManager configManager, Path menuFile, String legacyCommandSeparator) { public MenuNodeExpandUpgradeTask(ConfigManager configManager, Path menuFile, String legacyCommandSeparator) {
this.menuConfigLoader = configManager.getConfigLoader(menuFile); this.menuConfigLoader = configManager.getConfigLoader(menuFile);
this.legacyCommandSeparator = legacyCommandSeparator; this.legacyCommandSeparator = legacyCommandSeparator;
} }
@ -52,7 +52,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
} }
@Override @Override
protected void computeChanges() throws ConfigLoadException { public void computeChanges() throws ConfigLoadException {
Config menuConfig = menuConfigLoader.load(); Config menuConfig = menuConfigLoader.load();
menuConfig.setHeader(null); menuConfig.setHeader(null);
@ -74,7 +74,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
} }
@Override @Override
protected void saveChanges() throws ConfigSaveException { public void saveChanges() throws ConfigSaveException {
menuConfigLoader.save(updatedConfig); menuConfigLoader.save(updatedConfig);
} }
@ -110,7 +110,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
} }
material = parts[0]; material = parts[0];
section.set(IconSettingsNode.MATERIAL, material); section.set(IconSettingsNode.MATERIAL, material);
setModified(); setSaveRequired();
} }
if (material.contains(":")) { if (material.contains(":")) {
@ -124,7 +124,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
} }
material = parts[0]; material = parts[0];
section.set(IconSettingsNode.MATERIAL, material); section.set(IconSettingsNode.MATERIAL, material);
setModified(); setSaveRequired();
} }
} }
@ -132,7 +132,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
if (config.isSet(node)) { if (config.isSet(node)) {
if (config.isString(node)) { if (config.isString(node)) {
config.set(node, getSeparatedValues(config.getString(node), separator)); config.set(node, getSeparatedValues(config.getString(node), separator));
setModified(); setSaveRequired();
} }
} }
} }
@ -140,7 +140,7 @@ public class MenuNodeExpandUpgrade extends Upgrade {
private void expandSingletonList(ConfigSection config, String node) { private void expandSingletonList(ConfigSection config, String node) {
if (config.isSet(node)) { if (config.isSet(node)) {
config.set(node, Collections.singletonList(config.get(node))); config.set(node, Collections.singletonList(config.get(node)));
setModified(); setSaveRequired();
} }
} }

View File

@ -19,9 +19,9 @@ import me.filoghost.chestcommands.parsing.icon.IconSettingsNode;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class MenuNodeRenameUpgrade extends RegexUpgrade { public class MenuNodeRenameUpgradeTask extends RegexUpgradeTask {
public MenuNodeRenameUpgrade(Path menuFile) { public MenuNodeRenameUpgradeTask(Path menuFile) {
super(menuFile); super(menuFile);
addSubNodeReplacer("command", "commands"); addSubNodeReplacer("command", "commands");

View File

@ -28,13 +28,13 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
public class PlaceholdersYamlUpgrade extends Upgrade { public class PlaceholdersYamlUpgradeTask extends UpgradeTask {
private final Path oldPlaceholdersFile; private final Path oldPlaceholdersFile;
private final ConfigLoader newPlaceholdersConfigLoader; private final ConfigLoader newPlaceholdersConfigLoader;
private Config updatedConfig; private Config updatedConfig;
public PlaceholdersYamlUpgrade(ConfigManager configManager) { public PlaceholdersYamlUpgradeTask(ConfigManager configManager) {
this.oldPlaceholdersFile = configManager.getRootDataFolder().resolve("placeholders.yml"); this.oldPlaceholdersFile = configManager.getRootDataFolder().resolve("placeholders.yml");
this.newPlaceholdersConfigLoader = configManager.getConfigLoader("custom-placeholders.yml"); this.newPlaceholdersConfigLoader = configManager.getConfigLoader("custom-placeholders.yml");
} }
@ -50,7 +50,7 @@ public class PlaceholdersYamlUpgrade extends Upgrade {
} }
@Override @Override
protected void computeChanges() throws ConfigLoadException { public void computeChanges() throws ConfigLoadException {
if (!Files.isRegularFile(oldPlaceholdersFile)) { if (!Files.isRegularFile(oldPlaceholdersFile)) {
return; return;
} }
@ -80,14 +80,14 @@ public class PlaceholdersYamlUpgrade extends Upgrade {
String replacement = StringEscapeUtils.unescapeJava(unquote(parts[1])); String replacement = StringEscapeUtils.unescapeJava(unquote(parts[1]));
newPlaceholdersConfig.set(placeholder, replacement); newPlaceholdersConfig.set(placeholder, replacement);
setModified(); setSaveRequired();
} }
this.updatedConfig = newPlaceholdersConfig; this.updatedConfig = newPlaceholdersConfig;
} }
@Override @Override
protected void saveChanges() throws ConfigSaveException { public void saveChanges() throws ConfigSaveException {
try { try {
Files.deleteIfExists(oldPlaceholdersFile); Files.deleteIfExists(oldPlaceholdersFile);
} catch (IOException ignored) {} } catch (IOException ignored) {}

View File

@ -30,13 +30,13 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public class RegexUpgrade extends Upgrade { public class RegexUpgradeTask extends UpgradeTask {
private final Path file; private final Path file;
private final List<RegexReplacer> replacers; private final List<RegexReplacer> replacers;
private List<String> newContents; private List<String> newContents;
public RegexUpgrade(Path file) { public RegexUpgradeTask(Path file) {
this.file = file; this.file = file;
this.replacers = new ArrayList<>(); this.replacers = new ArrayList<>();
} }
@ -56,7 +56,7 @@ public class RegexUpgrade extends Upgrade {
} }
@Override @Override
protected void computeChanges() throws ConfigLoadException { public void computeChanges() throws ConfigLoadException {
if (!Files.isRegularFile(file)) { if (!Files.isRegularFile(file)) {
return; return;
} }
@ -76,12 +76,12 @@ public class RegexUpgrade extends Upgrade {
newContents = linesStream.collect(Collectors.toList()); newContents = linesStream.collect(Collectors.toList());
if (!newContents.equals(lines)) { if (!newContents.equals(lines)) {
setModified(); setSaveRequired();
} }
} }
@Override @Override
protected void saveChanges() throws ConfigSaveException { public void saveChanges() throws ConfigSaveException {
try { try {
Files.write(file, newContents); Files.write(file, newContents);
} catch (IOException e) { } catch (IOException e) {

View File

@ -22,12 +22,12 @@ import me.filoghost.chestcommands.config.framework.exception.ConfigSaveException
import java.nio.file.Path; import java.nio.file.Path;
public class SettingsUpgrade extends Upgrade { public class SettingsUpgradeTask extends UpgradeTask {
private final ConfigLoader settingsConfigLoader; private final ConfigLoader settingsConfigLoader;
private Config updatedConfig; private Config updatedConfig;
public SettingsUpgrade(ConfigManager configManager) { public SettingsUpgradeTask(ConfigManager configManager) {
this.settingsConfigLoader = configManager.getConfigLoader("config.yml"); this.settingsConfigLoader = configManager.getConfigLoader("config.yml");
} }
@ -42,7 +42,7 @@ public class SettingsUpgrade extends Upgrade {
} }
@Override @Override
protected void computeChanges() throws ConfigLoadException { public void computeChanges() throws ConfigLoadException {
if (!settingsConfigLoader.fileExists()) { if (!settingsConfigLoader.fileExists()) {
return; return;
} }
@ -58,13 +58,13 @@ public class SettingsUpgrade extends Upgrade {
private void removeNode(Config config, String node) { private void removeNode(Config config, String node) {
if (config.isSet(node)) { if (config.isSet(node)) {
config.set(node, null); config.set(node, null);
setModified(); setSaveRequired();
} }
} }
@Override @Override
protected void saveChanges() throws ConfigSaveException { public void saveChanges() throws ConfigSaveException {
settingsConfigLoader.save(updatedConfig); settingsConfigLoader.save(updatedConfig);
} }
} }

View File

@ -16,58 +16,50 @@ package me.filoghost.chestcommands.legacy.upgrade;
import me.filoghost.chestcommands.config.framework.exception.ConfigLoadException; import me.filoghost.chestcommands.config.framework.exception.ConfigLoadException;
import me.filoghost.chestcommands.config.framework.exception.ConfigSaveException; import me.filoghost.chestcommands.config.framework.exception.ConfigSaveException;
import me.filoghost.chestcommands.legacy.Backup;
import me.filoghost.chestcommands.logging.ErrorMessages; import me.filoghost.chestcommands.logging.ErrorMessages;
import me.filoghost.chestcommands.util.Preconditions; import me.filoghost.chestcommands.util.Preconditions;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public abstract class Upgrade { public abstract class UpgradeTask {
private boolean modified; private boolean saveRequired;
private boolean hasRun; private boolean hasRun;
protected void setModified() { protected void setSaveRequired() {
this.modified = true; this.saveRequired = true;
} }
public boolean runAndBackupIfNecessary(Backup backup) throws UpgradeTaskException {
public boolean backupAndUpgradeIfNecessary() throws UpgradeException { Preconditions.checkState(!hasRun, "Upgrade task has already run");
Preconditions.checkState(!hasRun, "Upgrade can only be run once");
hasRun = true; hasRun = true;
try { try {
computeChanges(); computeChanges();
} catch (ConfigLoadException e) { } catch (ConfigLoadException e) {
throw new UpgradeException(ErrorMessages.Upgrade.loadError(getOriginalFile()), e); throw new UpgradeTaskException(ErrorMessages.Upgrade.loadError(getOriginalFile()), e);
} }
if (modified) { if (saveRequired) {
try { try {
createBackupFile(getOriginalFile()); backup.backupFile(getOriginalFile());
} catch (IOException e) { } catch (IOException e) {
throw new UpgradeException(ErrorMessages.Upgrade.backupError(getOriginalFile()), e); throw new UpgradeTaskException(ErrorMessages.Upgrade.backupError(getOriginalFile()), e);
} }
try { try {
saveChanges(); saveChanges();
} catch (ConfigSaveException e) { } catch (ConfigSaveException e) {
throw new UpgradeException(ErrorMessages.Upgrade.saveError(getUpgradedFile()), e); throw new UpgradeTaskException(ErrorMessages.Upgrade.saveError(getUpgradedFile()), e);
} }
return true;
} else {
return false;
} }
return modified;
}
private void createBackupFile(Path path) throws IOException {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd-HH.mm"));
String backupName = path.getFileName() + "_" + date + ".backup";
Files.copy(path, path.resolveSibling(backupName), StandardCopyOption.REPLACE_EXISTING);
} }
public abstract Path getOriginalFile(); public abstract Path getOriginalFile();

View File

@ -14,9 +14,9 @@
*/ */
package me.filoghost.chestcommands.legacy.upgrade; package me.filoghost.chestcommands.legacy.upgrade;
public class UpgradeException extends Exception { public class UpgradeTaskException extends Exception {
public UpgradeException(String message, Throwable cause) { public UpgradeTaskException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@ -18,8 +18,6 @@ import me.filoghost.chestcommands.config.framework.mapped.MappedConfig;
import me.filoghost.chestcommands.parsing.icon.IconSettings; import me.filoghost.chestcommands.parsing.icon.IconSettings;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;
public class ErrorMessages { public class ErrorMessages {
@ -72,9 +70,10 @@ public class ErrorMessages {
public static class Upgrade { public static class Upgrade {
public static final String genericExecutorError = "encountered errors while running run automatic configuration upgrades, " public static final String genericExecutorError = "error while running automatic configuration upgrades";
+ "some configuration files or menus may require manual updates."; public static final String menuListIOException = "couldn't obtain a list of menu files";
public static final String menuListIOException = "couldn't obtain a list of menu files, some automatic upgrades were skipped"; public static final String failedSomeUpgrades = "note: one or more automatic upgrades may have not been applied, configuration files or menus may require manual changes";
public static final String failedToPrepareUpgradeTasks = "error while trying to prepare an automatic configuration upgrade";
public static String metadataReadError(Path metadataFile) { public static String metadataReadError(Path metadataFile) {
return "couldn't read upgrades metadata file \"" + formatPath(metadataFile) + "\""; return "couldn't read upgrades metadata file \"" + formatPath(metadataFile) + "\"";
@ -88,13 +87,6 @@ public class ErrorMessages {
return "error while trying to automatically upgrade \"" + formatPath(file) + "\""; return "error while trying to automatically upgrade \"" + formatPath(file) + "\"";
} }
public static String failedUpgradesList(Set<Path> failedUpgrades) {
String failedConversionFiles = failedUpgrades.stream()
.map(path -> "\"" + path + "\"")
.collect(Collectors.joining(", "));
return "failed to automatically upgrade the following files: " + failedConversionFiles;
}
public static String loadError(Path file) { public static String loadError(Path file) {
return "couldn't load file to upgrade \"" + formatPath(file) + "\""; return "couldn't load file to upgrade \"" + formatPath(file) + "\"";
} }
@ -106,6 +98,7 @@ public class ErrorMessages {
public static String saveError(Path file) { public static String saveError(Path file) {
return "couldn't save upgraded file \"" + formatPath(file) + "\""; return "couldn't save upgraded file \"" + formatPath(file) + "\"";
} }
} }

View File

@ -18,7 +18,7 @@ import me.filoghost.chestcommands.ChestCommands;
import me.filoghost.chestcommands.config.framework.exception.ConfigException; import me.filoghost.chestcommands.config.framework.exception.ConfigException;
import me.filoghost.chestcommands.config.framework.exception.ConfigSyntaxException; import me.filoghost.chestcommands.config.framework.exception.ConfigSyntaxException;
import me.filoghost.chestcommands.legacy.UpgradeExecutorException; import me.filoghost.chestcommands.legacy.UpgradeExecutorException;
import me.filoghost.chestcommands.legacy.upgrade.UpgradeException; import me.filoghost.chestcommands.legacy.upgrade.UpgradeTaskException;
import me.filoghost.chestcommands.parsing.ParseException; import me.filoghost.chestcommands.parsing.ParseException;
import me.filoghost.chestcommands.util.logging.ErrorCollector; import me.filoghost.chestcommands.util.logging.ErrorCollector;
import me.filoghost.chestcommands.util.logging.ErrorInfo; import me.filoghost.chestcommands.util.logging.ErrorInfo;
@ -65,7 +65,7 @@ public class PrintableErrorCollector extends ErrorCollector {
} else if (cause instanceof ConfigException } else if (cause instanceof ConfigException
|| cause instanceof ParseException || cause instanceof ParseException
|| cause instanceof UpgradeException || cause instanceof UpgradeTaskException
|| cause instanceof UpgradeExecutorException) { || cause instanceof UpgradeExecutorException) {
message.add(cause.getMessage()); message.add(cause.getMessage());
cause = cause.getCause(); // Print the cause (or nothing if null), not our "known" exception cause = cause.getCause(); // Print the cause (or nothing if null), not our "known" exception