[New] added MigrationsHandler.runCodeMigrations()

- this allows a project to add migrations to be run in a post
initializer etc.
This commit is contained in:
Robert von Burg 2015-02-11 16:41:43 +01:00
parent cfb30486e0
commit cf807edd7a
7 changed files with 174 additions and 21 deletions

View File

@ -26,9 +26,6 @@ public class StrolchTypeNavigation implements Navigation {
this.type = type;
}
/**
* @return the type
*/
public String getType() {
return this.type;
}

View File

@ -12,8 +12,12 @@ public class CodeMigration extends Migration {
super(realm, version, dataFile);
}
public CodeMigration(String realm, Version version) {
super(realm, version, null);
}
@Override
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);
}
}

View File

@ -22,7 +22,7 @@ public class DataMigration extends Migration {
public void migrate(ComponentContainer container, Certificate certificate) {
XmlImportModelCommand command;
try (StrolchTransaction tx = container.getRealm(getRealm()).openTx(certificate, DataMigration.class)) {
try (StrolchTransaction tx = openTx(container, certificate)) {
command = new XmlImportModelCommand(container, tx);
command.setModelFile(getDataFile());

View File

@ -48,6 +48,10 @@ public abstract class Migration {
return dataFile;
}
protected StrolchTransaction openTx(ComponentContainer container, Certificate cert) {
return container.getRealm(getRealm()).openTx(cert, getClass());
}
protected Command buildMigrationVersionChangeCommand(ComponentContainer container, StrolchTransaction tx) {
Resource migrationsRes = tx.getResourceBy(MIGRATIONS_TYPE, MIGRATIONS_ID);

View File

@ -3,6 +3,7 @@ package li.strolch.migrations;
import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedSet;
@ -103,6 +104,45 @@ public class Migrations {
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,
Map<String, SortedSet<DataMigration>> allDataMigrations,
Map<String, SortedSet<CodeMigration>> allCodeMigrations) {

View File

@ -46,7 +46,6 @@ public class MigrationsHandler extends StrolchComponent {
private boolean verbose;
private Migrations migrations;
private MapOfLists<String, Version> lastMigrations;
private File migrationsPath;
private Timer migrationTimer;
@ -58,9 +57,9 @@ public class MigrationsHandler extends StrolchComponent {
}
public MapOfLists<String, Version> getLastMigrations() {
if (this.lastMigrations == null)
if (this.migrations == null)
return new MapOfLists<>();
return this.lastMigrations;
return this.migrations.getMigrationsRan();
}
public Map<String, Version> getCurrentVersions(Certificate cert) {
@ -70,9 +69,8 @@ public class MigrationsHandler extends StrolchComponent {
}
public MapOfLists<String, Version> queryMigrationsToRun(Certificate cert) {
if (!this.migrationsPath.isDirectory()) {
if (!this.migrationsPath.isDirectory())
return new MapOfLists<>();
}
Map<String, Version> currentVersions = getCurrentVersions(cert);
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
@ -85,7 +83,13 @@ public class MigrationsHandler extends StrolchComponent {
public void runMigrations(Certificate cert) {
queryMigrationsToRun(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
@ -130,7 +134,6 @@ public class MigrationsHandler extends StrolchComponent {
RunMigrationsAction action = new RunMigrationsAction(this.migrations);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action);
this.lastMigrations = this.migrations.getMigrationsRan();
}
super.start();
@ -180,7 +183,6 @@ public class MigrationsHandler extends StrolchComponent {
} else {
RunMigrationsAction runMigrationsAction = new RunMigrationsAction(MigrationsHandler.this.migrations);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, runMigrationsAction);
MigrationsHandler.this.lastMigrations = MigrationsHandler.this.migrations.getMigrationsRan();
}
}
}

View File

@ -8,8 +8,13 @@ import java.util.Arrays;
import java.util.List;
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.privilege.PrivilegeHandler;
import li.strolch.testbase.runtime.RuntimeMock;
import org.junit.AfterClass;
@ -49,21 +54,122 @@ public class MigrationsTest {
@Test
public void shouldRunMigrations() {
PrivilegeHandler privilegeHandler = runtimeMock.getPrivilegeHandler();
Certificate cert = privilegeHandler.authenticate("test", "test".getBytes());
MigrationsHandler migrationsHandler = runtimeMock.getContainer().getComponent(MigrationsHandler.class);
Map<String, Version> currentVersions = migrationsHandler.getCurrentVersions(cert);
assertEquals("1.1.1", currentVersions.get(StrolchConstants.DEFAULT_REALM).toString());
Map<String, Version> currentVersions = migrationsHandler.getCurrentVersions(certificate);
String defRealm = StrolchConstants.DEFAULT_REALM;
assertEquals("1.1.1", currentVersions.get(defRealm).toString());
assertEquals("0.0.0", currentVersions.get("other").toString());
MapOfLists<String, Version> lastMigrations = migrationsHandler.getLastMigrations();
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"));
assertEquals(expectedMigrations, lastMigrations.getList(StrolchConstants.DEFAULT_REALM));
assertEquals(expectedMigrations, lastMigrations.getList(defRealm));
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());
// 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();
}
}
}
}