From 93dbaeae7c66f9569643655dc7adf1599caa1afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 14 May 2019 15:52:05 +0200 Subject: [PATCH] Run update steps only if they have not been run before --- .../java/sonia/scm/migration/UpdateStep.java | 5 +- .../src/main/java/sonia/scm/UpdateEngine.java | 32 ------ .../scm/boot/BootstrapContextListener.java | 2 +- .../java/sonia/scm/update/UpdateEngine.java | 53 ++++++++++ .../sonia/scm/update/UpdateVersionInfo.java | 22 +++++ .../test/java/sonia/scm/UpdateEngineTest.java | 48 --------- .../sonia/scm/update/UpdateEngineTest.java | 97 +++++++++++++++++++ 7 files changed, 177 insertions(+), 82 deletions(-) delete mode 100644 scm-webapp/src/main/java/sonia/scm/UpdateEngine.java create mode 100644 scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java create mode 100644 scm-webapp/src/main/java/sonia/scm/update/UpdateVersionInfo.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/UpdateEngineTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/update/UpdateEngineTest.java diff --git a/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java b/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java index ebb9bd0877..b29de6b151 100644 --- a/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java +++ b/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java @@ -1,10 +1,13 @@ package sonia.scm.migration; import sonia.scm.plugin.ExtensionPoint; +import sonia.scm.version.Version; @ExtensionPoint public interface UpdateStep { void doUpdate(); - String getTargetVersion(); + Version getTargetVersion(); + + String affectedDataType(); } diff --git a/scm-webapp/src/main/java/sonia/scm/UpdateEngine.java b/scm-webapp/src/main/java/sonia/scm/UpdateEngine.java deleted file mode 100644 index 4fb0b8239b..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/UpdateEngine.java +++ /dev/null @@ -1,32 +0,0 @@ -package sonia.scm; - -import sonia.scm.migration.UpdateStep; -import sonia.scm.version.Version; - -import javax.inject.Inject; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - -import static java.util.stream.Collectors.toList; - -public class UpdateEngine { - - private final List steps; - - @Inject - public UpdateEngine(Set steps) { - this.steps = sortSteps(steps); - } - - private List sortSteps(Set steps) { - Comparator compareByVersion = Comparator.comparing(step -> Version.parse(step.getTargetVersion())); - return steps.stream() - .sorted(compareByVersion.reversed()) - .collect(toList()); - } - - public void update() { - steps.forEach(UpdateStep::doUpdate); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java index d8a64444e7..b2dcbbba4a 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -39,7 +39,6 @@ import com.google.inject.Module; import com.google.inject.assistedinject.FactoryModuleBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.UpdateEngine; import sonia.scm.SCMContext; import sonia.scm.ScmContextListener; import sonia.scm.Stage; @@ -51,6 +50,7 @@ import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginWrapper; import sonia.scm.plugin.PluginsInternal; import sonia.scm.plugin.SmpArchive; +import sonia.scm.update.UpdateEngine; import sonia.scm.util.ClassLoaders; import sonia.scm.util.IOUtil; diff --git a/scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java b/scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java new file mode 100644 index 0000000000..b558650a29 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java @@ -0,0 +1,53 @@ +package sonia.scm.update; + +import sonia.scm.migration.UpdateStep; +import sonia.scm.store.ConfigurationEntryStore; +import sonia.scm.store.ConfigurationEntryStoreFactory; + +import javax.inject.Inject; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import static java.util.stream.Collectors.toList; + +public class UpdateEngine { + + private static final String STORE_NAME = "executedUpdates"; + + private final List steps; + private final ConfigurationEntryStore store; + + @Inject + public UpdateEngine(Set steps, ConfigurationEntryStoreFactory storeFactory) { + this.steps = sortSteps(steps); + this.store = storeFactory.withType(UpdateVersionInfo.class).withName(STORE_NAME).build(); + } + + private List sortSteps(Set steps) { + return steps.stream() + .sorted(Comparator.comparing(UpdateStep::getTargetVersion).reversed()) + .collect(toList()); + } + + public void update() { + steps + .stream() + .filter(this::notRunYet) + .forEach(this::execute); + } + + private void execute(UpdateStep updateStep) { + updateStep.doUpdate(); + UpdateVersionInfo newVersionInfo = new UpdateVersionInfo(updateStep.getTargetVersion().getParsedVersion()); + store.put(updateStep.affectedDataType(), newVersionInfo); + } + + private boolean notRunYet(UpdateStep updateStep) { + UpdateVersionInfo updateVersionInfo = store.get(updateStep.affectedDataType()); + if (updateVersionInfo == null) { + return true; + } + return updateStep.getTargetVersion().isNewer(updateVersionInfo.getLatestVersion()); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/update/UpdateVersionInfo.java b/scm-webapp/src/main/java/sonia/scm/update/UpdateVersionInfo.java new file mode 100644 index 0000000000..bc54d82bd5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/update/UpdateVersionInfo.java @@ -0,0 +1,22 @@ +package sonia.scm.update; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "latest-version") +@XmlAccessorType(XmlAccessType.FIELD) +public class UpdateVersionInfo { + private String latestVersion; + + public UpdateVersionInfo() { + } + + public UpdateVersionInfo(String latestVersion) { + this.latestVersion = latestVersion; + } + + public String getLatestVersion() { + return latestVersion; + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/UpdateEngineTest.java b/scm-webapp/src/test/java/sonia/scm/UpdateEngineTest.java deleted file mode 100644 index da72ed2ed0..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/UpdateEngineTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package sonia.scm; - -import org.junit.jupiter.api.Test; -import sonia.scm.migration.UpdateStep; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -class UpdateEngineTest { - - List processedUpdates = new ArrayList<>(); - - @Test - void shouldProcessStepsInCorrectOrder() { - LinkedHashSet updateSteps = new LinkedHashSet<>(); - - updateSteps.add(new FixedVersionUpdateStep("1.1.1")); - updateSteps.add(new FixedVersionUpdateStep("1.2.0")); - updateSteps.add(new FixedVersionUpdateStep("1.1.0")); - - UpdateEngine updateEngine = new UpdateEngine(updateSteps); - updateEngine.update(); - - assertThat(processedUpdates) - .containsExactly("1.1.0", "1.1.1", "1.2.0"); - } - - class FixedVersionUpdateStep implements UpdateStep { - private final String version; - - FixedVersionUpdateStep(String version) { - this.version = version; - } - - @Override - public String getTargetVersion() { - return version; - } - - @Override - public void doUpdate() { - processedUpdates.add(version); - } - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/update/UpdateEngineTest.java b/scm-webapp/src/test/java/sonia/scm/update/UpdateEngineTest.java new file mode 100644 index 0000000000..fb90d9d470 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/update/UpdateEngineTest.java @@ -0,0 +1,97 @@ +package sonia.scm.update; + +import org.junit.jupiter.api.Test; +import sonia.scm.migration.UpdateStep; +import sonia.scm.store.ConfigurationEntryStoreFactory; +import sonia.scm.store.InMemoryConfigurationEntryStore; +import sonia.scm.store.InMemoryConfigurationEntryStoreFactory; +import sonia.scm.version.Version; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static sonia.scm.version.Version.parse; + +class UpdateEngineTest { + + ConfigurationEntryStoreFactory storeFactory = new InMemoryConfigurationEntryStoreFactory(new InMemoryConfigurationEntryStore()); + + List processedUpdates = new ArrayList<>(); + + @Test + void shouldProcessStepsInCorrectOrder() { + LinkedHashSet updateSteps = new LinkedHashSet<>(); + + updateSteps.add(new FixedVersionUpdateStep("test", "1.1.1")); + updateSteps.add(new FixedVersionUpdateStep("test", "1.2.0")); + updateSteps.add(new FixedVersionUpdateStep("test", "1.1.0")); + + UpdateEngine updateEngine = new UpdateEngine(updateSteps, storeFactory); + updateEngine.update(); + + assertThat(processedUpdates) + .containsExactly("1.1.0", "1.1.1", "1.2.0"); + } + + @Test + void shouldRunStepsOnlyOnce() { + LinkedHashSet updateSteps = new LinkedHashSet<>(); + + updateSteps.add(new FixedVersionUpdateStep("test", "1.1.1")); + + UpdateEngine updateEngine = new UpdateEngine(updateSteps, storeFactory); + updateEngine.update(); + + processedUpdates.clear(); + + updateEngine.update(); + + assertThat(processedUpdates).isEmpty(); + } + + @Test + void shouldRunStepsForDifferentTypesIndependently() { + LinkedHashSet updateSteps = new LinkedHashSet<>(); + + updateSteps.add(new FixedVersionUpdateStep("test", "1.1.1")); + + UpdateEngine updateEngine = new UpdateEngine(updateSteps, storeFactory); + updateEngine.update(); + + processedUpdates.clear(); + + updateSteps.add(new FixedVersionUpdateStep("other", "1.1.1")); + + updateEngine = new UpdateEngine(updateSteps, storeFactory); + updateEngine.update(); + + assertThat(processedUpdates).containsExactly("1.1.1"); + } + + class FixedVersionUpdateStep implements UpdateStep { + private final String type; + private final String version; + + FixedVersionUpdateStep(String type, String version) { + this.type = type; + this.version = version; + } + + @Override + public Version getTargetVersion() { + return parse(version); + } + + @Override + public String affectedDataType() { + return type; + } + + @Override + public void doUpdate() { + processedUpdates.add(version); + } + } +}