diff --git a/liquibase-core/src/main/java/liquibase/change/Change.java b/liquibase-core/src/main/java/liquibase/change/Change.java index 555fa12ab72..ef4c8cdc097 100644 --- a/liquibase-core/src/main/java/liquibase/change/Change.java +++ b/liquibase-core/src/main/java/liquibase/change/Change.java @@ -100,7 +100,7 @@ public interface Change extends LiquibaseSerializable { /** - * Returns true if this change be rolled back for the given database. + * Returns true if this can change be rolled back for the given database. */ public boolean supportsRollback(Database database); diff --git a/liquibase-core/src/main/java/liquibase/changelog/DatabaseChangeLog.java b/liquibase-core/src/main/java/liquibase/changelog/DatabaseChangeLog.java index f1105185cd3..969df50ae5b 100644 --- a/liquibase-core/src/main/java/liquibase/changelog/DatabaseChangeLog.java +++ b/liquibase-core/src/main/java/liquibase/changelog/DatabaseChangeLog.java @@ -12,8 +12,6 @@ import liquibase.database.DatabaseList; import liquibase.database.ObjectQuotingStrategy; import liquibase.exception.*; -import liquibase.logging.LogFactory; -import liquibase.exception.*; import liquibase.logging.LogService; import liquibase.logging.LogType; import liquibase.logging.Logger; @@ -28,7 +26,6 @@ import liquibase.util.StringUtils; import liquibase.util.file.FilenameUtils; -import java.awt.*; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java b/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java index db42a553fa5..e73f21eef30 100644 --- a/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java +++ b/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java @@ -46,7 +46,6 @@ import liquibase.servicelocator.ServiceLocator; import liquibase.util.ISODateFormat; import liquibase.util.LiquibaseUtil; -import liquibase.util.StreamUtil; import liquibase.util.StringUtils; import liquibase.util.xml.XMLResourceBundle; import liquibase.util.xml.XmlResourceBundleControl; @@ -287,7 +286,9 @@ public static int run(String[] args) throws LiquibaseException { log.info(LogType.USER_MESSAGE, licenseService.getLicenseInfo()); } - if (main.commandParams.contains("--help") && main.command.startsWith("rollbackOneChangeSet")) { + if (main.commandParams.contains("--help") && + (main.command.startsWith("rollbackOneChangeSet") || + main.command.startsWith("rollbackOneUpdate"))) { //don't need to check setup } else { List setupMessages = main.checkSetup(); @@ -541,7 +542,8 @@ private static boolean isStandardOutputRequired(String command) { private static boolean isChangeLogRequired(String command) { return command.toLowerCase().startsWith(COMMANDS.UPDATE) || (command.toLowerCase().startsWith(COMMANDS.ROLLBACK) && - !command.equalsIgnoreCase(COMMANDS.ROLLBACK_ONE_CHANGE_SET)) + (!command.equalsIgnoreCase(COMMANDS.ROLLBACK_ONE_CHANGE_SET) && + !command.equalsIgnoreCase(COMMANDS.ROLLBACK_ONE_UPDATE))) || COMMANDS.CALCULATE_CHECKSUM.equalsIgnoreCase(command) || COMMANDS.STATUS.equalsIgnoreCase(command) || COMMANDS.VALIDATE.equalsIgnoreCase(command) @@ -549,7 +551,8 @@ private static boolean isChangeLogRequired(String command) { || COMMANDS.CHANGELOG_SYNC_SQL.equalsIgnoreCase(command) || COMMANDS.GENERATE_CHANGELOG.equalsIgnoreCase(command) || COMMANDS.DIFF_CHANGELOG.equalsIgnoreCase(command) - || COMMANDS.ROLLBACK_ONE_CHANGE_SET.equalsIgnoreCase(command); + || COMMANDS.ROLLBACK_ONE_CHANGE_SET.equalsIgnoreCase(command) + || COMMANDS.ROLLBACK_ONE_UPDATE.equalsIgnoreCase(command); } /** @@ -601,7 +604,9 @@ private static boolean isCommand(String arg) { || COMMANDS.MARK_NEXT_CHANGESET_RAN.equalsIgnoreCase(arg) || COMMANDS.MARK_NEXT_CHANGESET_RAN_SQL.equalsIgnoreCase(arg) || COMMANDS.ROLLBACK_ONE_CHANGE_SET.equalsIgnoreCase(arg) - || COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equalsIgnoreCase(arg); + || COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equalsIgnoreCase(arg) + || COMMANDS.ROLLBACK_ONE_UPDATE.equalsIgnoreCase(arg) + || COMMANDS.ROLLBACK_ONE_UPDATE_SQL.equalsIgnoreCase(arg); } /** @@ -860,7 +865,6 @@ private void checkForUnexpectedCommandParameter(List messages) { } else if (COMMANDS.ROLLBACK_ONE_CHANGE_SET.equalsIgnoreCase(command)) { for (String cmdParm : commandParams) { if (!cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_ID) - && !cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_ID) && !cmdParm.startsWith("--" + OPTIONS.HELP) && !cmdParm.startsWith("--" + OPTIONS.FORCE) && !cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_PATH) @@ -872,7 +876,6 @@ private void checkForUnexpectedCommandParameter(List messages) { } else if (COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equalsIgnoreCase(command)) { for (String cmdParm : commandParams) { if (!cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_ID) - && !cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_ID) && !cmdParm.startsWith("--" + OPTIONS.HELP) && !cmdParm.startsWith("--" + OPTIONS.FORCE) && !cmdParm.startsWith("--" + OPTIONS.CHANGE_SET_PATH) @@ -882,6 +885,24 @@ private void checkForUnexpectedCommandParameter(List messages) { } } } + else if (COMMANDS.ROLLBACK_ONE_UPDATE.equalsIgnoreCase(command)) { + for (String cmdParm : commandParams) { + if (!cmdParm.startsWith("--" + OPTIONS.DEPLOYMENT_ID) + && !cmdParm.startsWith("--" + OPTIONS.HELP) + && !cmdParm.startsWith("--" + OPTIONS.FORCE)) { + messages.add(String.format(coreBundle.getString("unexpected.command.parameter"), cmdParm)); + } + } + } + else if (COMMANDS.ROLLBACK_ONE_UPDATE_SQL.equalsIgnoreCase(command)) { + for (String cmdParm : commandParams) { + if (!cmdParm.startsWith("--" + OPTIONS.DEPLOYMENT_ID) + && !cmdParm.startsWith("--" + OPTIONS.HELP) + && !cmdParm.startsWith("--" + OPTIONS.FORCE)) { + messages.add(String.format(coreBundle.getString("unexpected.command.parameter"), cmdParm)); + } + } + } } /** @@ -1278,9 +1299,12 @@ protected void doMigration() throws Exception { // // Check for a valid license to run PRO commands // - if (COMMANDS.ROLLBACK_ONE_CHANGE_SET.equals(command) || COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equals(command)) { + if (COMMANDS.ROLLBACK_ONE_CHANGE_SET.equals(command) || + COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equals(command) || + COMMANDS.ROLLBACK_ONE_UPDATE.equals(command) || + COMMANDS.ROLLBACK_ONE_UPDATE_SQL.equals(command)){ if (!commandParams.contains("--help") && !liquibaseProLicenseValid) { - String messageString = String.format(coreBundle.getString("no.pro.license.found"), COMMANDS.ROLLBACK_ONE_CHANGE_SET); + String messageString = String.format(coreBundle.getString("no.pro.license.found"), command); throw new LiquibaseException(messageString); } } @@ -1516,7 +1540,7 @@ protected void doMigration() throws Exception { if (this.commandParams.contains("--help")) { argsMap.put("help", true); } - LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, argsMap); + LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, COMMANDS.ROLLBACK_ONE_CHANGE_SET, argsMap); liquibaseCommand.execute(); return; } else if (COMMANDS.ROLLBACK_ONE_CHANGE_SET_SQL.equals(command)) { @@ -1524,7 +1548,42 @@ protected void doMigration() throws Exception { Map argsMap = new HashMap(); argsMap.put("outputWriter", outputWriter); argsMap.put("force", true); - LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, argsMap); + LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, COMMANDS.ROLLBACK_ONE_CHANGE_SET, argsMap); + liquibaseCommand.execute(); + outputWriter.flush(); + outputWriter.close(); + return; + } else if (COMMANDS.ROLLBACK_ONE_UPDATE.equals(command)) { + Map argsMap = new HashMap(); + argsMap.put("deploymentId", getCommandParam(OPTIONS.DEPLOYMENT_ID, null)); + argsMap.put("changeLogFile", changeLogFile); + argsMap.put("database", database); + if (!commandParams.contains("--help")) { + argsMap.put("changeLog", liquibase.getDatabaseChangeLog()); + } + argsMap.put("resourceAccessor", liquibase.getResourceAccessor()); + ChangeLogParameters clp = new ChangeLogParameters(database); + for (Map.Entry entry : changeLogParameters.entrySet()) { + clp.set(entry.getKey(), entry.getValue()); + } + argsMap.put("changeLogParameters", clp); + + if (this.commandParams.contains("--force")) { + argsMap.put("force", true); + } + if (this.commandParams.contains("--help")) { + argsMap.put("help", true); + } + LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, COMMANDS.ROLLBACK_ONE_UPDATE, argsMap); + liquibaseCommand.execute(); + return; + } else if (COMMANDS.ROLLBACK_ONE_UPDATE_SQL.equals(command)) { + Writer outputWriter = getOutputWriter(); + Map argsMap = new HashMap(); + argsMap.put("deploymentId", getCommandParam(OPTIONS.DEPLOYMENT_ID, null)); + argsMap.put("outputWriter", outputWriter); + argsMap.put("force", true); + LiquibaseCommand liquibaseCommand = createLiquibaseCommand(database, liquibase, COMMANDS.ROLLBACK_ONE_UPDATE, argsMap); liquibaseCommand.execute(); outputWriter.flush(); outputWriter.close(); @@ -1719,9 +1778,9 @@ protected void doMigration() throws Exception { } } - private LiquibaseCommand createLiquibaseCommand(Database database, Liquibase liquibase, Map argsMap) + private LiquibaseCommand createLiquibaseCommand(Database database, Liquibase liquibase, String commandName, Map argsMap) throws CommandLineParsingException, LiquibaseException { - LiquibaseCommand liquibaseCommand = (CommandFactory.getInstance().getCommand(COMMANDS.ROLLBACK_ONE_CHANGE_SET)); + LiquibaseCommand liquibaseCommand = CommandFactory.getInstance().getCommand(commandName); AbstractSelfConfiguratingCommand configuratingCommand = (AbstractSelfConfiguratingCommand) liquibaseCommand; argsMap.put("changeSetId", getCommandParam(OPTIONS.CHANGE_SET_ID, null)); argsMap.put("changeSetAuthor", getCommandParam(OPTIONS.CHANGE_SET_AUTHOR, null)); @@ -1904,6 +1963,8 @@ private enum COMMANDS { private static final String RELEASE_LOCKS = "releaseLocks"; private static final String ROLLBACK_ONE_CHANGE_SET = "rollbackOneChangeSet"; private static final String ROLLBACK_ONE_CHANGE_SET_SQL = "rollbackOneChangeSetSQL"; + private static final String ROLLBACK_ONE_UPDATE = "rollbackOneUpdate"; + private static final String ROLLBACK_ONE_UPDATE_SQL = "rollbackOneUpdateSQL"; private static final String ROLLBACK = "rollback"; private static final String ROLLBACK_COUNT = "rollbackCount"; private static final String ROLLBACK_COUNT_SQL = "rollbackCountSQL"; @@ -1937,6 +1998,7 @@ private enum OPTIONS { private static final String CHANGE_SET_ID = "changeSetId"; private static final String CHANGE_SET_AUTHOR = "changeSetAuthor"; private static final String CHANGE_SET_PATH = "changeSetPath"; + private static final String DEPLOYMENT_ID = "deploymentId"; private static final String OUTPUT_FILE = "outputFile"; private static final String FORCE = "force"; private static final String ROLLBACK_SCRIPT = "rollbackScript"; diff --git a/liquibase-core/src/main/java/liquibase/license/LicenseServiceUtils.java b/liquibase-core/src/main/java/liquibase/license/LicenseServiceUtils.java index b4a9737d8e4..0cf890e9fbc 100644 --- a/liquibase-core/src/main/java/liquibase/license/LicenseServiceUtils.java +++ b/liquibase-core/src/main/java/liquibase/license/LicenseServiceUtils.java @@ -21,8 +21,8 @@ public static ValidationErrors checkForValidLicense(String licenseType, Change c return new ValidationErrors(); } if (licenseService.licenseIsValid(licenseType)) { - String message = String.format("Found valid license with subject '%s'",licenseType); - LOG.info(message); + String message = String.format("Found valid license with subject '%s' for '%s'",licenseType, change.getDescription()); + LOG.debug(message); return new ValidationErrors(); } diff --git a/liquibase-core/src/main/resources/liquibase/i18n/liquibase-commandline-helptext.xml b/liquibase-core/src/main/resources/liquibase/i18n/liquibase-commandline-helptext.xml index 4cfcc6ac653..8110487530a 100644 --- a/liquibase-core/src/main/resources/liquibase/i18n/liquibase-commandline-helptext.xml +++ b/liquibase-core/src/main/resources/liquibase/i18n/liquibase-commandline-helptext.xml @@ -33,6 +33,12 @@ Standard Commands: rollbackOneChangeSetSQL Writes SQL to roll back one specific changeset, without rolling back changesets deployed before or afterwards. (Liquibase Pro key required) + rollbackOneUpdate Rolls back all the changesets from one update, identified by "deploymentId", + if all the changesets can be rolled back. If not, a WARNING message will provide + details (Liquibase Pro key required). Note: A list of deploymentIds may be + viewed by using the "history" command. + rollbackOneUpdateSQL Displays the SQL which will be executed when the corresponding rollbackOneUpdate + command is executed, and does not perform the actual rollback (Liquibase Pro key required). rollbackToDate Rolls back the database to the the state is was at the given date/time. Date Format: yyyy-MM-dd'T'HH:mm:ss @@ -74,9 +80,10 @@ Diff Commands Documentation Commands dbDoc Generates Javadoc-like documentation based on current database and change log - history Writes details about what changeSets have been - applied to the database to standard out - + history Displays the changesets already deployed to the + database in the specified connection url, grouped + by default according to their update command's + "deployment_id" value. Maintenance Commands tag 'Tags' the current database state for future rollback tagExists Checks whether the given tag is already existing diff --git a/liquibase-core/src/main/resources/liquibase/i18n/liquibase-core.properties b/liquibase-core/src/main/resources/liquibase/i18n/liquibase-core.properties index 841a66405bf..d052bf1e847 100644 --- a/liquibase-core/src/main/resources/liquibase/i18n/liquibase-core.properties +++ b/liquibase-core/src/main/resources/liquibase/i18n/liquibase-core.properties @@ -29,8 +29,11 @@ errors=Errors: unexpected.value=Unexpected value '%s' (options must start with a '--') cannot.specify.both=Cannot specify both '%s' and '%s' changelogfile.already.exists=Output ChangeLogFile '%s' already exists! -force.option.required=The targeted changeset is followed by %d changesets, so unexpected outcomes may occur.\nTo review the rollback SQL, please run rollbackOneChangesetSQL. This message can be suppressed by\nadding the --force flag. +force.option.required=The targeted changeset is followed by %d changesets, so unexpected outcomes may occur.\nTo review the rollback SQL, please run '%s'. This message can be suppressed by\nadding the --force flag. id.author.path.required=You must specify the change set ID, author, and path +no.deployment.ids.found=No deployment IDs were located. No rollbacks were performed. +no.change.sets.found.for.deployment.id=No changesets were located matching deployment ID '%s'. No rollbacks were performed. +deployment.id.required=You must specify the deployment ID. no.pro.license.found=The command %s requires a Liquibase Pro license, available at https://download.liquibase.org/ or lbprosales@datical.com attempt.to.delete.the.file.failed.cannot.continue=Attempt to delete the file '%s' failed. Cannot continue. Sorry. successfully.released.database.change.log.locks=Successfully released all database change log locks for '%s' diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseChangeLogMojo.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseChangeLogMojo.java index c29d5f30858..3643a50e068 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseChangeLogMojo.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseChangeLogMojo.java @@ -23,14 +23,14 @@ public abstract class AbstractLiquibaseChangeLogMojo extends AbstractLiquibaseMojo { /** - * Specifies the directory where Liquibase can find your change-log file. + * Specifies the directory where Liquibase can find your changelog file. * * @parameter property="liquibase.changeLogDirectory" */ protected String changeLogDirectory; /** - * Specifies the change-log file for Liquibase to use. + * Specifies the changelog file for Liquibase to use. * @parameter property="liquibase.changeLogFile" */ protected String changeLogFile; diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseMojo.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseMojo.java index 913eb4014a2..d42f8ed7ec0 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseMojo.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/AbstractLiquibaseMojo.java @@ -131,20 +131,20 @@ public abstract class AbstractLiquibaseMojo extends AbstractMojo { */ protected String propertyProviderClass; /** - * Controls whether users are prompted before executing changese to a non-local database. + * Controls whether users are prompted before executing changeSet to a non-local database. * * @parameter property="liquibase.promptOnNonLocalDatabase" default-value="true" */ protected boolean promptOnNonLocalDatabase; /** - * Includes a maven project artifcat in the class loader which obtains the Liquibase property and changeLog files. + * Includes a Maven project artifact in the class loader which obtains the liquibase.properties and changelog files. * * @parameter property="liquibase.includeArtifact" default-value="true" */ protected boolean includeArtifact; /** - * Includes the maven test output directory in the class loader which obtainst he Liquibase property and changeLog files. + * 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" */ @@ -186,7 +186,7 @@ public abstract class AbstractLiquibaseMojo extends AbstractMojo { */ protected boolean clearCheckSums; /** - * A list of system properties you want to to pass to the database. + * Specifies a list of system properties you want to to pass to the database. * * @parameter */ @@ -202,7 +202,7 @@ public abstract class AbstractLiquibaseMojo extends AbstractMojo { /** * Specifies whether to skip running Liquibase. - * The use of this parameter is NOT RECOMMENDED, but can be used when needed. + * The use of this parameter is NOT RECOMMENDED but can be used when needed. * * @parameter property="liquibase.skip" default-value="false" */ @@ -253,14 +253,14 @@ public abstract class AbstractLiquibaseMojo extends AbstractMojo { private Liquibase liquibase; /** - * A property-based collection of changeLog properties to apply. + * A property-based collection of changelog properties to apply. * * @parameter */ private Properties expressionVars; /** - * A map-based collection of changeLog properties to apply. + * A map-based collection of changelog properties to apply. * * @parameter */ @@ -289,7 +289,6 @@ public abstract class AbstractLiquibaseMojo extends AbstractMojo { protected boolean hasProLicense() { return hasProLicense; } - protected Writer getOutputWriter(final File outputFile) throws IOException { if (outputFileEncoding==null) { getLog().info("Char encoding not set! The created file will be system dependent!"); @@ -321,19 +320,19 @@ public void execute() throws MojoExecutionException, MojoFailureException { return; } if (skip) { - getLog().warn("Liquibase skipped due to maven configuration"); + getLog().warn("Liquibase skipped due to Maven configuration"); return; } + ClassLoader artifactClassLoader = getMavenArtifactClassLoader(); + ResourceAccessor fileOpener = getFileOpener(artifactClassLoader); + configureFieldsAndValues(fileOpener); + // // Check for a LiquibasePro license // hasProLicense = MavenUtils.checkProLicense(liquibaseProLicenseKey, commandName, getLog()); - ClassLoader artifactClassLoader = getMavenArtifactClassLoader(); - ResourceAccessor fileOpener = getFileOpener(artifactClassLoader); - configureFieldsAndValues(fileOpener); - // LogService.getInstance().setDefaultLoggingLevel(logging); getLog().info(CommandLineUtils.getBanner()); diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetMojo.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetMojo.java index 8c10370a8b5..6dc00a2fa74 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetMojo.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetMojo.java @@ -1,5 +1,3 @@ -// Version: $Id: $ -// Copyright: Copyright(c) 2007 Trace Financial Limited package org.liquibase.maven.plugins; import liquibase.Liquibase; @@ -90,7 +88,8 @@ protected void performLiquibaseTask(Liquibase liquibase) throws LiquibaseExcepti // // Check the Pro license // - if (! hasProLicense()) { + boolean hasProLicense = MavenUtils.checkProLicense(liquibaseProLicenseKey, commandName, getLog()); + if (! hasProLicense) { throw new LiquibaseException("The command 'rollbackOneChangeSet' requires a Liquibase Pro License, available at http://liquibase.org."); } Database database = liquibase.getDatabase(); diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetSQL.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetSQL.java index b0c8c69d9b4..f1558d4990b 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetSQL.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneChangeSetSQL.java @@ -1,5 +1,3 @@ -// Version: $Id: $ -// Copyright: Copyright(c) 2007 Trace Financial Limited package org.liquibase.maven.plugins; import liquibase.Liquibase; @@ -26,7 +24,8 @@ /** * - * A helper command that allows you to inspect the SQL Liquibase will run to revert the changeSet specified in the rollbackOneChangeSet command. It is only available for Liquibase Pro users. + * Displays the SQL which will be executed when the corresponding rollbackOneChangeSet command is + * executed. This command does not perform the actual rollback. A Liquibase Pro license key is required. * * @goal rollbackOneChangeSetSQL * @@ -105,7 +104,8 @@ protected void performLiquibaseTask(Liquibase liquibase) throws LiquibaseExcepti // // Check the Pro license // - if (! hasProLicense()) { + boolean hasProLicense = MavenUtils.checkProLicense(liquibaseProLicenseKey, commandName, getLog()); + if (! hasProLicense) { throw new LiquibaseException("The command 'rollbackOneChangeSetSQL' requires a Liquibase Pro License, available at http://liquibase.org."); } Database database = liquibase.getDatabase(); @@ -133,13 +133,11 @@ protected void performLiquibaseTask(Liquibase liquibase) throws LiquibaseExcepti finally { try { outputWriter.flush(); - outputWriter.close(); + closeOutputWriter(outputWriter); } catch (IOException ioe) { LogService.getLog(getClass()).info(LogType.LOG, String.format("Unable to close output file")); } - finally { - } } } @@ -159,6 +157,13 @@ private OutputStream getOutputStream() throws IOException { return fileOut; } + private void closeOutputWriter(Writer outputWriter) throws IOException { + if (outputFile == null) { + return; + } + outputWriter.close(); + } + private Writer createOutputWriter() throws IOException { String charsetName = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class) .getOutputEncoding(); @@ -169,11 +174,7 @@ private Writer createOutputWriter() throws IOException { private Map getCommandArgsObjectMap(Liquibase liquibase) throws LiquibaseException { Database database = liquibase.getDatabase(); Map argsMap = new HashMap(); - argsMap.put("changeSetId", this.changeSetId); - argsMap.put("changeSetAuthor", this.changeSetAuthor); - argsMap.put("changeSetPath", this.changeSetPath); argsMap.put("force", true); - argsMap.put("rollbackScript", this.rollbackScript); argsMap.put("changeLogFile", this.changeLogFile); argsMap.put("database", database); argsMap.put("changeLog", liquibase.getDatabaseChangeLog()); diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateMojo.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateMojo.java new file mode 100644 index 00000000000..7c977b060ee --- /dev/null +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateMojo.java @@ -0,0 +1,92 @@ +package org.liquibase.maven.plugins; + +import liquibase.Liquibase; +import liquibase.changelog.ChangeLogParameters; +import liquibase.command.AbstractSelfConfiguratingCommand; +import liquibase.command.CommandExecutionException; +import liquibase.command.CommandFactory; +import liquibase.command.LiquibaseCommand; +import liquibase.database.Database; +import liquibase.exception.LiquibaseException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * Rolls back all changesets from any specific update, if all changesets can be rolled back. + * By default, the last update is rolled back, but an optional deployentId parameter can target any update. + * (Liquibase Pro only). + * + * @goal rollbackOneUpdate + * + */ +public class LiquibaseRollbackOneUpdateMojo extends AbstractLiquibaseChangeLogMojo { + /** + * + * Specifies the update your want to rollback. A list of the updates's + * changesets grouped by their deploymentId can be found by using the history command. + * + * @parameter property="liquibase.deploymentId" + * + */ + protected String deploymentId; + + /** + * + * A required flag for rollbackOneUpdate. + * + * @parameter property="liquibase.force" + * + */ + protected String force; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + commandName = "rollbackOneUpdate"; + super.execute(); + } + + @Override + protected void performLiquibaseTask(Liquibase liquibase) throws LiquibaseException { + // + // Check the Pro license + // + boolean hasProLicense = MavenUtils.checkProLicense(liquibaseProLicenseKey, commandName, getLog()); + if (! hasProLicense) { + throw new LiquibaseException("The command 'rollbackOneUpdate' requires a Liquibase Pro License, available at http://liquibase.org."); + } + Database database = liquibase.getDatabase(); + LiquibaseCommand liquibaseCommand = (CommandFactory.getInstance().getCommand("rollbackOneUpdate")); + AbstractSelfConfiguratingCommand configuratingCommand = (AbstractSelfConfiguratingCommand)liquibaseCommand; + Map argsMap = getCommandArgsObjectMap(liquibase); + ChangeLogParameters clp = new ChangeLogParameters(database); + argsMap.put("changeLogParameters", clp); + if (force == null || (force != null && ! Boolean.parseBoolean(force))) { + throw new LiquibaseException("Invalid value for --force. You must specify 'liquibase.force=true' to use rollbackOneUpdate."); + } + argsMap.put("force", Boolean.TRUE); + argsMap.put("liquibase", liquibase); + configuratingCommand.configure(argsMap); + try { + liquibaseCommand.execute(); + } + catch (CommandExecutionException cee) { + throw new LiquibaseException("Error executing rollbackOneUpdate", cee); + } + } + + private Map getCommandArgsObjectMap(Liquibase liquibase) throws LiquibaseException { + Database database = liquibase.getDatabase(); + Map argsMap = new HashMap(); + argsMap.put("deploymentId", this.deploymentId); + argsMap.put("force", this.force); + argsMap.put("database", database); + argsMap.put("changeLog", liquibase.getDatabaseChangeLog()); + argsMap.put("resourceAccessor", liquibase.getResourceAccessor()); + return argsMap; + } + +} diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateSQL.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateSQL.java new file mode 100644 index 00000000000..7a4f9b830df --- /dev/null +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/LiquibaseRollbackOneUpdateSQL.java @@ -0,0 +1,157 @@ +package org.liquibase.maven.plugins; + +import liquibase.Liquibase; +import liquibase.changelog.ChangeLogParameters; +import liquibase.command.AbstractSelfConfiguratingCommand; +import liquibase.command.CommandExecutionException; +import liquibase.command.CommandFactory; +import liquibase.command.LiquibaseCommand; +import liquibase.configuration.GlobalConfiguration; +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.database.Database; +import liquibase.exception.LiquibaseException; +import liquibase.logging.LogService; +import liquibase.logging.LogType; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +import static java.util.ResourceBundle.getBundle; + +/** + * + * Displays the SQL which will be executed when the corresponding rollbackOneUpdate + * command is executed. This command does not perform the actual rollback. + * A Liquibase Pro license key is required. + * + * @goal rollbackOneUpdateSQL + * + */ +public class LiquibaseRollbackOneUpdateSQL extends AbstractLiquibaseChangeLogMojo { + /** + * + * Specifies the Deployment ID in the DATABASECHANGELOG table for all change sets you + * want to rollback. + * + * @parameter property="liquibase.deploymentId" + * + */ + protected String deploymentId; + + /** + * + * Required flag for RollbackOneChangeSet + * + * @parameter property="liquibase.force" + * + */ + protected String force; + + /** + * + * Specifies the path to the generated SQL output file. + * + * @parameter property="liquibase.outputFile" + * + */ + protected String outputFile; + + private static ResourceBundle coreBundle = getBundle("liquibase/i18n/liquibase-core"); + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + commandName = "rollbackOneUpdateSQL"; + super.execute(); + } + + @Override + protected void performLiquibaseTask(Liquibase liquibase) throws LiquibaseException { + // + // Check the Pro license + // + boolean hasProLicense = MavenUtils.checkProLicense(liquibaseProLicenseKey, commandName, getLog()); + if (! hasProLicense) { + throw new LiquibaseException("The command 'rollbackOneUpdateSQL' requires a Liquibase Pro License, available at http://liquibase.org."); + } + Database database = liquibase.getDatabase(); + LiquibaseCommand liquibaseCommand = (CommandFactory.getInstance().getCommand("rollbackOneUpdate")); + AbstractSelfConfiguratingCommand configuratingCommand = (AbstractSelfConfiguratingCommand)liquibaseCommand; + Map argsMap = getCommandArgsObjectMap(liquibase); + Writer outputWriter = null; + try { + outputWriter = createOutputWriter(); + argsMap.put("outputWriter", outputWriter); + } + catch (IOException ioe) { + throw new LiquibaseException("Error executing rollbackOneChangeSetSQL. Unable to create output writer.", ioe); + } + ChangeLogParameters clp = new ChangeLogParameters(database); + argsMap.put("changeLogParameters", clp); + if (force != null && ! Boolean.parseBoolean(force)) { + throw new LiquibaseException("Invalid value for --force. You must specify 'liquibase.force=true' to use rollbackOneUpdateSQL."); + } + argsMap.put("force", Boolean.TRUE); + argsMap.put("liquibase", liquibase); + configuratingCommand.configure(argsMap); + try { + liquibaseCommand.execute(); + } + catch (CommandExecutionException cee) { + throw new LiquibaseException("Error executing rollbackOneUpdate", cee); + } + finally { + try { + outputWriter.flush(); + closeOutputWriter(outputWriter); + } + catch (IOException ioe) { + LogService.getLog(getClass()).info(LogType.LOG, String.format("Unable to close output file")); + } + } + } + + private void closeOutputWriter(Writer outputWriter) throws IOException { + if (outputFile == null) { + return; + } + outputWriter.close(); + } + + private Writer createOutputWriter() throws IOException { + String charsetName = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class) + .getOutputEncoding(); + + return new OutputStreamWriter(getOutputStream(), charsetName); + } + private OutputStream getOutputStream() throws IOException { + if (outputFile == null) { + return System.out; + } + FileOutputStream fileOut; + try { + fileOut = new FileOutputStream(outputFile, false); + } catch (IOException e) { + LogService.getLog(getClass()).severe(LogType.LOG, String.format( + coreBundle.getString("could.not.create.output.file"), + outputFile)); + throw e; + } + return fileOut; + } + + private Map getCommandArgsObjectMap(Liquibase liquibase) throws LiquibaseException { + Database database = liquibase.getDatabase(); + Map argsMap = new HashMap(); + argsMap.put("deploymentId", this.deploymentId); + argsMap.put("force", this.force); + argsMap.put("database", database); + argsMap.put("changeLog", liquibase.getDatabaseChangeLog()); + argsMap.put("resourceAccessor", liquibase.getResourceAccessor()); + return argsMap; + } + +} diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java index ca28d31017b..17362fe1d71 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java @@ -99,7 +99,9 @@ public static boolean checkProLicense(String liquibaseProLicenseKey, String comm } if (liquibaseProLicenseKey == null) { log.info(""); - log.info("The command '" + commandName + "' requires a Liquibase Pro License, available at http://liquibase.org."); + if (commandName != null) { + log.info("The command '" + commandName + "' requires a Liquibase Pro License, available at http://liquibase.org."); + } log.info(""); hasProLicense = false; }