In the previous post i mentioned all the things you need to take into consideration when creating a migration process for you application.
in this post i will show an example of a migration framework that we developed based on our needs using the spring boot framework.
the basic idea is to register 2 Application Listeners in the spring boot application so that each will be invoked to run the corresponding phase and block the application startup until it finish.
List<ApplicationListener> applicationListeners = Arrays.asList( new ApplicationStartingListener(), new ApplicationContextReadyListener() ); SpringApplication application = new SpringApplication(new Object[]{MyApplication.class});
application.addListeners(applicationListeners);
the first listener is:
ApplicationListener<ApplicationEnvironmentPreparedEvent> this listener will be invoked when the Spring Environment is ready but before any of the @beans are initialised. this is the perfect phase to make some configuration files changes.
the second listener is:
ApplicationListener<ContextRefreshedEvent> this listener will be invoked when the spring context is refreshed, all beans are constructed and registered but the server is still not responding to request from the outside world. this is the phase at which we decided to make changes in the DataBase.
each listener looks something like this:
String newVersion = // get the version from application.properties String previousVersion = // get the previous version from versions.properties if (!VersionsComparator.equals(newVersion, previousVersion)) { MigrationService migrationService = new MigrationService(newVersion,
previousVersion);
migrationService.runMigration(); }
* versions.properties is a file we keep updating each time the application starts and holds the current running version. the idea is that when the system is updated, his file holds the version from the previous build so we can use it as a reference.
the migrator service holds all the existing migration version and the MigrationService.runMigration() invokes only the relevant migrators from previous version to the newVersion (this is the reason we pass them as params to the constructor)
migrators = Arrays.asList( new DataBaseMigratorV1_1_0(), new DataBaseMigratorV1_2_0(), new DataBaseMigratorV1_3_0(), new DataBaseMigratorV1_4_0(), new DataBaseMigratorV1_4_1() );
when each migator look something like:
public class DataBaseMigratorV1_1_0 implements DataBaseMigrator { @Override public String migratesFromVersion() { return "1.0.0"; } @Override public String migratesToVersion() { return "1.1.0"; } @Override public boolean migrate() { // **** do what ever migration you want return true; } }
And so when the application is starting (after the update of the jars/binaries) spring boot will invoke the listener that uses the migration service that triggers all the relevant migrators:
By the end of the process when all the migrators where executed successfully the application can start with all the data and configuration up to date.
By the end of the process when all the migrators where executed successfully the application can start with all the data and configuration up to date.
