package liquibase.changelog.visitor;
import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.changelog.RanChangeSet;
import liquibase.database.Database;
import liquibase.exception.*;
import liquibase.precondition.core.DBMSPrecondition;
import liquibase.precondition.core.ErrorPrecondition;
import liquibase.precondition.core.FailedPrecondition;
import liquibase.precondition.core.PreconditionContainer;
import liquibase.util.StringUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ValidatingVisitor implements ChangeSetVisitor {
private List<ChangeSet> invalidMD5Sums = new ArrayList<ChangeSet>();
private List<FailedPrecondition> failedPreconditions = new ArrayList<FailedPrecondition>();
private List<ErrorPrecondition> errorPreconditions = new ArrayList<ErrorPrecondition>();
private Set<ChangeSet> duplicateChangeSets = new HashSet<ChangeSet>();
private List<SetupException> setupExceptions = new ArrayList<SetupException>();
private List<Throwable> changeValidationExceptions = new ArrayList<Throwable>();
private ValidationErrors validationErrors = new ValidationErrors();
private Warnings warnings = new Warnings();
private Set<String> seenChangeSets = new HashSet<String>();
private List<RanChangeSet> ranChangeSets;
private Database database;
final Logger logger = Logger.getLogger(getClass().getName());
public ValidatingVisitor(List<RanChangeSet> ranChangeSets) {
this.ranChangeSets = ranChangeSets;
}
public void validate(Database database, DatabaseChangeLog changeLog) {
this.database = database;
PreconditionContainer preconditions = changeLog.getPreconditions();
try {
if (preconditions == null) {
return;
}
preconditions.check(database, changeLog, null);
} catch (PreconditionFailedException e) {
logger.log(Level.FINE,"Precondition Failed: " + e.getMessage(), e);
failedPreconditions.addAll(e.getFailedPreconditions());
} catch (PreconditionErrorException e) {
logger.log(Level.FINE,"Precondition Error: "+e.getMessage(), e);
errorPreconditions.addAll(e.getErrorPreconditions());
} finally {
try {
database.rollback();
} catch (DatabaseException e) {
logger.log(Level.WARNING,"Error rolling back after precondition check", e);
}
}
}
public Direction getDirection() {
return ChangeSetVisitor.Direction.FORWARD;
}
public void visit(ChangeSet changeSet, DatabaseChangeLog databaseChangeLog, Database database) {
for (Change change : changeSet.getChanges()) {
try {
change.init();
} catch (SetupException se) {
setupExceptions.add(se);
}
warnings.addAll(change.warn(database));
try {
ValidationErrors foundErrors = change.validate(database);
if (foundErrors != null && foundErrors.hasErrors()) {
if (changeSet.getOnValidationFail().equals(ChangeSet.ValidationFailOption.MARK_RAN)) {
logger.info("Skipping changeSet "+changeSet+" due to validation error(s): "+ StringUtils.join(foundErrors.getErrorMessages(), ", "));
changeSet.setValidationFailed(true);
} else {
validationErrors.addAll(foundErrors, changeSet);
}
}
} catch (Throwable e) {
changeValidationExceptions.add(e);
}
}
for (RanChangeSet ranChangeSet : ranChangeSets) {
if (ranChangeSet.getId().equalsIgnoreCase(changeSet.getId())
&& ranChangeSet.getAuthor().equalsIgnoreCase(changeSet.getAuthor())
&& ranChangeSet.getChangeLog().equalsIgnoreCase(changeSet.getFilePath())) {
if (!changeSet.isCheckSumValid(ranChangeSet.getLastCheckSum())) {
if (!changeSet.shouldRunOnChange()) {
invalidMD5Sums.add(changeSet);
}
}
}
}
String changeSetString = changeSet.toString(false);
if (seenChangeSets.contains(changeSetString)) {
duplicateChangeSets.add(changeSet);
} else {
seenChangeSets.add(changeSetString);
}
}
public List<ChangeSet> getInvalidMD5Sums() {
return invalidMD5Sums;
}
public List<FailedPrecondition> getFailedPreconditions() {
return failedPreconditions;
}
public List<ErrorPrecondition> getErrorPreconditions() {
return errorPreconditions;
}
public Set<ChangeSet> getDuplicateChangeSets() {
return duplicateChangeSets;
}
public List<SetupException> getSetupExceptions() {
return setupExceptions;
}
public List<Throwable> getChangeValidationExceptions() {
return changeValidationExceptions;
}
public ValidationErrors getValidationErrors() {
return validationErrors;
}
public Warnings getWarnings() {
return warnings;
}
public boolean validationPassed() {
return invalidMD5Sums.size() == 0
&& failedPreconditions.size() == 0
&& errorPreconditions.size() == 0
&& duplicateChangeSets.size() == 0
&& changeValidationExceptions.size() == 0
&& setupExceptions.size() == 0
&& !validationErrors.hasErrors();
}
public Database getDatabase() {
return database;
}
}