org.liquibase.maven.plugins.AbstractLiquibaseMojo Maven / Gradle / Ivy
Show all versions of liquibase-maven-plugin Show documentation
package org.liquibase.maven.plugins;
import liquibase.GlobalConfiguration;
import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.configuration.core.DefaultsFileValueProvider;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.integration.IntegrationDetails;
import liquibase.integration.commandline.CommandLineUtils;
import liquibase.integration.commandline.LiquibaseCommandLineConfiguration;
import liquibase.resource.DirectoryResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.resource.SearchPathResourceAccessor;
import liquibase.util.FileUtil;
import liquibase.util.StringUtil;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.wagon.authentication.AuthenticationInfo;
import org.liquibase.maven.property.PropertyElement;
import javax.xml.bind.annotation.XmlSchema;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.*;
/**
* A base class for providing Liquibase {@link liquibase.Liquibase} functionality.
*
* @author Peter Murray
* @author Florent Biville
*
* Test dependency is used because when you run a goal outside the build phases you want to have the same dependencies
* that it would had if it was ran inside test phase
* @requiresDependencyResolution test
*/
@SuppressWarnings("java:S2583")
public abstract class AbstractLiquibaseMojo extends AbstractMojo {
/**
* Suffix for fields that are representing a default value for a another field.
*/
private static final String DEFAULT_FIELD_SUFFIX = "Default";
/**
*
* Specifies whether to preserve the case of schemas and catalogs
*
* @parameter property="liquibase.preserveSchemaCase"
*
*/
@PropertyElement
protected Boolean preserveSchemaCase;
/**
* Specifies the driver class name to use for the database connection.
*
* @parameter property="liquibase.driver"
*/
@PropertyElement
protected String driver;
/**
* Specifies the database URL you want to use to execute Liquibase.
*
* @parameter property="liquibase.url"
*/
@PropertyElement
protected String url;
/**
* The Maven Wagon manager to use when obtaining server authentication details.
*
* @component role="org.apache.maven.artifact.manager.WagonManager"
* @required
* @readonly
*/
protected WagonManager wagonManager;
/**
* Specifies the database username for database connection.
*
* @parameter property="liquibase.username"
*/
@PropertyElement
protected String username;
/**
* Specifies the database password for database connection.
*
* @parameter property="liquibase.password"
*/
@PropertyElement
protected String password;
/**
* Use an empty string as the password for the database connection. This should not be
* used along side the {@link #password} setting.
*
* @parameter property="liquibase.emptyPassword" default-value="false"
* @deprecated Use an empty or null value for the password instead.
*/
@PropertyElement
protected boolean emptyPassword;
/**
* Specifies whether to ignore the schema name.
*
* @parameter property="liquibase.outputDefaultSchema" default-value="false"
*/
@PropertyElement
protected boolean outputDefaultSchema;
/**
* Specifies whether to ignore the catalog/database name.
*
* @parameter property="liquibase.outputDefaultCatalog" default-value="false"
*/
@PropertyElement
protected boolean outputDefaultCatalog;
/**
* Specifies the default catalog name to use for the database connection.
*
* @parameter property="liquibase.defaultCatalogName"
*/
@PropertyElement
protected String defaultCatalogName;
/**
* Specifies the default schema name to use for the database connection.
*
* @parameter property="liquibase.defaultSchemaName"
*/
@PropertyElement
protected String defaultSchemaName;
/**
* Specifies the database object class.
*
* @parameter property="liquibase.databaseClass"
*/
@PropertyElement
protected String databaseClass;
/**
* Specifies the property provider which must be a java.util.Properties implementation.
*
* @parameter property="liquibase.propertyProviderClass"
*/
@PropertyElement
protected String propertyProviderClass;
/**
* (DEPRECATED) Controls whether users are prompted before executing changeSet to a non-local database.
*
* @parameter property="liquibase.promptOnNonLocalDatabase" default-value="false"
* @deprecated No longer prompts
*/
@PropertyElement
protected boolean promptOnNonLocalDatabase;
/**
* Includes a Maven project artifact in the class loader which obtains the liquibase.properties and changelog files.
*
* @parameter property="liquibase.includeArtifact" default-value="true"
*/
@PropertyElement
protected boolean includeArtifact;
/**
* Includes the Maven test output directory in the class loader which obtains the liquibase.properties and changelog files.
*
* @parameter property="liquibase.includeTestOutputDirectory" default-value="true"
*/
@PropertyElement
protected boolean includeTestOutputDirectory;
/**
* Controls the amount of output detail when you call the plugin.
*
* @parameter property="liquibase.verbose" default-value="false"
*/
@PropertyElement
protected boolean verbose;
/**
* Deprecated and ignored configuration property. Logging is managed via the standard maven logging system
* either using the -e, -X or -q flags or the ${maven.home}/conf/logging/simplelogger.properties file.
*
* See https://maven.apache.org/maven-logging.html for more information.
*
* @parameter property="liquibase.logging"
* @deprecated Logging managed by maven
*/
@PropertyElement
protected String logging;
/**
* Specifies the liquibase.properties you want to use to configure Liquibase.
*
* @parameter property="liquibase.propertyFile"
*/
@PropertyElement
protected String propertyFile;
/**
* A flag which indicates you want the liquibase.properties file to override any settings provided in the Maven plugin configuration.
* By default, if a property is explicitly specified it is
* not overridden if it also appears in the properties file.
*
* @parameter property="liquibase.propertyFileWillOverride" default-value="false"
*/
@PropertyElement
protected boolean propertyFileWillOverride;
/**
* A flag that forces checksums to be cleared from the DATABASECHANGELOG table.
*
* @parameter property="liquibase.clearCheckSums" default-value="false"
*/
@PropertyElement
protected boolean clearCheckSums;
/**
* Specifies a list of system properties you want to to pass to the database.
*
* @parameter
*/
@PropertyElement
protected Properties systemProperties;
/**
* The Maven project that plugin is running under.
*
* @parameter property="project"
* @required
* @readonly
*/
protected MavenProject project;
/**
* Specifies whether to skip running Liquibase.
* The use of this parameter is NOT RECOMMENDED but can be used when needed.
*
* @parameter property="liquibase.skip" default-value="false"
*/
@PropertyElement
protected boolean skip;
/**
* A flag which indicates you want to set the character encoding of the output file during the updateSQL phase.
*
* @parameter property="liquibase.outputFileEncoding"
*/
@PropertyElement
protected String outputFileEncoding;
/**
* Specifies the schema Liquibase will use to create your changelog tables.
*
* @parameter property="liquibase.changelogCatalogName"
*/
@PropertyElement
protected String changelogCatalogName;
/**
* Specifies the schema Liquibase will use to create your changelog table.
*
* @parameter property="liquibase.changelogSchemaName"
*/
@PropertyElement
protected String changelogSchemaName;
/**
* Specifies the table name to use for the DATABASECHANGELOG table.
*
* @parameter property="liquibase.databaseChangeLogTableName"
*/
@PropertyElement
protected String databaseChangeLogTableName;
/**
* Specifies the table name to use for the DATABASECHANGELOGLOCK table.
*
* @parameter property="liquibase.databaseChangeLogLockTableName"
*/
@PropertyElement
protected String databaseChangeLogLockTableName;
/**
* Show the liquibase banner in output.
*
* @parameter property="liquibase.showBanner"
*/
@PropertyElement
protected boolean showBanner = true;
/**
* Specifies the server ID in the Maven settings.xml to use when authenticating.
*
* @parameter property="liquibase.server"
*/
@PropertyElement
private String server;
/**
* The {@link Liquibase} object used modify the database.
*/
@PropertyElement
private Liquibase liquibase;
/**
* Specifies the locations where Liquibase can find your changelog files.
*
* @parameter property="liquibase.searchPath"
*/
@PropertyElement
protected String searchPath;
/**
* A property-based collection of changelog properties to apply.
*
* @parameter
*/
private Properties expressionVars;
/**
* A map-based collection of changelog properties to apply.
*
* @parameter
*/
private Map expressionVariables;
/**
* Specifies the location of a JDBC connection-properties file which contains properties the driver will use.
*
* @parameter
*/
private File driverPropertiesFile;
/**
* Specifies your Liquibase Pro license key. This has been deprecated in favor of using
* "liquibase.liquibaseLicenseKey", but this property will continue to be operational.
*
* @parameter property="liquibase.liquibaseProLicenseKey"
*/
@PropertyElement
@Deprecated
private String liquibaseProLicenseKey;
/**
* Specifies your Liquibase license key.
*
* @parameter property="liquibase.licenseKey"
*/
@PropertyElement
private String liquibaseLicenseKey;
/**
* Specifies your psql path.
*
* @parameter property="liquibase.psql.path"
*/
@PropertyElement
protected String psqlPath;
/**
* Specifies whether to keep generated psql files.
*
* @parameter property="liquibase.psql.keep.temp"
*/
@PropertyElement
protected Boolean psqlKeepTemp;
/**
* Specifies the name of generated psql files.
*
* @parameter property="liquibase.psql.keep.temp.name"
*/
@PropertyElement
protected String psqlKeepTempName;
/**
* Specifies where to keep generated psql files.
*
* @parameter property="liquibase.psql.keep.temp.path"
*/
@PropertyElement
protected String psqlKeepTempPath;
/**
* Specifies additional psql args.
*
* @parameter property="liquibase.psql.args"
*/
@PropertyElement
protected String psqlArgs;
/**
* Specifies psql executor name.
*
* @parameter property="liquibase.psql.executor"
*/
@PropertyElement
protected String psqlExecutorName;
/**
* Specifies psql timeout.
*
* @parameter property="liquibase.psql.timeout"
*/
@PropertyElement
protected Integer psqlTimeout;
/**
* Specifies where to output psql logs.
*
* @parameter property="liquibase.psql.logFile"
*/
@PropertyElement
protected String psqlLogFile;
/**
* Specifies your sqlplus path.
*
* @parameter property="liquibase.sqlplus.path"
*/
@PropertyElement
protected String sqlPlusPath;
/**
* Specifies whether to keep generated sqlplus files.
*
* @parameter property="liquibase.sqlplus.keep.temp"
*/
@PropertyElement
protected Boolean sqlPlusKeepTemp;
/**
* Specifies the name of generated sqlplus files.
*
* @parameter property="liquibase.sqlplus.keep.temp.name"
*/
@PropertyElement
protected String sqlPlusKeepTempName;
/**
* Specifies where to keep generated sqlplus files.
*
* @parameter property="liquibase.sqlplus.keep.temp.path"
*/
@PropertyElement
protected String sqlPlusKeepTempPath;
/**
* Specifies whether to overwrite generated sqlplus files.
*
* @parameter property="liquibase.sqlplus.keep.temp.overwrite"
*/
@PropertyElement
protected Boolean sqlPlusKeepTempOverwrite;
/**
* Specifies additional sqlplus args.
*
* @parameter property="liquibase.sqlplus.args"
*/
@PropertyElement
protected String sqlPlusArgs;
/**
* Specifies sqlPlus executor name.
*
* @parameter property="liquibase.sqlplus.executor"
*/
@PropertyElement
protected String sqlPlusExecutorName;
/**
* Specifies sqlplus timeout.
*
* @parameter property="liquibase.sqlplus.timeout"
*/
@PropertyElement
protected Integer sqlPlusTimeout;
/**
* Specifies where to output sqlplus logs.
*
* @parameter property="liquibase.sqlplus.logFile"
*/
@PropertyElement
protected String sqlPlusLogFile;
/**
* Specifies your sqlcmd path.
*
* @parameter property="liquibase.sqlcmd.path"
*/
@PropertyElement
protected String sqlcmdPath;
/**
* Specifies whether to keep generated sqlcmd files.
*
* @parameter property="liquibase.sqlcmd.keep.temp"
*/
@PropertyElement
protected Boolean sqlcmdKeepTemp;
/**
* Specifies the name of generated sqlcmd files.
*
* @parameter property="liquibase.sqlcmd.keep.temp.name"
*/
@PropertyElement
protected String sqlcmdKeepTempName;
/**
* Specifies where to keep generated sqlcmd files.
*
* @parameter property="liquibase.sqlcmd.keep.temp.path"
*/
@PropertyElement
protected String sqlcmdKeepTempPath;
/**
* Specifies whether to overwrite generated sqlcmd files.
*
* @parameter property="liquibase.sqlcmd.keep.temp.overwrite"
*/
@PropertyElement
protected Boolean sqlcmdKeepTempOverwrite;
/**
* Specifies additional sqlcmd args.
*
* @parameter property="liquibase.sqlcmd.args"
*/
@PropertyElement
protected String sqlcmdArgs;
/**
* Specifies sqlcmd executor name.
*
* @parameter property="liquibase.sqlcmd.executor"
*/
@PropertyElement
protected String sqlcmdExecutorName;
/**
* Specifies sqlcmd timeout.
*
* @parameter property="liquibase.sqlcmd.timeout"
*/
@PropertyElement
protected Integer sqlcmdTimeout;
/**
* Specifies where to output sqlcmd logs.
*
* @parameter property="liquibase.sqlcmd.logFile"
*/
@PropertyElement
protected String sqlcmdLogFile;
/**
* Specifies sqlcmd catalog name.
*
* @parameter property="liquibase.sqlcmd.catalogName"
*/
@PropertyElement
protected String sqlcmdCatalogName;
protected String commandName;
/**
* Get the specified license key. This first checks liquibaseLicenseKey and if no key is found, then returns
* liquibaseProLicenseKey.
*/
protected String getLicenseKey() {
if (StringUtil.isNotEmpty(liquibaseLicenseKey)) {
return liquibaseLicenseKey;
} else {
return liquibaseProLicenseKey;
}
}
protected Writer getOutputWriter(final File outputFile) throws IOException {
String encoding = this.outputFileEncoding;
if (encoding == null) {
encoding = GlobalConfiguration.OUTPUT_FILE_ENCODING.getCurrentValue();
}
getLog().debug("Writing output file with '" + encoding + "' file encoding.");
return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), encoding));
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (StringUtil.trimToNull(logging) != null) {
getLog().error("The liquibase-maven-plugin now manages logging via the standard maven logging config, not the 'logging' configuration. Use the -e, -X or -q flags or see https://maven.apache.org/maven-logging.html");
}
try {
Scope.child(Scope.Attr.logService, new MavenLogService(getLog()), () -> {
getLog().info(MavenUtils.LOG_SEPARATOR);
if (server != null) {
AuthenticationInfo info = wagonManager.getAuthenticationInfo(server);
if (info != null) {
username = info.getUserName();
password = info.getPassword();
}
}
processSystemProperties();
if (!LiquibaseCommandLineConfiguration.SHOULD_RUN.getCurrentValue()) {
getLog().info("Liquibase did not run because " + LiquibaseCommandLineConfiguration.SHOULD_RUN.getKey() + " was set to false");
return;
}
if (skip) {
getLog().warn("Liquibase skipped due to Maven configuration");
return;
}
ClassLoader mavenClassLoader = getClassLoaderIncludingProjectClasspath();
Map scopeValues = new HashMap<>();
scopeValues.put(Scope.Attr.resourceAccessor.name(), getResourceAccessor(mavenClassLoader));
scopeValues.put(Scope.Attr.classLoader.name(), getClassLoaderIncludingProjectClasspath());
IntegrationDetails integrationDetails = new IntegrationDetails();
integrationDetails.setName("maven");
final PluginDescriptor pluginDescriptor = (PluginDescriptor) getPluginContext().get("pluginDescriptor");
for (MojoDescriptor descriptor : pluginDescriptor.getMojos()) {
if (!descriptor.getImplementationClass().equals(this.getClass())) {
continue;
}
for (Parameter param : descriptor.getParameters()) {
final String name = param.getName();
if (name.equalsIgnoreCase("project") || name.equalsIgnoreCase("systemProperties")) {
continue;
}
final Field field = getField(this.getClass(), name);
if (field == null) {
getLog().debug("Cannot read current maven value for. Will not send the value to hub " + name);
} else {
field.setAccessible(true);
final Object value = field.get(this);
if (value != null) {
try {
integrationDetails.setParameter("maven__" + param.getName().replaceAll("[${}]", ""), String.valueOf(value));
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
}
//
// Add properties to this top-level scope
//
scopeValues.put("integrationDetails", integrationDetails);
scopeValues.put("liquibase.licenseKey", getLicenseKey());
String key = GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey();
scopeValues.put(key, preserveSchemaCase);
scopeValues.putAll(getNativeExecutorProperties());
Scope.child(scopeValues, () -> {
configureFieldsAndValues();
if (showBanner) {
getLog().info(CommandLineUtils.getBanner());
}
// Displays the settings for the Mojo depending of verbosity mode.
displayMojoSettings();
// Check that all the parameters that must be specified have been by the user.
checkRequiredParametersAreSpecified();
Database database = null;
try {
if (databaseConnectionRequired()) {
String dbPassword = (emptyPassword || (password == null)) ? "" : password;
String driverPropsFile = (driverPropertiesFile == null) ? null : driverPropertiesFile.getAbsolutePath();
database = CommandLineUtils.createDatabaseObject(mavenClassLoader,
url,
username,
dbPassword,
driver,
defaultCatalogName,
defaultSchemaName,
outputDefaultCatalog,
outputDefaultSchema,
databaseClass,
driverPropsFile,
propertyProviderClass,
changelogCatalogName,
changelogSchemaName,
databaseChangeLogTableName,
databaseChangeLogLockTableName);
liquibase = createLiquibase(database);
configureChangeLogProperties();
getLog().debug("expressionVars = " + String.valueOf(expressionVars));
if (expressionVars != null) {
for (Map.Entry