[Major] refactored MigrationsHandler

- now the currentVersions are queried later, because only after the
realm handler is started, can we query the current version.
- this lead to only parsing the migrations at initialize
- and thus in start querying the versions and performing the required
migrations
This commit is contained in:
Robert von Burg 2015-02-12 17:25:59 +01:00
parent 30d4916a7e
commit 174e5bd37f
3 changed files with 73 additions and 64 deletions

View File

@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.List; 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.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -25,7 +26,7 @@ public class Migrations {
private static final Logger logger = LoggerFactory.getLogger(Migrations.class); private static final Logger logger = LoggerFactory.getLogger(Migrations.class);
private ComponentContainer container; private ComponentContainer container;
private Map<String, Version> currentVersions; private Set<String> realmNames;
private boolean verbose; private boolean verbose;
private Map<String, SortedSet<DataMigration>> dataMigrations; private Map<String, SortedSet<DataMigration>> dataMigrations;
@ -33,9 +34,9 @@ public class Migrations {
private MapOfLists<String, Version> migrationsRan; private MapOfLists<String, Version> migrationsRan;
public Migrations(ComponentContainer container, Map<String, Version> currentVersions, boolean verbose) { public Migrations(ComponentContainer container, Set<String> realmNames, boolean verbose) {
this.container = container; this.container = container;
this.currentVersions = currentVersions; this.realmNames = realmNames;
this.verbose = verbose; this.verbose = verbose;
} }
@ -55,41 +56,46 @@ public class Migrations {
DBC.PRE.assertTrue("If migrations path is not a directory!", migrationsPath.isDirectory()); DBC.PRE.assertTrue("If migrations path is not a directory!", migrationsPath.isDirectory());
// data migrations // data migrations
this.dataMigrations = loadDataMigrations(this.currentVersions, migrationsPath); this.dataMigrations = loadDataMigrations(this.realmNames, migrationsPath);
// code migrations // code migrations
this.codeMigrations = loadCodeMigrations(this.currentVersions, migrationsPath); this.codeMigrations = loadCodeMigrations(this.realmNames, migrationsPath);
// log found migrations // log found migrations
if (this.verbose) if (this.verbose)
logDetectedMigrations(this.currentVersions, this.dataMigrations, this.codeMigrations); logDetectedMigrations(this.realmNames, this.dataMigrations, this.codeMigrations);
} }
public void runMigrations(Certificate certificate) { public void runMigrations(Certificate certificate, Map<String, Version> currentVersions) {
MapOfLists<String, Version> migrationsRan = new MapOfLists<>(); MapOfLists<String, Version> migrationsRan = new MapOfLists<>();
for (Entry<String, Version> entry : this.currentVersions.entrySet()) { for (Entry<String, Version> entry : currentVersions.entrySet()) {
String realm = entry.getKey(); String realm = entry.getKey();
Version currentVersion = entry.getValue(); Version currentVersion = entry.getValue();
logger.info("[" + realm + "] Performing all migrations after " + currentVersion); if (this.verbose)
logger.info("[" + realm + "] Performing all migrations after " + currentVersion);
Version nextPossibleVersion = currentVersion.add(0, 0, 1); Version nextPossibleVersion = currentVersion.add(0, 0, 1);
CodeMigration currentCodeMigration = new CodeMigration(realm, nextPossibleVersion, null); CodeMigration currentCodeMigration = new CodeMigration(realm, nextPossibleVersion, null);
DataMigration currentDataMigration = new DataMigration(realm, nextPossibleVersion, null); DataMigration currentDataMigration = new DataMigration(realm, nextPossibleVersion, null);
SortedSet<CodeMigration> codeMigrations = this.codeMigrations.get(realm); SortedSet<DataMigration> dataMigrations = this.dataMigrations.get(realm);
if (codeMigrations != null && !codeMigrations.isEmpty()) { if (dataMigrations != null && !dataMigrations.isEmpty()) {
for (CodeMigration migration : codeMigrations.tailSet(currentCodeMigration)) { for (DataMigration migration : dataMigrations.tailSet(currentDataMigration)) {
String msg = "[{0}] Running data migration {1}";
logger.info(MessageFormat.format(msg, realm, migration.getVersion()));
migration.migrate(container, certificate); migration.migrate(container, certificate);
migrationsRan.addElement(realm, migration.getVersion()); migrationsRan.addElement(realm, migration.getVersion());
} }
} }
SortedSet<DataMigration> dataMigrations = this.dataMigrations.get(realm); SortedSet<CodeMigration> codeMigrations = this.codeMigrations.get(realm);
if (dataMigrations != null && !dataMigrations.isEmpty()) { if (codeMigrations != null && !codeMigrations.isEmpty()) {
for (DataMigration migration : dataMigrations.tailSet(currentDataMigration)) { for (CodeMigration migration : codeMigrations.tailSet(currentCodeMigration)) {
String msg = "[{0}] Running code migration {1} {2}";
logger.info(MessageFormat.format(msg, realm, migration.getVersion(), migration.getClass().getName()));
migration.migrate(container, certificate); migration.migrate(container, certificate);
migrationsRan.addElement(realm, migration.getVersion()); migrationsRan.addElement(realm, migration.getVersion());
} }
@ -110,17 +116,18 @@ public class Migrations {
* @param cert * @param cert
* @param codeMigrationsByRealm * @param codeMigrationsByRealm
*/ */
public void runCodeMigrations(Certificate cert, MapOfLists<String, CodeMigration> codeMigrationsByRealm) { public void runCodeMigrations(Certificate cert, Map<String, Version> currentVersions,
MapOfLists<String, CodeMigration> codeMigrationsByRealm) {
MapOfLists<String, Version> migrationsRan = new MapOfLists<>(); MapOfLists<String, Version> migrationsRan = new MapOfLists<>();
for (String realm : codeMigrationsByRealm.keySet()) { for (String realm : codeMigrationsByRealm.keySet()) {
// ignore if no such realm // ignore if no such realm
if (!this.currentVersions.containsKey(realm)) if (!this.realmNames.contains(realm))
continue; continue;
Version currentVersion = this.currentVersions.get(realm); Version currentVersion = currentVersions.get(realm);
List<CodeMigration> listOfMigrations = codeMigrationsByRealm.getList(realm); List<CodeMigration> listOfMigrations = codeMigrationsByRealm.getList(realm);
SortedSet<CodeMigration> migrations = new TreeSet<>((o1, o2) -> o1.getVersion().compareTo(o2.getVersion())); SortedSet<CodeMigration> migrations = new TreeSet<>((o1, o2) -> o1.getVersion().compareTo(o2.getVersion()));
@ -153,12 +160,11 @@ public class Migrations {
this.migrationsRan = migrationsRan; this.migrationsRan = migrationsRan;
} }
private static void logDetectedMigrations(Map<String, Version> currentVersions, private static void logDetectedMigrations(Set<String> realmNames,
Map<String, SortedSet<DataMigration>> allDataMigrations, Map<String, SortedSet<DataMigration>> allDataMigrations,
Map<String, SortedSet<CodeMigration>> allCodeMigrations) { Map<String, SortedSet<CodeMigration>> allCodeMigrations) {
for (Entry<String, Version> entry : currentVersions.entrySet()) {
String realm = entry.getKey(); for (String realm : realmNames) {
Version currentVersion = entry.getValue();
SortedSet<CodeMigration> codeMigrations = allCodeMigrations.get(realm); SortedSet<CodeMigration> codeMigrations = allCodeMigrations.get(realm);
if (codeMigrations == null || codeMigrations.isEmpty()) { if (codeMigrations == null || codeMigrations.isEmpty()) {
@ -166,10 +172,7 @@ public class Migrations {
} else { } else {
logger.info("[" + realm + "] Found " + codeMigrations.size() + " code migrations"); logger.info("[" + realm + "] Found " + codeMigrations.size() + " code migrations");
for (CodeMigration codeMigration : codeMigrations) { for (CodeMigration codeMigration : codeMigrations) {
if (codeMigration.getVersion().compareTo(currentVersion) > 0) logger.info("[" + realm + "] " + codeMigration.getVersion().toString());
logger.info("[" + realm + "] + " + codeMigration.getVersion().toString());
else
logger.info("[" + realm + "] - " + codeMigration.getVersion().toString());
} }
} }
@ -179,17 +182,13 @@ public class Migrations {
} else { } else {
logger.info("[" + realm + "] Found " + dataMigrations.size() + " data migrations"); logger.info("[" + realm + "] Found " + dataMigrations.size() + " data migrations");
for (DataMigration dataMigration : dataMigrations) { for (DataMigration dataMigration : dataMigrations) {
if (dataMigration.getVersion().compareTo(currentVersion) > 0) logger.info("[" + realm + "] " + dataMigration.getVersion().toString());
logger.info("[" + realm + "] + " + dataMigration.getVersion().toString());
else
logger.info("[" + realm + "] - " + dataMigration.getVersion().toString());
} }
} }
} }
} }
private static Map<String, SortedSet<DataMigration>> loadDataMigrations(Map<String, Version> currentVersions, private static Map<String, SortedSet<DataMigration>> loadDataMigrations(Set<String> realmNames, File migrationsPath) {
File migrationsPath) {
Map<String, SortedSet<DataMigration>> migrationsByRealm = new HashMap<>(); Map<String, SortedSet<DataMigration>> migrationsByRealm = new HashMap<>();
@ -198,8 +197,7 @@ public class Migrations {
DBC.PRE.assertTrue("migrations/data must be a directory!", dataDir.isDirectory()); DBC.PRE.assertTrue("migrations/data must be a directory!", dataDir.isDirectory());
// only list directories where name is a realmName // only list directories where name is a realmName
File[] realmMigrations = dataDir File[] realmMigrations = dataDir.listFiles((FileFilter) path -> realmNames.contains(path.getName()));
.listFiles((FileFilter) path -> currentVersions.containsKey(path.getName()));
for (File realmMigration : realmMigrations) { for (File realmMigration : realmMigrations) {
String realm = realmMigration.getName(); String realm = realmMigration.getName();
@ -221,8 +219,7 @@ public class Migrations {
return migrationsByRealm; return migrationsByRealm;
} }
private static Map<String, SortedSet<CodeMigration>> loadCodeMigrations(Map<String, Version> currentVersions, private static Map<String, SortedSet<CodeMigration>> loadCodeMigrations(Set<String> realmNames, File migrationsPath) {
File migrationsPath) {
Map<String, SortedSet<CodeMigration>> migrationsByRealm = new HashMap<>(); //new TreeSet<>((o1, o2) -> o1.getVersion().compareTo(o2.getVersion())); Map<String, SortedSet<CodeMigration>> migrationsByRealm = new HashMap<>(); //new TreeSet<>((o1, o2) -> o1.getVersion().compareTo(o2.getVersion()));
@ -230,8 +227,8 @@ public class Migrations {
if (codeDir.exists()) { if (codeDir.exists()) {
DBC.PRE.assertTrue("migrations/code must be a directory!", codeDir.isDirectory()); DBC.PRE.assertTrue("migrations/code must be a directory!", codeDir.isDirectory());
File[] realmMigrations = codeDir File[] realmMigrations = codeDir.listFiles((FileFilter) path -> realmNames.contains(path.getName()));
.listFiles((FileFilter) path -> currentVersions.containsKey(path.getName()));
for (File realmMigration : realmMigrations) { for (File realmMigration : realmMigrations) {
String realm = realmMigration.getName(); String realm = realmMigration.getName();
@ -252,11 +249,11 @@ public class Migrations {
return migrationsByRealm; return migrationsByRealm;
} }
public MapOfLists<String, Version> getMigrationsToRun() { public MapOfLists<String, Version> getMigrationsToRun(Map<String, Version> currentVersions) {
MapOfLists<String, Version> migrationsToRun = new MapOfLists<>(); MapOfLists<String, Version> migrationsToRun = new MapOfLists<>();
for (Entry<String, Version> entry : this.currentVersions.entrySet()) { for (Entry<String, Version> entry : currentVersions.entrySet()) {
String realm = entry.getKey(); String realm = entry.getKey();
Version nextPossibleVersion = entry.getValue().add(0, 0, 1); Version nextPossibleVersion = entry.getValue().add(0, 0, 1);
CodeMigration currentCodeMigration = new CodeMigration(realm, nextPossibleVersion, null); CodeMigration currentCodeMigration = new CodeMigration(realm, nextPossibleVersion, null);

View File

@ -72,24 +72,30 @@ public class MigrationsHandler extends StrolchComponent {
if (!this.migrationsPath.isDirectory()) if (!this.migrationsPath.isDirectory())
return new MapOfLists<>(); return new MapOfLists<>();
Map<String, Version> currentVersions = getCurrentVersions(cert); Migrations migrations = new Migrations(getContainer(), getContainer().getRealmNames(), this.verbose);
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
migrations.parseMigrations(this.migrationsPath); migrations.parseMigrations(this.migrationsPath);
Map<String, Version> currentVersions = getCurrentVersions(cert);
this.migrations = migrations; this.migrations = migrations;
return this.migrations.getMigrationsToRun(); return this.migrations.getMigrationsToRun(currentVersions);
} }
public void runMigrations(Certificate cert) { public void runMigrations(Certificate cert) {
queryMigrationsToRun(cert); if (!this.migrationsPath.isDirectory())
this.migrations.runMigrations(cert); return;
Migrations migrations = new Migrations(getContainer(), getContainer().getRealmNames(), this.verbose);
migrations.parseMigrations(this.migrationsPath);
Map<String, Version> currentVersions = getCurrentVersions(cert);
this.migrations.runMigrations(cert, currentVersions);
} }
public void runCodeMigrations(Certificate cert, MapOfLists<String, CodeMigration> codeMigrationsByRealm) { public void runCodeMigrations(Certificate cert, MapOfLists<String, CodeMigration> codeMigrationsByRealm) {
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(), getContainer().getRealmNames(), this.verbose);
this.migrations = migrations; this.migrations = migrations;
migrations.runCodeMigrations(cert, codeMigrationsByRealm); migrations.runCodeMigrations(cert, currentVersions, codeMigrationsByRealm);
} }
public boolean isVerbose() { public boolean isVerbose() {
@ -108,13 +114,7 @@ public class MigrationsHandler extends StrolchComponent {
this.migrationsPath = runtimeConf.getDataDir(MigrationsHandler.class.getName(), PATH_MIGRATIONS, false); this.migrationsPath = runtimeConf.getDataDir(MigrationsHandler.class.getName(), PATH_MIGRATIONS, false);
if (this.runMigrationsOnStart && this.migrationsPath.exists()) { if (this.runMigrationsOnStart && this.migrationsPath.exists()) {
CurrentMigrationVersionQuery query = new CurrentMigrationVersionQuery(getContainer()); Migrations migrations = new Migrations(getContainer(), getContainer().getRealmNames(), this.verbose);
PrivilegeHandler privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
QueryCurrentVersionsAction action = new QueryCurrentVersionsAction(query);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action);
Map<String, Version> currentVersions = query.getCurrentVersions();
Migrations migrations = new Migrations(getContainer(), currentVersions, this.verbose);
migrations.parseMigrations(this.migrationsPath); migrations.parseMigrations(this.migrationsPath);
this.migrations = migrations; this.migrations = migrations;
@ -134,8 +134,13 @@ public class MigrationsHandler extends StrolchComponent {
if (this.runMigrationsOnStart && this.migrations != null) { if (this.runMigrationsOnStart && this.migrations != null) {
CurrentMigrationVersionQuery query = new CurrentMigrationVersionQuery(getContainer());
QueryCurrentVersionsAction queryAction = new QueryCurrentVersionsAction(query);
PrivilegeHandler privilegeHandler = getContainer().getComponent(PrivilegeHandler.class); PrivilegeHandler privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
RunMigrationsAction action = new RunMigrationsAction(this.migrations); privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, queryAction);
Map<String, Version> currentVersions = query.getCurrentVersions();
RunMigrationsAction action = new RunMigrationsAction(this.migrations, currentVersions);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action); privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, action);
} }
@ -172,22 +177,23 @@ public class MigrationsHandler extends StrolchComponent {
return; return;
} }
Migrations migrations = new Migrations(getContainer(), getContainer().getRealmNames(),
MigrationsHandler.this.verbose);
migrations.parseMigrations(MigrationsHandler.this.migrationsPath);
CurrentMigrationVersionQuery query = new CurrentMigrationVersionQuery(getContainer()); CurrentMigrationVersionQuery query = new CurrentMigrationVersionQuery(getContainer());
PrivilegeHandler privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
QueryCurrentVersionsAction queryAction = new QueryCurrentVersionsAction(query); QueryCurrentVersionsAction queryAction = new QueryCurrentVersionsAction(query);
PrivilegeHandler privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, queryAction); privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, queryAction);
Map<String, Version> currentVersions = query.getCurrentVersions(); Map<String, Version> currentVersions = query.getCurrentVersions();
Migrations migrations = new Migrations(getContainer(), currentVersions, MigrationsHandler.this.verbose);
migrations.parseMigrations(MigrationsHandler.this.migrationsPath);
MigrationsHandler.this.migrations = migrations; MigrationsHandler.this.migrations = migrations;
if (migrations.getMigrationsToRun(currentVersions).isEmpty()) {
if (migrations.getMigrationsToRun().isEmpty()) {
if (verbose) if (verbose)
logger.info("There are no migrations required at the moment!"); logger.info("There are no migrations required at the moment!");
} else { } else {
RunMigrationsAction runMigrationsAction = new RunMigrationsAction(MigrationsHandler.this.migrations); RunMigrationsAction runMigrationsAction = new RunMigrationsAction(MigrationsHandler.this.migrations,
currentVersions);
privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, runMigrationsAction); privilegeHandler.runAsSystem(RealmHandler.SYSTEM_USER_AGENT, runMigrationsAction);
} }
} }

View File

@ -1,7 +1,10 @@
package li.strolch.migrations; package li.strolch.migrations;
import java.util.Map;
import ch.eitchnet.privilege.handler.SystemUserAction; import ch.eitchnet.privilege.handler.SystemUserAction;
import ch.eitchnet.privilege.model.PrivilegeContext; import ch.eitchnet.privilege.model.PrivilegeContext;
import ch.eitchnet.utils.Version;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -9,16 +12,19 @@ import ch.eitchnet.privilege.model.PrivilegeContext;
public class RunMigrationsAction implements SystemUserAction { public class RunMigrationsAction implements SystemUserAction {
private Migrations migrations; private Migrations migrations;
private Map<String, Version> currentVersions;
/** /**
* @param migrations * @param migrations
* @param currentVersions
*/ */
public RunMigrationsAction(Migrations migrations) { public RunMigrationsAction(Migrations migrations, Map<String, Version> currentVersions) {
this.migrations = migrations; this.migrations = migrations;
this.currentVersions = currentVersions;
} }
@Override @Override
public void execute(PrivilegeContext privilegeContext) { public void execute(PrivilegeContext privilegeContext) {
this.migrations.runMigrations(privilegeContext.getCertificate()); this.migrations.runMigrations(privilegeContext.getCertificate(), currentVersions);
} }
} }