All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.changelog.visitor.ValidatingVisitor Maven / Gradle / Ivy

There is a newer version: 3.6.2.5.inovus
Show newest version
package liquibase.changelog.visitor;

import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.changelog.RanChangeSet;
import liquibase.changelog.filter.ChangeSetFilterResult;
import liquibase.database.Database;
import liquibase.exception.*;
import liquibase.logging.LogService;
import liquibase.logging.LogType;
import liquibase.precondition.ErrorPrecondition;
import liquibase.precondition.FailedPrecondition;
import liquibase.precondition.core.PreconditionContainer;
import liquibase.util.StringUtils;

import java.util.*;

public class ValidatingVisitor implements ChangeSetVisitor {

    private List invalidMD5Sums = new ArrayList<>();
    private List failedPreconditions = new ArrayList<>();
    private List errorPreconditions = new ArrayList<>();
    private Set duplicateChangeSets = new HashSet<>();
    private List setupExceptions = new ArrayList<>();
    private List changeValidationExceptions = new ArrayList<>();
    private ValidationErrors validationErrors = new ValidationErrors();
    private Warnings warnings = new Warnings();

    private Set seenChangeSets = new HashSet<>();

    private Map ranIndex;
    private Database database;

    public ValidatingVisitor(List ranChangeSets) {
        ranIndex = new HashMap<>();
        for(RanChangeSet changeSet:ranChangeSets) {
            ranIndex.put(changeSet.toString(), changeSet);
        }
    }

    public void validate(Database database, DatabaseChangeLog changeLog) {
        this.database = database;
        PreconditionContainer preconditions = changeLog.getPreconditions();
        try {
            if (preconditions == null) {
                return;
            }
            preconditions.check(database, changeLog, null, null);
        } catch (PreconditionFailedException e) {
            LogService.getLog(getClass()).debug(LogType.LOG, "Precondition Failed: "+e.getMessage(), e);
            failedPreconditions.addAll(e.getFailedPreconditions());
        } catch (PreconditionErrorException e) {
            LogService.getLog(getClass()).debug(LogType.LOG, "Precondition Error: "+e.getMessage(), e);
            errorPreconditions.addAll(e.getErrorPreconditions());
        } finally {
            try {
                if (database.getConnection() != null) {
                    database.rollback();
                }
            } catch (DatabaseException e) {
                LogService.getLog(getClass()).warning(LogType.LOG, "Error rolling back after precondition check", e);
            }
        }
    }

    @Override
    public Direction getDirection() {
        return ChangeSetVisitor.Direction.FORWARD;
    }

    private RanChangeSet findChangeSet(ChangeSet changeSet) {
        RanChangeSet result = ranIndex.get(changeSet.toString(false));
        if (result == null) {
            for (RanChangeSet ranChangeSet : ranIndex.values()) {
                if (ranChangeSet.getId().equalsIgnoreCase(changeSet.getId())) {
                    if (ranChangeSet.getAuthor().equalsIgnoreCase(changeSet.getAuthor())) {
                        String changeSetPath = normalizePath(changeSet.getFilePath());
                        String ranChangeSetPath = normalizePath(ranChangeSet.getChangeLog());
                        if (ranChangeSetPath.equalsIgnoreCase(changeSetPath)
                            || ranChangeSetPath.endsWith(changeSetPath) || changeSetPath.endsWith(ranChangeSetPath)) {
                            result = ranChangeSet;
                        }
                    }
                }
            }
        }
        return result;
    }
        
    private String normalizePath(String filePath) {
        return filePath.replaceFirst("^classpath:", "");
    }

        @Override
    public void visit(ChangeSet changeSet, DatabaseChangeLog databaseChangeLog, Database database, Set filterResults) throws LiquibaseException {
        RanChangeSet ranChangeSet = findChangeSet(changeSet);
        boolean ran = ranChangeSet != null;
        boolean shouldValidate = !ran || changeSet.shouldRunOnChange() || changeSet.shouldAlwaysRun();
        for (Change change : changeSet.getChanges()) {
            try {
                change.finishInitialization();
            } catch (SetupException se) {
                setupExceptions.add(se);
            }
            
            
            if(shouldValidate){
                warnings.addAll(change.warn(database));

                try {
                    ValidationErrors foundErrors = change.validate(database);
                    if ((foundErrors != null)) {
                        if (foundErrors.hasErrors() && (changeSet.getOnValidationFail().equals
                                (ChangeSet.ValidationFailOption.MARK_RAN))) {
                            LogService.getLog(getClass()).info(
                                    LogType.LOG, "Skipping change set " + changeSet + " due to validation error(s): " +
                                            StringUtils.join(foundErrors.getErrorMessages(), ", "));
                            changeSet.setValidationFailed(true);
                        } else {
                            if (!foundErrors.getWarningMessages().isEmpty())
                                LogService.getLog(getClass()).warning(
                                        LogType.LOG, "Change set " + changeSet + ": " +
                                                StringUtils.join(foundErrors.getWarningMessages(), ", "));
                            validationErrors.addAll(foundErrors, changeSet);
                        }
                    }
                } catch (Exception e) {
                    changeValidationExceptions.add(e);
                }
            }
        }

        if(ranChangeSet != null){
            if (!changeSet.isCheckSumValid(ranChangeSet.getLastCheckSum())) {
                if (!changeSet.shouldRunOnChange()) {
                    invalidMD5Sums.add(changeSet.toString(false)+" was: "+ranChangeSet.getLastCheckSum().toString()+" but is now: "+changeSet.generateCheckSum().toString());
                }
            }
        }

        // Did we already see this ChangeSet?
        String changeSetString = changeSet.toString(false);
        if (seenChangeSets.contains(changeSetString)) {
            duplicateChangeSets.add(changeSet);
            return;
        } else {
            seenChangeSets.add(changeSetString);
        }
    } // public void visit(...)

    public List getInvalidMD5Sums() {
        return invalidMD5Sums;
    }


    public List getFailedPreconditions() {
        return failedPreconditions;
    }

    public List getErrorPreconditions() {
        return errorPreconditions;
    }

    public Set getDuplicateChangeSets() {
        return duplicateChangeSets;
    }

    public List getSetupExceptions() {
        return setupExceptions;
    }

    public List getChangeValidationExceptions() {
        return changeValidationExceptions;
    }

    public ValidationErrors getValidationErrors() {
        return validationErrors;
    }

    public Warnings getWarnings() {
        return warnings;
    }

    public boolean validationPassed() {
        return invalidMD5Sums.isEmpty() && failedPreconditions.isEmpty() && errorPreconditions.isEmpty() &&
            duplicateChangeSets.isEmpty() && changeValidationExceptions.isEmpty() && setupExceptions.isEmpty() &&
            !validationErrors.hasErrors();
    }

    public Database getDatabase() {
        return database;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy