From be9ce826dce00df63e2640a9f946d1f16def2a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 21 May 2019 09:13:49 +0200 Subject: [PATCH 1/2] Move further listener modules to bootstrap module --- .../src/main/java/sonia/scm/ScmContextListener.java | 2 -- .../sonia/scm/boot/BootstrapContextListener.java | 13 ++++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 4f61ebaa17..40fb345caa 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -132,8 +132,6 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList List moduleList = Lists.newArrayList(); moduleList.add(new ResteasyModule()); - moduleList.add(new ScmInitializerModule()); - moduleList.add(new EagerSingletonModule()); moduleList.add(ShiroWebModule.guiceFilterModule()); moduleList.add(new WebElementModule(pluginLoader)); moduleList.add(new ScmServletModule(context, pluginLoader, overrides)); 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 86e62d8f94..69e70828a2 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -39,9 +39,11 @@ import com.google.inject.Module; import com.google.inject.assistedinject.FactoryModuleBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.EagerSingletonModule; import sonia.scm.SCMContext; import sonia.scm.ScmContextListener; import sonia.scm.ScmEventBusModule; +import sonia.scm.ScmInitializerModule; import sonia.scm.Stage; import sonia.scm.event.ScmEventBus; import sonia.scm.plugin.DefaultPluginLoader; @@ -150,9 +152,18 @@ public class BootstrapContextListener implements ServletContextListener { Module scmContextListenerModule = new ScmContextListenerModule(); BootstrapModule bootstrapModule = new BootstrapModule(pluginLoader); + ScmInitializerModule scmInitializerModule = new ScmInitializerModule(); + EagerSingletonModule eagerSingletonModule = new EagerSingletonModule(); ScmEventBusModule scmEventBusModule = new ScmEventBusModule(); - Injector bootstrapInjector = Guice.createInjector(bootstrapModule, scmContextListenerModule, scmEventBusModule); + Injector bootstrapInjector = + Guice.createInjector( + bootstrapModule, + scmContextListenerModule, + scmEventBusModule, + scmInitializerModule, + eagerSingletonModule + ); processUpdates(pluginLoader, bootstrapInjector); From f3d7727198828ac7427e7550d032441a18a348a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 20 May 2019 16:34:22 +0200 Subject: [PATCH 2/2] Add documentation --- .../java/sonia/scm/migration/UpdateStep.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) 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 eaa6d8d549..ed5f60a630 100644 --- a/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java +++ b/scm-core/src/main/java/sonia/scm/migration/UpdateStep.java @@ -3,11 +3,80 @@ package sonia.scm.migration; import sonia.scm.plugin.ExtensionPoint; import sonia.scm.version.Version; +/** + * This is the main interface for data migration/update. Using this interface, SCM-Manager provides the possibility to + * change data structures between versions for a given type of data. + *

The data type can be an arbitrary string, but it is considered a best practice to use a qualified name, for + * example + *

    + *
  • com.example.myPlugin.configuration
  • for data in plugins, or + *
  • com.cloudogu.scm.repository
  • for core data structures. + *
+ *

+ *

The version is unrelated to other versions and therefore can be chosen freely, so that a data type can be updated + * without in various ways independent of other data types or the official version of the plugin or the core. + * A coordination between different data types and their versions is only necessary, when update steps of different data + * types rely on each other. If a update step of data type A has to run before another step for data type + * B, the version number of the second step has to be greater in regards to {@link Version#compareTo(Version)}. + *

+ *

The algorithm looks something like this:
+ * Whenever the SCM-Manager starts, + *

    + *
  • it creates a so called bootstrap guice context, that contains + *
      + *
    • a {@link sonia.scm.security.KeyGenerator},
    • + *
    • the {@link sonia.scm.repository.RepositoryLocationResolver},
    • + *
    • the {@link sonia.scm.io.FileSystem},
    • + *
    • the {@link sonia.scm.security.CipherHandler},
    • + *
    • a {@link sonia.scm.store.ConfigurationStoreFactory},
    • + *
    • a {@link sonia.scm.store.ConfigurationEntryStoreFactory},
    • + *
    • a {@link sonia.scm.store.DataStoreFactory},
    • + *
    • a {@link sonia.scm.store.BlobStoreFactory}, and
    • + *
    • the {@link sonia.scm.plugin.PluginLoader}.
    • + *
    + * Mind, that there are no DAOs, Managers or the like available at this time! + *
  • + *
  • It then checks whether there are instances of this interface that have not run before, that is either + *
      + *
    • their version number given by {@link #getTargetVersion()} is bigger than the last recorded target version of an + * executed update step for the data type given by {@link #getAffectedDataType()}, or + *
    • + *
    • there is no version number known for the given data type. + *
    • + *
    + * These are the relevant update steps. + *
  • + *
  • These relevant update steps are then sorted ascending by their target version given by + * {@link #getTargetVersion()}. + *
  • + *
  • Finally, these sorted steps are executed one after another calling {@link #doUpdate()} of each step, updating the + * version for the data type accordingly. + *
  • + *
  • If all works well, SCM-Manager then creates the runtime guice context by loading all further modules.
  • + *
  • If any of the update steps fails, the whole process is interrupted and SCM-Manager will not start up and will + * not record the version number of this update step. + *
  • + *
+ *

+ */ @ExtensionPoint public interface UpdateStep { + /** + * Implement this to update the data to the new version. If any {@link Exception} is thrown, SCM-Manager will not + * start up. + */ void doUpdate() throws Exception; + /** + * Declares the new version of the data type given by {@link #getAffectedDataType()}. A update step will only be + * executed, when this version is bigger than the last recorded version for its data type according to + * {@link Version#compareTo(Version)} + */ Version getTargetVersion(); + /** + * Declares the data type this update step will take care of. This should be a qualified name, like + * com.example.myPlugin.configuration. + */ String getAffectedDataType(); }