Baton pays homage to Burton Baton, a fabulous beer that is a blend of a traditional English-style Old Ale and a modern imperial IPA. This "two thread" beer brings both source beers together into something arguably better than either beer was individually. Like the beer, this project blends your existing codebase with new migrations that helps modernize and keep your old code relevant. The result is more substantive and satisfying than either old or new code alone could accomplish.
Baton is a Maven plugin that helps generically provide lightweight, convention-driven migration capabilities to make it easier to evolve your projects over time. It makes it easy to embed automated migrations into your Maven build. Additionally, by using classpath-driven configurations, version upgrades of your migration jars control what gets applied when. This makes it easy to release a set of artifacts that correspond to a general release of a framework and get just the migration you need at the appropriate time.
In order to use Baton, the following prerequisites must be installed:
- Maven 3.9+
- Java 11+
Creating and applying migrations is easy. Follow these steps and you'll be migrating your code in no time.
To start, implement your migration by extending AbstractMigration, adding your specific logic to:
- Determine if the migration applies to a given file
- If applicable, perform the migration
These steps are outlined below is a simple class that migrates files ending in .foo
to now end in .bar
:
public class FooToBarMigration extends AbstractMigration {
@Override
protected boolean shouldExecuteOnFile(File file) {
return file.getName().contains(".foo");
}
@Override
protected boolean performMigration(File file) {
FileUtils.moveFile(file, new File(file.getParent(), file.getName().replace(".foo", ".bar")));
return true;
}
}
The following Java classes in org.technologybrewery.baton.util
can be leveraged to easily implement common migration logic into your extension:
CommonUtils
FileUtils
pom.PomHelper
pom.PomModifications
pom.LocationAwareMavenReader
With a migration to apply, we both configure and tailor that use through a simple json file. This file can live anywhere
in Baton's classpath and is named migrations.json
by default.
The following example configures the migration to only look in the ./src/main/resources/legacy
folder while leaving
one file, original-specification-example.foo
alone.
[
{
"group": "group-1",
"migrations": [
"name": "upgrade-foo-extension-files-migration",
"implementation": "org.technologybrewery.baton.example.FooToBarMigration",
"fileSets": [
{
"includes": ["**/legacy/*.foo"],
"excludes": ["original-specification-example.foo"]
}
]
]
}
]
Migration files contain groups of migrations. Each group is executed in its entirety before moving on to the next. Groups are executed in the order in which they appear in the migration file.
Example of ordered groups
[
{
"group": "foo",
"type": "ordered",
"migrations": [
{
"name": "migration_1"
}
]
},
{
"group": "bar",
"type": "ordered",
"migrations": [
{
"name": "migration_2"
}
]
}
]
If Baton is executed with this migrations.json, groups would be processed in the order in which they were specified (foo, bar)
Migrations within a group can be ordered manually or by version.
Example of manually ordered migrations
[
{
"group": "foo",
"type": "ordered",
"migrations": [
{
"name": "migration_1"
},
{
"name": "migration_2"
},
{
"name": "migration_3"
},
{
"name": "migration_4"
}
]
}
]
If Baton is executed with this migration.json, migrations would be processed in the order in which they were specified (migration_1, migration_2, migration_3, migration_4)
Example of version ordered migrations
[
{
"group": "bar",
"type": "versioned",
"migrations": [
{
"name": "migration_1",
"version": "3.0.0"
},
{
"name": "migration_2",
"version": "2.0.0"
},
{
"name": "migration_3",
"version": "1.0.0"
},
{
"name": "migration_4",
"version": "5.0.0"
}
]
}
]
If Baton is executed with this migration.json, migrations would be processed by the version number in ascending order (migration_3, migration_2, migration_1, migration_4)
The last step is to add baton-maven-plugin
to your Maven build process just like any other plugin.
The following example highlight the default plugin configuration as well as a notional dependency containing both the Migration class and configuration json file above.
<plugin>
<groupId>org.technologybrewery.baton</groupId>
<artifactId>baton-maven-plugin</artifactId>
<version>0.1.0</version>
<extensions>true</extensions>
<executions>
<execution>
<id>default</id>
<goals>
<goal>baton-migrate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.technologybrewery.baton.example</groupId>
<artifactId>example-migration-configuration</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</plugin>
When executing your next Maven build, Baton will execute as part of the initialize
build lifecycle and output similar
to the following will result:
[INFO] --- baton:0.1.0:baton-migrate (default) @ toml-file-update ---
[INFO] Loading migrations from: jar:file:/Users/dfh/.m2/repository/org/technologybrewery/baton/example/example-migration-configuration/1.0.0/example-migration-configuration-1.0.0-SNAPSHOT.jar!/migrations.json
[INFO] Found 1 migrations
[INFO] Migrations Processed: 1, Successfully Migrated Files: 1, Unsuccessfully Migrated Files: 0
All Baton configurations may be set either via the baton-maven-plugin
's <configuration>
definition, Maven POM
properties, or -D
on the command line and follow a consistent naming pattern for the different configuration
approaches. For setting configurations via POM properties or -D
on the command line, all configuration keys may be
prepended with baton.
. For example, migrationsConfigurationFile
controls the file name that Baton will use to find
migrations and may be configured using the following approaches:
- Plugin
<configuration>
<plugin>
<groupId>org.technologybrewery.baton</groupId>
<artifactId>baton-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<migrationsConfigurationFile>alternative-migrations.json</migrationsConfigurationFile>
</configuration>
</plugin>
-D
via command line
mvn clean install -DmigrationsConfigurationFile=alternative-migrations.json
- POM properties
<properties>
<migrationsConfigurationFile>alternative-migrations.json</migrationsConfigurationFile>
</properties>
NOTE: The above list's order reflects the precedence in which configurations will be applied. For example,
configuration values that are specified in the plugin's <configuration>
definition will always take precedence, while
system properties via the command line (-D
) will take precedence over <properties>
definitions.
The desired base directory to use when Baton looks for files on which to perform migrations. By default, only pom.xml
and *.toml
from the base directory will be included.
Default: ${project.basedir}
The desired source directory to include when Baton looks for files on which to perform migrations.
Default: ${project.basedir}/src
The desired test directory to include, if desired, when Baton looks for files on which to perform migrations.
Default: None
A standard Maven fileSets block that can be used to include any sets of file you desire. If used, no additional defaulting of fileSet values will be performed by Baton and only these filesets will be used for the plugin's execution.
NOTE: File set configurations specified in your migrations.json
will override those specified to your plugin at
large.
Default: None
The configurations file name to look for in the classpath (all matches will be used).
Default: migrations.json
Controls if backups of original files prior to migration are saved.
Default: true
Customizes the location where backups of original files prior to migration are stored By default, the system temp directory is used. This will result in the files in that temp location being subjected to your local computer's policy for cleaning (often either on reboot or never). Changing this property to the location of your choosing will allow greater control.
Default: system temp directory
The number of backups for original files prior to migration to keep. Once this number is hit, the backup files will rotate, with the oldest being purged.
It is worth noting that this controls number of old files kept, not the total number files kept. For instance, if set to 5, you will have 5 old backup files as well as one active backup. This is based on how the file rotation library works.
Default: 10
Used to filter out version ordered migrations. All migrations with versions less than the minimum version will be skipped.
Default: 0.0.0
Format: [Number].[Number].[Number]
When specifying your configurations in migrations.json
or your custom migrationsConfigurationFile
file name, the
following options are available.
The name of the group. Simply used to give a distinctive identifier to a group.
Required? true
Default: None
The migration ordering type.
Required? true
Default: None
Values: ordered | versioned
List of all the migrations in a group. This must be a non-zero list of migrations.
Required? true
Default: None
The name of the migration to perform. As of 0.1.0, name
is not particularly impactful. However, in subsequent
releases it will gain importance as a means to order migration execution (convention-driven in the style of Flyway) as
well as inactivate specific migrations.
Required? true
Default: None
The version of the migration
Required? Only if type is set to versioned
in the parent group configuration
Default: None
Format: [Number].[Number].[Number]
The description of the migration. This is intended to provide context on why the migration is needed.
Required? false
Default: None
The fully qualified Java class name that will perform the migration. This MUST implement the
org.technologybrewery.baton.Migration
interface, however it is recommended that it extend
org.technologybrewery.baton.AbstractMigration
to allow implementations to be more consistent and focus on migration
logic rather than Baton plumbing. The implementation MUST have a default constructor.
Required? true
Default: None
A Maven-inspired object that allows specification of common file sets. MUST be added as a list item.
Required? false
Default: None
The directory on which this file set should operate.
Required? false
Default: ./
(project base directory)
A list of specific inclusions following standard Maven conventions.
Required? false
Default: None
A list of specific exclusions following standard Maven conventions.
Required? false
Default: None
Whether to follow symlinks following standard Maven conventions.
Required? false
Default: None
If you are working on Baton, please be aware of some nuances in working with a plugin that defines a custom Maven build
lifecycle and packaging. examples
are utilized to immediately test the baton-maven-plugin
. If baton-maven-plugin
has not been previously built, developers must manually build the baton-maven-plugin
and then execute another, separate
build of examples
(and any other baton module) to use the updated baton-maven-plugin
. That said, once an initial
build has been completed, a single build may be used to build baton-maven-plugin
and apply the updates to examples
.
To assist, there are two profiles available in the build:
mvn clean install -Pbootstrap
: Builds the baton-maven-plugin such that it may be utilized within subsequent builds.mvn clean install -Pdefault
: (ACTIVE BY DEFAULT --Pdefault
does not need to be specified) builds all modules. Developers may use this profile to build and apply changes to existingbaton-maven-plugin
Mojo classes.