org.flywaydb.ant.AbstractFlywayTask Maven / Gradle / Ivy
Show all versions of flyway-ant Show documentation
/*
* Copyright 2017-2020 Tomas Tulka
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.ant;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.Location;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.flywaydb.core.api.logging.Log;
import org.flywaydb.core.api.logging.LogFactory;
import org.flywaydb.core.internal.jdbc.DriverDataSource;
import org.flywaydb.core.internal.util.ExceptionUtils;
import org.flywaydb.core.internal.util.StringUtils;
/**
* Base class for all Flyway Ant tasks.
*/
@SuppressWarnings({"UnusedDeclaration"})
public abstract class AbstractFlywayTask extends Task {
/**
* Property name prefix for placeholders that are configured through properties.
*/
private static final String PLACEHOLDERS_PROPERTY_PREFIX = "flyway.placeholders.";
/**
* Flyway Configuration.
*/
private FluentConfiguration flywayConfig;
/**
* Logger.
*/
protected Log log;
/**
* The classpath used to load the JDBC driver and the migrations.
*/
private Path classPath;
/**
* The fully qualified classname of the jdbc driver to use to connect to the database.
Also configurable with Ant Property: ${flyway.driver}
*/
private String driver;
/**
* The jdbc url to use to connect to the database.
Also configurable with Ant Property: ${flyway.url}
*/
private String url;
/**
* The user to use to connect to the database. (default: blank)
Also configurable with Ant Property: ${flyway.user}
The credentials can be
* specified by user/password or serverId from settings.xml
*/
private String user;
/**
* The password to use to connect to the database. (default: blank)
Also configurable with Ant Property: ${flyway.password}
*/
private String password;
/**
* Locations on the classpath to scan recursively for migrations. Locations may contain both sql and java-based migrations. (default: db.migration)
Also
* configurable with Ant Property: ${flyway.locations}
*/
private String[] locations;
/**
* The custom MigrationResolvers to be used in addition or as replacement to the built-in (as determined by the skipDefaultResolvers property) ones for
* resolving Migrations to apply. (default: none)
*/
private String[] resolvers;
/**
* The callbacks for lifecycle notifications. (default: none)
*/
private String[] callbacks;
/**
* A map of <placeholder, replacementValue> to apply to sql migration scripts.
*/
private Map placeholders;
/**
* @param classpath The classpath used to load the JDBC driver and the migrations.
Also configurable with Ant Property: ${flyway.classpath}
*/
public void setClasspath(Path classpath) {
this.classPath = classpath;
}
/**
* @param classpathref The reference to the classpath used to load the JDBC driver and the migrations.
Also configurable with Ant Property:
* ${flyway.classpathref}
*/
public void setClasspathref(Reference classpathref) {
Path classPath = new Path(getProject());
classPath.setRefid(classpathref);
this.classPath = classPath;
}
/**
* @param driver The fully qualified classname of the jdbc driver to use to connect to the database.
By default, the driver is autodetected based on the
* url.
Also configurable with Ant Property: ${flyway.driver}
*/
public void setDriver(String driver) {
this.driver = driver;
}
/**
* @param url The jdbc url to use to connect to the database.
Also configurable with Ant Property: ${flyway.url}
*/
public void setUrl(String url) {
this.url = url;
}
/**
* @param user The user to use to connect to the database.
Also configurable with Ant Property: ${flyway.user}
*/
public void setUser(String user) {
this.user = user;
}
/**
* @param password The password to use to connect to the database. (default: blank)
Also configurable with Ant Property: ${flyway.password}
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @param connectRetries The maximum number of retries when attempting to connect to the database. (default: 0)
Also configurable with Ant
* Property: ${flyway.connectRetries}
*/
public void setConnectRetries(int connectRetries) {
this.flywayConfig.connectRetries(connectRetries);
}
/**
* @param initSql The SQL statements to run to initialize a new database connection immediately after opening it. (default: none)
Also
* configurable with Ant Property: ${flyway.initSql}
*/
public void setInitSql(String initSql) {
this.flywayConfig.initSql(initSql);
}
/**
* @param schemas Comma-separated list of the schemas managed by Flyway. These schema names are case-sensitive.
(default: The default schema for the
* datasource connection) Consequences:
- The first schema in the list will be automatically set as the default one during the
* migration.
- The first schema in the list will also be the one containing the metadata table.
- The schemas will be cleaned
* in the order of this list.
Also configurable with Ant Property: ${flyway.schemas}
*/
public void setSchemas(String schemas) {
this.flywayConfig.schemas(StringUtils.tokenizeToStringArray(schemas, ","));
}
/**
* @param createSchemas Whether Flyway should attempt to create the schemas specified in the schemas property.(default: true)
Also configurable with Ant Property: ${flyway.createSchemas}
*/
public void setCreateSchemas(boolean createSchemas) {
this.flywayConfig.createSchemas(createSchemas);
}
/**
* @param defaultSchema Sets the default schema managed by Flyway. If not specified, but flyway.schemas is, we use the first schema in that list.
* Also configurable with Ant Property: ${flyway.defaultSchema}
*/
public void setDefaultSchema(String defaultSchema) {
this.flywayConfig.defaultSchema(defaultSchema);
}
/**
* @param resolvers The custom MigrationResolvers to be used in addition to the built-in ones for resolving Migrations to apply. (default: none)
*/
public void setResolvers(String resolvers) {
this.resolvers = StringUtils.tokenizeToStringArray(resolvers, ",");
}
/**
* @param skipDefaultResolvers Whether built-int resolvers should be skipped. If true, only custom resolvers are used.(default: false)
Also
* configurable with Ant Property: ${flyway.skipDefaultResolvers}
*/
public void setSkipDefaultResolvers(boolean skipDefaultResolvers) {
this.flywayConfig.skipDefaultResolvers(skipDefaultResolvers);
}
/**
* @param callbacks A comma-separated list of fully qualified FlywayCallback implementation class names. These classes will be instantiated and wired into
* the Flyway lifecycle notification events.
*/
public void setCallbacks(String callbacks) {
this.callbacks = StringUtils.tokenizeToStringArray(callbacks, ",");
}
/**
* @param skipDefaultCallbacks Whether built-int callbacks should be skipped. If true, only custom callbacks are used.(default: false)
Also
* configurable with Ant Property: ${flyway.skipDefaultCallbacks}
*/
public void setSkipDefaultCallbacks(boolean skipDefaultCallbacks) {
this.flywayConfig.skipDefaultCallbacks(skipDefaultCallbacks);
}
/**
* @param table The name of the schema metadata table that will be used by Flyway.
By default (single-schema mode) the metadata table is placed
* in the default schema for the connection provided by the datasource.
When the flyway.schemas property is set (multi-schema
* mode), the metadata table is placed in the first schema of the list.
(default: schema_version)
Also configurable with Ant Property:
* ${flyway.table}
*/
public void setTable(String table) {
this.flywayConfig.table(table);
}
/**
* @param baselineVersion The version to tag an existing schema with when executing baseline. (default: 1)
Also configurable with Ant Property:
* ${flyway.baselineVersion}
*/
public void setBaselineVersion(String baselineVersion) {
this.flywayConfig.baselineVersion(baselineVersion);
}
/**
* @param baselineDescription The description to tag an existing schema with when executing baseline. (default: << Flyway Baseline >>)
Also
* configurable with Ant Property: ${flyway.baselineDescription}
*/
public void setBaselineDescription(String baselineDescription) {
this.flywayConfig.baselineDescription(baselineDescription);
}
/**
* Whether to allow mixing transactional and non-transactional statements within the same migration.
Also configurable with Ant Property:
* ${flyway.mixed}
*
* @param mixed {@code true} if mixed migrations should be allowed. {@code false} if an error should be thrown instead. (default: {@code false})
*/
public void setMixed(boolean mixed) {
this.flywayConfig.mixed(mixed);
}
/**
* Whether to group all pending migrations together in the same transaction when applying them (only recommended for databases with support for DDL
* transactions).
*
* Also configurable with Ant Property: ${flyway.group}
*
* @param group {@code true} if migrations should be grouped. {@code false} if they should be applied individually instead. (default: {@code false})
*/
public void setGroup(boolean group) {
this.flywayConfig.group(group);
}
/**
* Whether to stream SQL migrations when executing them. Streaming doesn't load the entire migration in memory at once. Instead each statement is loaded
* individually. This is particularly useful for very large SQL migrations composed of multiple MB or even GB of reference data, as this dramatically
* reduces Flyway's memory consumption.
*
* Also configurable with Ant Property: ${flyway.stream}
*
* @param stream {@code true} if migrations should be streamed. {@code false} if they should be loaded individually instead. (default: {@code false})
*/
public void setStream(boolean stream) {
this.flywayConfig.stream(stream);
}
/**
* Whether to batch SQL statements when executing them. Batching can save up to 99 percent of network roundtrips by sending up to 100 statements at once
* over the network to the database, instead of sending each statement individually. This is particularly useful for very large SQL migrations composed of
* multiple MB or even GB of reference data, as this can dramatically reduce the network overhead. This is supported for INSERT, UPDATE, DELETE, MERGE and
* UPSERT statements. All other statements are automatically executed without batching.
*
Flyway Pro and Flyway Enterprise only
*
* Also configurable with Ant Property: ${flyway.batch}
*
* @param batch {@code true} if SQL statements should be batched. {@code false} if they should be sent individually instead. (default: {@code false})
*/
public void setBatch(boolean batch) {
this.flywayConfig.batch(batch);
}
/**
* The username that will be recorded in the metadata table as having applied the migration.
Also configurable with Ant Property:
* ${flyway.installedBy}
*
* @param installedBy The username or blank for the current database user of the connection. (default: blank).
*/
public void setInstalledBy(String installedBy) {
this.flywayConfig.installedBy(installedBy);
}
/**
* @param encoding The encoding of Sql migrations. (default: UTF-8)
Also configurable with Ant Property: ${flyway.encoding}
*/
public void setEncoding(String encoding) {
this.flywayConfig.encoding(encoding);
}
/**
* Sql migrations have the following file name structure: prefixVERSIONseparatorDESCRIPTIONsuffix, which using the defaults translates to
* V1_1__My_description.sql
*
* @param sqlMigrationPrefix The file name prefix for Sql migrations (default: V).
Also configurable with Ant Property: ${flyway.sqlMigrationPrefix}
*/
public void setSqlMigrationPrefix(String sqlMigrationPrefix) {
this.flywayConfig.sqlMigrationPrefix(sqlMigrationPrefix);
}
/**
* Repeatable sql migrations have the following file name structure: prefixSeparatorDESCRIPTIONsuffix , which using the defaults translates to
* R__My_description.sql
*
* @param repeatableSqlMigrationPrefix The file name prefix for repeatable sql migrations (default: R)
Also configurable with Ant Property:
* ${flyway.repeatableSqlMigrationPrefix}
*/
public void setRepeatableSqlMigrationPrefix(String repeatableSqlMigrationPrefix) {
this.flywayConfig.repeatableSqlMigrationPrefix(repeatableSqlMigrationPrefix);
}
/**
* Undo SQL migrations have the following file name structure: prefixVERSIONseparatorDESCRIPTIONsuffix, which using the defaults translates to
* U1.1__My_description.sql
*
* @param undoSqlMigrationPrefix The file name prefix for undo SQL migrations (default: U)
Also configurable with Ant Property:
* ${flyway.undoSqlMigrationPrefix }
*/
public void setUndoSqlMigrationPrefix(String undoSqlMigrationPrefix) {
this.flywayConfig.undoSqlMigrationPrefix(undoSqlMigrationPrefix);
}
/**
* Sql migrations have the following file name structure: prefixVERSIONseparatorDESCRIPTIONsuffix , which using the defaults translates to
* V1_1__My_description.sql
*
* @param sqlMigrationSeparator The file name separator for Sql migrations (default: V)
Also configurable with Ant Property:
* ${flyway.sqlMigrationPrefix}
*/
public void setSqlMigrationSeparator(String sqlMigrationSeparator) {
this.flywayConfig.sqlMigrationSeparator(sqlMigrationSeparator);
}
/**
* Sql migrations have the following file name structure: prefixVERSIONseparatorDESCRIPTIONsuffix , which using the defaults translates to
* V1_1__My_description.sql
Multiple suffixes (like .sql,.pkg,.pkb) can be specified for easier compatibility with other tools such as editors with
* specific file associations.
*
* @param sqlMigrationSuffixes The file name suffixes for SQL migrations, comma-separated. (default: .sql)
Also configurable with Ant Property:
* ${flyway.sqlMigrationSuffixes}
*/
public void setSqlMigrationSuffixes(String sqlMigrationSuffixes) {
this.flywayConfig.sqlMigrationSuffixes(StringUtils.tokenizeToStringArray(sqlMigrationSuffixes, ","));
}
/**
* Whether to ignore migration files whose names do not match the naming conventions.
* If false, files with invalid names are ignored and Flyway continues normally. If true, Flyway fails fast and lists the offending files.
*
* @param validateMigrationNaming Whether to ignore migration files whose names do not match the naming conventions. (default: false)
Also configurable
* with Ant Property: ${flyway.validateMigrationNaming}
*/
public void setValidateMigrationNaming(boolean validateMigrationNaming) {
this.flywayConfig.validateMigrationNaming(validateMigrationNaming);
}
/**
* @param target The target version up to which Flyway should consider migrations. Migrations with a higher version number will be ignored. The special
* value {@code current} designates the current version of the schema. (default: the latest version)
Also configurable with Ant Property:
* ${flyway.target}
*/
public void setTarget(String target) {
this.flywayConfig.target(target);
}
/**
* @param cleanOnValidationError Whether to automatically call clean or not when a validation error occurs. (default: {@code false})
This is
* exclusively intended as a convenience for development. Even tough we strongly recommend not to change migration scripts
* once they have been checked into SCM and run, this provides a way of dealing with this case in a smooth manner. The
* database will be wiped clean automatically, ensuring that the next migration will bring you back to the state checked into
* SCM.
Warning ! Do not enable in production !
Also configurable with Ant Property:
* ${flyway.cleanOnValidationError}
*/
public void setCleanOnValidationError(boolean cleanOnValidationError) {
this.flywayConfig.cleanOnValidationError(cleanOnValidationError);
}
/**
* @param cleanDisabled Whether to disable clean. (default: {@code false}) This is especially useful for production environments where running clean can
* be quite a career limiting move.
*/
public void setCleanDisabled(boolean cleanDisabled) {
this.flywayConfig.cleanDisabled(cleanDisabled);
}
/**
* @param outOfOrder Allows migrations to be run "out of order" (default: {@code false}). If you already have versions 1 and 3 applied, and now a version
* 2 is found, it will be applied too instead of being ignored.
Also configurable with Ant Property: ${flyway.outOfOrder}
*/
public void setOutOfOrder(boolean outOfOrder) {
this.flywayConfig.outOfOrder(outOfOrder);
}
/**
* @param placeholderReplacement Whether placeholders should be replaced. (default: true)
Also configurable with Ant Property:
* ${flyway.placeholderReplacement}
*/
public void setPlaceholderReplacement(boolean placeholderReplacement) {
this.flywayConfig.placeholderReplacement(placeholderReplacement);
}
/**
* @param placeholderPrefix The prefix of every placeholder. (default: ${ )
Also configurable with Ant Property: ${flyway.placeholderPrefix}
*/
public void setPlaceholderPrefix(String placeholderPrefix) {
this.flywayConfig.placeholderPrefix(placeholderPrefix);
}
/**
* @param placeholderSuffix The suffix of every placeholder. (default: } )
Also configurable with Ant Property: ${flyway.placeholderSuffix}
*/
public void setPlaceholderSuffix(String placeholderSuffix) {
this.flywayConfig.placeholderSuffix(placeholderSuffix);
}
/**
* Ignore missing migrations when reading the metadata table. These are migrations that were performed by an older deployment of the application that are no
* longer available in this version. For example: we have migrations available on the classpath with versions 1.0 and 3.0. The metadata table indicates that
* a migration with version 2.0 (unknown to us) has also been applied. Instead of bombing out (fail fast) with an exception, a warning is logged and Flyway
* continues normally. This is useful for situations where one must be able to deploy a newer version of the application even though it doesn't contain
* migrations included with an older one anymore.
*
* @param ignoreMissingMigrations {@code true} to continue normally and log a warning, {@code false} to fail fast with an exception. (default: {@code
* false})
*/
public void setIgnoreMissingMigrations(boolean ignoreMissingMigrations) {
this.flywayConfig.ignoreMissingMigrations(ignoreMissingMigrations);
}
/**
* Ignore ignored migrations when reading the schema history table. These are migrations that were added in between already migrated migrations in this
* version. For example: we have migrations available on the classpath with versions from 1.0 to 3.0. The schema history table indicates that version 1 was
* finished on 1.0.15, and the next one was 2.0.0. But with the next release a new migration was added to version 1: 1.0.16. Such scenario is ignored by
* migrate command, but by default is rejected by validate. When ignoreIgnoredMigrations is enabled, such case will not be reported by validate command.
* This is useful for situations where one must be able to deliver complete set of migrations in a delivery package for multiple versions of the product,
* and allows for further development of older versions.
Also configurable with Ant Property: ${flyway.ignoreIgnoredMigrations}
*
* @param ignoreIgnoredMigrations {@code true} to continue normally and log a warning, {@code false} to fail fast with an exception. (default: {@code
* false})
*/
public void setIgnoreIgnoredMigrations(boolean ignoreIgnoredMigrations) {
this.flywayConfig.ignoreIgnoredMigrations(ignoreIgnoredMigrations);
}
/**
* @param ignorePendingMigrations {@code true} Ignore pending migrations when reading the schema history table. (default: {@code false})
Also
* configurable with Ant Property: ${flyway.ignorePendingMigrations}
*/
public void setIgnorePendingMigrations(boolean ignorePendingMigrations) {
this.flywayConfig.ignorePendingMigrations(ignorePendingMigrations);
}
/**
* Whether to ignore future migrations when reading the metadata table. These are migrations that were performed by a newer deployment of the application
* that are not yet available in this version. For example: we have migrations available on the classpath up to version 3.0. The metadata table indicates
* that a migration to version 4.0 (unknown to us) has already been applied. Instead of bombing out (fail fast) with an exception, a warning is logged and
* Flyway continues normally. This is useful for situations where one must be able to redeploy an older version of the application after the database has
* been migrated by a newer one.
Also configurable with Ant Property: ${flyway.ignoreFutureMigrations}
*
* @param ignoreFutureMigrations {@code true} to continue normally and log a warning, {@code false} to fail fast with an exception. (default: {@code true})
*/
public void setIgnoreFutureMigrations(boolean ignoreFutureMigrations) {
this.flywayConfig.ignoreFutureMigrations(ignoreFutureMigrations);
}
/**
* @param validateOnMigrate Whether to automatically call validate or not when running migrate. (default: {@code true})
Also configurable with Ant
* Property: ${flyway.validateOnMigrate}
*/
public void setValidateOnMigrate(boolean validateOnMigrate) {
this.flywayConfig.validateOnMigrate(validateOnMigrate);
}
/**
* Whether to automatically call baseline when migrate is executed against a non-empty schema with no metadata table. This schema will then be baselined
* with the {@code initialVersion} before executing the migrations. Only migrations above {@code initialVersion} will then be applied.
This is useful
* for initial Flyway production deployments on projects with an existing DB.
Be careful when enabling this as it removes the safety net that ensures
* Flyway does not migrate the wrong database in case of a configuration mistake!
Also configurable with Ant Property: ${flyway.baselineOnMigrate}
*
* @param baselineOnMigrate {@code true} if baseline should be called on migrate for non-empty schemas, {@code false} if not. (default: {@code false})
*/
public void setBaselineOnMigrate(boolean baselineOnMigrate) {
this.flywayConfig.baselineOnMigrate(baselineOnMigrate);
}
/**
* @param errorOverrides Rules for the built-in error handling that lets you override specific SQL states and errors codes from error to warning or from
* warning to error, comma-separated. (default: *blank*)
Also configurable with Ant Property: ${flyway.errorOverrides}
*/
public void setErrorOverrides(String errorOverrides) {
this.flywayConfig.errorOverrides(StringUtils.tokenizeToStringArray(errorOverrides, ","));
}
/**
* @param dryRunOutput The file where to output the SQL statements of a migration dry run. (default: *Execute directly against the database*)
Also
* configurable with Ant Property: ${flyway.dryRunOutput}
*/
public void setDryRunOutput(String dryRunOutput) {
this.flywayConfig.dryRunOutput(dryRunOutput);
}
/**
* @param oracleSqlplus Whether to Flyway's support for Oracle SQL*Plus commands should be activated. (default: *false*)
Also configurable with Ant
* Property: ${flyway.oracleSqlplus}
*
Flyway Pro and Flyway Enterprise only
*/
public void setOracleSqlplus(boolean oracleSqlplus) {
this.flywayConfig.oracleSqlplus(oracleSqlplus);
}
/**
* @param oracleSqlplusWarn Whether Flyway should issue a warning instead of an error whenever it encounters an Oracle SQL*Plus statement it doesn't yet
* support. (default: *false*)
Also configurable with Ant Property: ${flyway.oracleSqlplusWarn}
*
Flyway Pro and Flyway Enterprise only
*/
public void setOracleSqlplusWarn(boolean oracleSqlplusWarn) {
this.flywayConfig.oracleSqlplusWarn(oracleSqlplusWarn);
}
/**
* @param licenseKey Flyway's license key. (default: *blank*)
Also configurable with Ant Property: ${flyway.licenseKey}
*/
public void setLicenseKey(String licenseKey) {
this.flywayConfig.licenseKey(licenseKey);
}
/**
* Adds placeholders from a nested <placeholders> element. Called by Ant.
*
* @param placeholders The fully configured placeholders element.
*/
public void addConfiguredPlaceholders(PlaceholdersElement placeholders) {
this.placeholders = placeholders.placeholders;
}
/**
* Do not use. For Ant itself.
*
* @param locationsElement The locations on the classpath.
*/
public void addConfiguredLocations(LocationsElement locationsElement) {
this.locations = locationsElement.locations.toArray(new String[locationsElement.locations.size()]);
}
/**
* Do not use. For Ant itself.
*
* @param resolversElement The resolvers on the classpath.
*/
public void addConfiguredResolvers(ResolversElement resolversElement) {
this.resolvers = resolversElement.resolvers.toArray(new String[resolversElement.resolvers.size()]);
}
/**
* Do not use. For Ant itself.
*
* @param callbacksElement The callbacks on the classpath.
*/
public void addConfiguredCallbacks(CallbacksElement callbacksElement) {
this.callbacks = callbacksElement.callbacks.toArray(new String[callbacksElement.callbacks.size()]);
}
/**
* Do not use. For Ant itself.
*
* @param schemasElement The schemas.
*/
public void addConfiguredSchemas(SchemasElement schemasElement) {
this.flywayConfig.schemas(schemasElement.schemas.toArray(new String[schemasElement.schemas.size()]));
}
/**
* Creates the datasource base on the provided parameters.
*
* @return The fully configured datasource.
*/
protected DataSource createDataSource() {
String driverValue = useValueIfPropertyNotSet(driver, "driver");
String urlValue = useValueIfPropertyNotSet(url, "url");
String userValue = useValueIfPropertyNotSet(user, "user");
String passwordValue = useValueIfPropertyNotSet(password, "password");
return new DriverDataSource(Thread.currentThread().getContextClassLoader(), driverValue, urlValue, userValue, passwordValue, null);
}
/**
* Retrieves a value either from an Ant property or if not set, directly.
*
* @param value The value to check.
* @param flywayProperty The flyway Ant property. Ex. 'url' for 'flyway.url'
* @return The value.
*/
protected String useValueIfPropertyNotSet(String value, String flywayProperty) {
String propertyValue = getProject().getProperty("flyway." + flywayProperty);
if (propertyValue != null) {
return propertyValue;
}
return value;
}
/**
* Retrieves a boolean value either from an Ant property or if not set, directly.
*
* @param value The boolean value to check.
* @param flywayProperty The flyway Ant property. Ex. 'url' for 'flyway.url'
* @return The boolean value.
*/
protected boolean useValueIfPropertyNotSet(boolean value, String flywayProperty) {
String propertyValue = getProject().getProperty("flyway." + flywayProperty);
if (propertyValue != null) {
return Boolean.parseBoolean(propertyValue);
}
return value;
}
/**
* Prepares the classpath this task runs in, so that it includes both the classpath for Flyway and the classpath for the JDBC drivers and migrations.
*/
private void prepareClassPath() {
Path classpath = getProject().getReference("flyway.classpath");
if (classpath != null) {
setClasspath(classpath);
} else {
Reference classpathRef = getProject().getReference("flyway.classpathref");
if (classpathRef != null) {
setClasspathref(classpathRef);
}
}
ClassLoader classLoader = new AntClassLoader(getClass().getClassLoader(), getProject(), classPath);
Thread.currentThread().setContextClassLoader(classLoader);
}
@Override
public void init() throws BuildException {
AntLogCreator.INSTANCE.setAntProject(getProject());
LogFactory.setLogCreator(AntLogCreator.INSTANCE);
log = LogFactory.getLog(getClass());
prepareClassPath();
flywayConfig = Flyway.configure(Thread.currentThread().getContextClassLoader());
locations = locationsToStrings(flywayConfig.getLocations());
placeholders = flywayConfig.getPlaceholders();
}
@Override
public void execute() throws BuildException {
try {
// first, load configuration from the environment
flywayConfig.configuration(System.getProperties());
// second, load configuration from system properties
Properties projectProperties = new Properties();
projectProperties.putAll(getProject().getProperties());
flywayConfig.configuration(projectProperties);
// last, load configuration from build script properties
flywayConfig.dataSource(createDataSource());
if (resolvers != null) {
flywayConfig.resolvers(resolvers);
}
if (callbacks != null) {
flywayConfig.callbacks(callbacks);
}
flywayConfig.locations(getLocations());
flywayConfig.placeholders(loadPlaceholdersFromProperties(flywayConfig.getPlaceholders(), getProject().getProperties()));
doExecute(flywayConfig.load());
} catch (Exception e) {
throw new BuildException("Flyway Error: " + e.toString(), ExceptionUtils.getRootCause(e));
}
}
/**
* Executes this task.
*
* @param flyway The flyway instance to operate on.
* @throws Exception any exception
*/
protected abstract void doExecute(Flyway flyway) throws Exception;
/**
* @return The locations configured through Ant.
*/
private String[] getLocations() {
String[] locationsVal = locations;
String locationsProperty = getProject().getProperty("flyway.locations");
if (locationsProperty != null) {
locationsVal = StringUtils.tokenizeToStringArray(locationsProperty, ",");
}
//Adjust relative locations to be relative from Ant's basedir.
File baseDir = getProject().getBaseDir();
for (int i = 0; i < locationsVal.length; i++) {
locationsVal[i] = adjustRelativeFileSystemLocationToBaseDir(baseDir, locationsVal[i]);
}
return locationsVal;
}
private String[] locationsToStrings(Location[] locations) {
String[] locationsString = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
locationsString[i] = locations[i].getDescriptor();
}
return locationsString;
}
/**
* Adjusts a relative filesystem location to Ant's basedir. All other locations are left untouched.
*
* @param baseDir Ant's basedir.
* @param locationStr The location to adjust.
* @return The adjusted location.
*/
/* private -> testing */
static String adjustRelativeFileSystemLocationToBaseDir(File baseDir, String locationStr) {
Location location = new Location(locationStr);
if (location.isFileSystem() && !new File(location.getPath()).isAbsolute()) {
return Location.FILESYSTEM_PREFIX + baseDir.getAbsolutePath() + "/" + location.getPath();
}
return locationStr;
}
/**
* Load the additional placeholders contained in these properties.
*
* @param currentPlaceholders The current placeholders map.
* @param properties The properties containing additional placeholders.
*/
private static Map loadPlaceholdersFromProperties(Map currentPlaceholders, Hashtable properties) {
Map placeholders = new HashMap(currentPlaceholders);
for (Object property : properties.keySet()) {
String propertyName = (String) property;
if (propertyName.startsWith(PLACEHOLDERS_PROPERTY_PREFIX)
&& propertyName.length() > PLACEHOLDERS_PROPERTY_PREFIX.length()) {
String placeholderName = propertyName.substring(PLACEHOLDERS_PROPERTY_PREFIX.length());
String placeholderValue = (String) properties.get(propertyName);
placeholders.put(placeholderName, placeholderValue);
}
}
return placeholders;
}
/**
* The nested <locations> element of the task. Contains 1 or more <location> sub-elements.
*/
public static class LocationsElement {
/**
* The classpath locations.
*/
List locations = new ArrayList();
/**
* Do not use. For Ant itself.
*
* @param location A location on the classpath.
*/
public void addConfiguredLocation(LocationElement location) {
locations.add(location.path);
}
}
/**
* One <location> sub-element within the <locations> element.
*/
public static class LocationElement {
/**
* The path of the location.
*/
private String path;
/**
* Do not use. For Ant itself.
*
* @param path The path of the location.
*/
public void setPath(String path) {
this.path = path;
}
}
/**
* The nested <schemas> element of the task. Contains 1 or more <schema> sub-elements.
*/
public static class SchemasElement {
/**
* The schema names.
*/
List schemas = new ArrayList();
/**
* Do not use. For Ant itself.
*
* @param schema A schema.
*/
public void addConfiguredLocation(SchemaElement schema) {
schemas.add(schema.name);
}
}
/**
* One <location> sub-element within the <locations> element.
*/
public static class SchemaElement {
/**
* The name of the schema.
*/
private String name;
/**
* Do not use. For Ant itself.
*
* @param name The name of the schema.
*/
public void setPath(String name) {
this.name = name;
}
}
/**
* The nested <resolvers> element of the task. Contains 1 or more <resolver> sub-elements.
*/
public static class ResolversElement {
/**
* The classpath locations.
*/
List resolvers = new ArrayList();
/**
* Do not use. For Ant itself.
*
* @param resolver A resolver on the classpath.
*/
public void addConfiguredResolver(ResolverElement resolver) {
resolvers.add(resolver.clazz);
}
}
/**
* One <resolver> sub-element within the <resolvers> element.
*/
public static class ResolverElement {
/**
* The fully qualified class name of the resolver.
*/
private String clazz;
/**
* Do not use. For Ant itself.
*
* @param clazz The fully qualified class name of the resolver.
*/
public void setClass(String clazz) {
this.clazz = clazz;
}
}
/**
* The nested <callbacks> element of the task. Contains 1 or more <callback> sub-elements.
*/
public static class CallbacksElement {
/**
* The classpath locations.
*/
List callbacks = new ArrayList();
/**
* Do not use. For Ant itself.
*
* @param callback A callback on the classpath.
*/
public void addConfiguredCallback(CallbackElement callback) {
callbacks.add(callback.clazz);
}
}
/**
* One <callback> sub-element within the <callbacks> element.
*/
public static class CallbackElement {
/**
* The fully qualified class name of the callback.
*/
private String clazz;
/**
* Do not use. For Ant itself.
*
* @param clazz The fully qualified class name of the callback.
*/
public void setClass(String clazz) {
this.clazz = clazz;
}
}
/**
* Nested <placeholders> element of the migrate Ant task.
*/
public static class PlaceholdersElement {
/**
* A map of <placeholder, replacementValue> to apply to sql migration scripts.
*/
Map placeholders = new HashMap();
/**
* Adds a placeholder from a nested <placeholder> element. Called by Ant.
*
* @param placeholder The fully configured placeholder element.
*/
public void addConfiguredPlaceholder(PlaceholderElement placeholder) {
placeholders.put(placeholder.name, placeholder.value);
}
}
/**
* Nested <placeholder> element inside the <placeholders> element of the migrate Ant task.
*/
public static class PlaceholderElement {
/**
* The name of the placeholder.
*/
private String name;
/**
* The value of the placeholder.
*/
private String value;
/**
* @param name The name of the placeholder.
*/
public void setName(String name) {
this.name = name;
}
/**
* @param value The value of the placeholder.
*/
public void setValue(String value) {
this.value = value;
}
}
}