[New] added MigrationsHandler.runCodeMigrations()
- this allows a project to add migrations to be run in a post initializer etc.
This commit is contained in:
parent
cfb30486e0
commit
cf807edd7a
|
@ -26,9 +26,6 @@ public class StrolchTypeNavigation implements Navigation {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the type
|
|
||||||
*/
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return this.type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,12 @@ public class CodeMigration extends Migration {
|
||||||
super(realm, version, dataFile);
|
super(realm, version, dataFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodeMigration(String realm, Version version) {
|
||||||
|
super(realm, version, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void migrate(ComponentContainer container, Certificate certificate) {
|
public void migrate(ComponentContainer container, Certificate certificate) {
|
||||||
logger.info("[" + this.realm + "] Running migration " + this.version);
|
logger.info("[" + this.realm + "] Running no-op migration " + this.version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class DataMigration extends Migration {
|
||||||
public void migrate(ComponentContainer container, Certificate certificate) {
|
public void migrate(ComponentContainer container, Certificate certificate) {
|
||||||
|
|
||||||
XmlImportModelCommand command;
|
XmlImportModelCommand command;
|
||||||
try (StrolchTransaction tx = container.getRealm(getRealm()).openTx(certificate, DataMigration.class)) {
|
try (StrolchTransaction tx = openTx(container, certificate)) {
|
||||||
|
|
||||||
command = new XmlImportModelCommand(container, tx);
|
command = new XmlImportModelCommand(container, tx);
|
||||||
command.setModelFile(getDataFile());
|
command.setModelFile(getDataFile());
|
||||||
|
|
|
@ -48,6 +48,10 @@ public abstract class Migration {
|
||||||
return dataFile;
|
return dataFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected StrolchTransaction openTx(ComponentContainer container, Certificate cert) {
|
||||||
|
return container.getRealm(getRealm()).openTx(cert, getClass());
|
||||||
|
}
|
||||||
|
|
||||||
protected Command buildMigrationVersionChangeCommand(ComponentContainer container, StrolchTransaction tx) {
|
protected Command buildMigrationVersionChangeCommand(ComponentContainer container, StrolchTransaction tx) {
|
||||||
|
|
||||||
Resource migrationsRes = tx.getResourceBy(MIGRATIONS_TYPE, MIGRATIONS_ID);
|
Resource migrationsRes = tx.getResourceBy(MIGRATIONS_TYPE, MIGRATIONS_ID);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package li.strolch.migrations;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
@ -103,6 +104,45 @@ public class Migrations {
|
||||||
this.migrationsRan = migrationsRan;
|
this.migrationsRan = migrationsRan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cert
|
||||||
|
* @param codeMigrationsByRealm
|
||||||
|
*/
|
||||||
|
public void runCodeMigrations(Certificate cert, MapOfLists<String, CodeMigration> codeMigrationsByRealm) {
|
||||||
|
|
||||||
|
MapOfLists<String, Version> migrationsRan = new MapOfLists<>();
|
||||||
|
|
||||||
|
for (String realm : codeMigrationsByRealm.keySet()) {
|
||||||
|
Version currentVersion = this.currentVersions.get(realm);
|
||||||
|
|
||||||
|
List<CodeMigration> listOfMigrations = codeMigrationsByRealm.getList(realm);
|
||||||
|
SortedSet<CodeMigration> migrations = new TreeSet<>((o1, o2) -> o1.getVersion().compareTo(o2.getVersion()));
|
||||||
|
migrations.addAll(listOfMigrations);
|
||||||
|
|
||||||
|
Version nextVersion = currentVersion.add(0, 0, 1);
|
||||||
|
CodeMigration nextMigration = new CodeMigration(realm, nextVersion);
|
||||||
|
|
||||||
|
SortedSet<CodeMigration> migrationsToRun = migrations.tailSet(nextMigration);
|
||||||
|
for (CodeMigration migration : migrationsToRun) {
|
||||||
|
DBC.INTERIM.assertEquals("Realms do not match!", realm, migration.getRealm());
|
||||||
|
Version migrateVersion = migration.getVersion();
|
||||||
|
boolean isLaterMigration = migrateVersion.compareTo(currentVersion) > 0;
|
||||||
|
DBC.INTERIM.assertTrue("Current version " + currentVersion + " is not before next " + migrateVersion,
|
||||||
|
isLaterMigration);
|
||||||
|
|
||||||
|
migration.migrate(this.container, cert);
|
||||||
|
migrationsRan.addElement(realm, migration.getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (migrationsRan.isEmpty()) {
|
||||||
|
logger.info("There were no migrations required!");
|
||||||
|
} else {
|
||||||
|
logger.info("Migrated " + migrationsRan.size() + " realms!");
|
||||||
|
}
|
||||||
|
this.migrationsRan = migrationsRan;
|
||||||
|
}
|
||||||
|
|
||||||
private static void logDetectedMigrations(Map<String, Version> currentVersions,
|
private static void logDetectedMigrations(Map<String, Version> currentVersions,
|
||||||
Map<String, SortedSet<DataMigration>> allDataMigrations,
|
Map<String, SortedSet<DataMigration>> allDataMigrations,
|
||||||
Map<String, SortedSet<CodeMigration>> allCodeMigrations) {
|
Map<String, SortedSet<CodeMigration>> allCodeMigrations) {
|
||||||
|
|
|
@ -46,7 +46,6 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
private boolean verbose;
|
private boolean verbose;
|
||||||
|
|
||||||
private Migrations migrations;
|
private Migrations migrations;
|
||||||
private MapOfLists<String, Version> lastMigrations;
|
|
||||||
private File migrationsPath;
|
private File migrationsPath;
|
||||||
|
|
||||||
private Timer migrationTimer;
|
private Timer migrationTimer;
|
||||||
|
@ -58,9 +57,9 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapOfLists<String, Version> getLastMigrations() {
|
public MapOfLists<String, Version> getLastMigrations() {
|
||||||
if (this.lastMigrations == null)
|
if (this.migrations == null)
|
||||||
return new MapOfLists<>();
|
return new MapOfLists<>();
|
||||||
return this.lastMigrations;
|
return this.migrations.getMigrationsRan();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Version> getCurrentVersions(Certificate cert) {
|
public Map<String, Version> getCurrentVersions(Certificate cert) {
|
||||||
|
@ -70,9 +69,8 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapOfLists<String, Version> queryMigrationsToRun(Certificate cert) {
|
public MapOfLists<String, Version> queryMigrationsToRun(Certificate cert) {
|
||||||
if (!this.migrationsPath.isDirectory()) {
|
if (!this.migrationsPath.isDirectory())
|
||||||
return new MapOfLists<>();
|
return new MapOfLists<>();
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Version> currentVersions = getCurrentVersions(cert);
|
Map<String, Version> currentVersions = getCurrentVersions(cert);
|
||||||
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
|
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
|
||||||
|
@ -85,7 +83,13 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
public void runMigrations(Certificate cert) {
|
public void runMigrations(Certificate cert) {
|
||||||
queryMigrationsToRun(cert);
|
queryMigrationsToRun(cert);
|
||||||
this.migrations.runMigrations(cert);
|
this.migrations.runMigrations(cert);
|
||||||
this.lastMigrations = this.migrations.getMigrationsRan();
|
}
|
||||||
|
|
||||||
|
public void runCodeMigrations(Certificate cert, MapOfLists<String, CodeMigration> codeMigrationsByRealm) {
|
||||||
|
Map<String, Version> currentVersions = getCurrentVersions(cert);
|
||||||
|
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
|
||||||
|
this.migrations = migrations;
|
||||||
|
migrations.runCodeMigrations(cert, codeMigrationsByRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,7 +134,6 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
RunMigrationsAction action = new RunMigrationsAction(this.migrations);
|
RunMigrationsAction action = new RunMigrationsAction(this.migrations);
|
||||||
|
|
||||||
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action);
|
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action);
|
||||||
this.lastMigrations = this.migrations.getMigrationsRan();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.start();
|
super.start();
|
||||||
|
@ -180,7 +183,6 @@ public class MigrationsHandler extends StrolchComponent {
|
||||||
} else {
|
} else {
|
||||||
RunMigrationsAction runMigrationsAction = new RunMigrationsAction(MigrationsHandler.this.migrations);
|
RunMigrationsAction runMigrationsAction = new RunMigrationsAction(MigrationsHandler.this.migrations);
|
||||||
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, runMigrationsAction);
|
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, runMigrationsAction);
|
||||||
MigrationsHandler.this.lastMigrations = MigrationsHandler.this.migrations.getMigrationsRan();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,13 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import li.strolch.agent.api.ComponentContainer;
|
||||||
|
import li.strolch.command.AddOrderCommand;
|
||||||
|
import li.strolch.command.RemoveOrderCommand;
|
||||||
|
import li.strolch.model.ModelGenerator;
|
||||||
|
import li.strolch.model.Order;
|
||||||
|
import li.strolch.persistence.api.StrolchTransaction;
|
||||||
import li.strolch.runtime.StrolchConstants;
|
import li.strolch.runtime.StrolchConstants;
|
||||||
import li.strolch.runtime.privilege.PrivilegeHandler;
|
|
||||||
import li.strolch.testbase.runtime.RuntimeMock;
|
import li.strolch.testbase.runtime.RuntimeMock;
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
@ -49,21 +54,122 @@ public class MigrationsTest {
|
||||||
@Test
|
@Test
|
||||||
public void shouldRunMigrations() {
|
public void shouldRunMigrations() {
|
||||||
|
|
||||||
PrivilegeHandler privilegeHandler = runtimeMock.getPrivilegeHandler();
|
|
||||||
Certificate cert = privilegeHandler.authenticate("test", "test".getBytes());
|
|
||||||
|
|
||||||
MigrationsHandler migrationsHandler = runtimeMock.getContainer().getComponent(MigrationsHandler.class);
|
MigrationsHandler migrationsHandler = runtimeMock.getContainer().getComponent(MigrationsHandler.class);
|
||||||
Map<String, Version> currentVersions = migrationsHandler.getCurrentVersions(cert);
|
Map<String, Version> currentVersions = migrationsHandler.getCurrentVersions(certificate);
|
||||||
assertEquals("1.1.1", currentVersions.get(StrolchConstants.DEFAULT_REALM).toString());
|
String defRealm = StrolchConstants.DEFAULT_REALM;
|
||||||
|
assertEquals("1.1.1", currentVersions.get(defRealm).toString());
|
||||||
assertEquals("0.0.0", currentVersions.get("other").toString());
|
assertEquals("0.0.0", currentVersions.get("other").toString());
|
||||||
|
|
||||||
MapOfLists<String, Version> lastMigrations = migrationsHandler.getLastMigrations();
|
MapOfLists<String, Version> lastMigrations = migrationsHandler.getLastMigrations();
|
||||||
List<Version> expectedMigrations = Arrays.asList(Version.valueOf("0.1.0"), Version.valueOf("0.1.1"),
|
List<Version> expectedMigrations = Arrays.asList(Version.valueOf("0.1.0"), Version.valueOf("0.1.1"),
|
||||||
Version.valueOf("0.5.2"), Version.valueOf("1.0.0"), Version.valueOf("1.0.5"), Version.valueOf("1.1.1"));
|
Version.valueOf("0.5.2"), Version.valueOf("1.0.0"), Version.valueOf("1.0.5"), Version.valueOf("1.1.1"));
|
||||||
assertEquals(expectedMigrations, lastMigrations.getList(StrolchConstants.DEFAULT_REALM));
|
assertEquals(expectedMigrations, lastMigrations.getList(defRealm));
|
||||||
assertEquals(null, lastMigrations.getList("other"));
|
assertEquals(null, lastMigrations.getList("other"));
|
||||||
|
|
||||||
MapOfLists<String, Version> migrationsToRun = migrationsHandler.queryMigrationsToRun(cert);
|
MapOfLists<String, Version> migrationsToRun = migrationsHandler.queryMigrationsToRun(certificate);
|
||||||
assertTrue("Expected to have all migrations run", migrationsToRun.isEmpty());
|
assertTrue("Expected to have all migrations run", migrationsToRun.isEmpty());
|
||||||
|
|
||||||
|
// assert new current version
|
||||||
|
currentVersions = migrationsHandler.getCurrentVersions(certificate);
|
||||||
|
assertEquals("1.1.1", currentVersions.get(defRealm).toString());
|
||||||
|
assertEquals("0.0.0", currentVersions.get("other").toString());
|
||||||
|
|
||||||
|
MapOfLists<String, CodeMigration> codeMigrationsByRealm = new MapOfLists<>();
|
||||||
|
// add migrations in wrong sequence - should be fixed by migration handler
|
||||||
|
codeMigrationsByRealm.addElement(defRealm, new MyMigration2(defRealm));
|
||||||
|
codeMigrationsByRealm.addElement(defRealm, new MyMigration1(defRealm));
|
||||||
|
codeMigrationsByRealm.addElement(defRealm, new MyMigration0(defRealm));
|
||||||
|
migrationsHandler.runCodeMigrations(certificate, codeMigrationsByRealm);
|
||||||
|
|
||||||
|
lastMigrations = migrationsHandler.getLastMigrations();
|
||||||
|
assertEquals(1, lastMigrations.keySet().size());
|
||||||
|
assertEquals(Arrays.asList(Version.valueOf("1.2.0"), Version.valueOf("1.3.0")),
|
||||||
|
lastMigrations.getList(defRealm));
|
||||||
|
|
||||||
|
// assert new current version
|
||||||
|
currentVersions = migrationsHandler.getCurrentVersions(certificate);
|
||||||
|
assertEquals("1.3.0", currentVersions.get(defRealm).toString());
|
||||||
|
assertEquals("0.0.0", currentVersions.get("other").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyMigration0 extends CodeMigration {
|
||||||
|
|
||||||
|
private static final Version version = Version.valueOf("1.0.0");
|
||||||
|
|
||||||
|
public MyMigration0(String realm) {
|
||||||
|
super(realm, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void migrate(ComponentContainer container, Certificate cert) {
|
||||||
|
logger.info("[" + this.realm + "] Running migration " + this.getVersion());
|
||||||
|
|
||||||
|
try (StrolchTransaction tx = openTx(container, cert)) {
|
||||||
|
|
||||||
|
Order fooOrder = ModelGenerator.createOrder("foo", "Foo", "Foo");
|
||||||
|
|
||||||
|
AddOrderCommand addOrderCommand = new AddOrderCommand(container, tx);
|
||||||
|
addOrderCommand.setOrder(fooOrder);
|
||||||
|
tx.addCommand(addOrderCommand);
|
||||||
|
|
||||||
|
tx.addCommand(buildMigrationVersionChangeCommand(container, tx));
|
||||||
|
|
||||||
|
tx.commitOnClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyMigration1 extends CodeMigration {
|
||||||
|
|
||||||
|
private static final Version version = Version.valueOf("1.2.0");
|
||||||
|
|
||||||
|
public MyMigration1(String realm) {
|
||||||
|
super(realm, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void migrate(ComponentContainer container, Certificate cert) {
|
||||||
|
logger.info("[" + this.realm + "] Running migration " + this.getVersion());
|
||||||
|
|
||||||
|
try (StrolchTransaction tx = openTx(container, cert)) {
|
||||||
|
|
||||||
|
Order fooOrder = ModelGenerator.createOrder("foo", "Foo", "Foo");
|
||||||
|
|
||||||
|
AddOrderCommand addOrderCommand = new AddOrderCommand(container, tx);
|
||||||
|
addOrderCommand.setOrder(fooOrder);
|
||||||
|
tx.addCommand(addOrderCommand);
|
||||||
|
|
||||||
|
tx.addCommand(buildMigrationVersionChangeCommand(container, tx));
|
||||||
|
|
||||||
|
tx.commitOnClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyMigration2 extends CodeMigration {
|
||||||
|
|
||||||
|
private static final Version version = Version.valueOf("1.3.0");
|
||||||
|
|
||||||
|
public MyMigration2(String realm) {
|
||||||
|
super(realm, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void migrate(ComponentContainer container, Certificate cert) {
|
||||||
|
logger.info("[" + this.realm + "] Running migration " + this.getVersion());
|
||||||
|
|
||||||
|
try (StrolchTransaction tx = openTx(container, cert)) {
|
||||||
|
|
||||||
|
Order fooOrder = tx.getOrderBy("Foo", "foo");
|
||||||
|
|
||||||
|
RemoveOrderCommand removeOrderCommand = new RemoveOrderCommand(container, tx);
|
||||||
|
removeOrderCommand.setOrder(fooOrder);
|
||||||
|
tx.addCommand(removeOrderCommand);
|
||||||
|
|
||||||
|
tx.addCommand(buildMigrationVersionChangeCommand(container, tx));
|
||||||
|
|
||||||
|
tx.commitOnClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue