Add a Precondition
Prerequisites
Implementing Liquibase extensions requires an understanding of Java. You will be creating classes, overriding methods, and working with inheritance hierarchies.
Project Setup
If you have not already created a repository to hold your code, see Your First Extension in the Getting Started guide.
Overview
Liquibase ships with a large number of standard preconditions such as:
- tableExists
- columnExists
- sqlCheck
- etc.
but extensions can provide any functionality desired.
When adding support for a new precondition, the interface you are going to implement is liquibase.precondition.Precondition.
Precondition implementations do not need to be thread-safe. Liquibase will generate a new instance of them each time they are used.
Tip
There is a liquibase.precondition.AbstractPrecondition base class you can use which limits the number of methods you must implement.
API Documentation
A complete description of the API, including what methods must be implemented and how is available on the liquibase.precondition.Precondition API page.
Example Code
package com.example.precondition;
import liquibase.Scope;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.changelog.visitor.ChangeExecListener;
import liquibase.database.Database;
import liquibase.exception.*;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.precondition.AbstractPrecondition;
import liquibase.precondition.ErrorPrecondition;
import liquibase.statement.core.RawSqlStatement;
import java.util.Arrays;
public class PasswordIsSetPrecondition extends AbstractPrecondition {
private String username;
@Override
public String getName() {
return "passwordIsSet";
}
@Override
public Warnings warn(Database database) {
return new Warnings();
}
@Override
public ValidationErrors validate(Database database) {
return new ValidationErrors()
.checkRequiredField("username", getUsername());
}
@Override
public String getSerializedObjectNamespace() {
return GENERIC_CHANGELOG_EXTENSION_NAMESPACE;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public void check(Database database, DatabaseChangeLog changeLog, ChangeSet changeSet, ChangeExecListener changeExecListener) throws PreconditionFailedException, PreconditionErrorException {
try {
Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database);
String password = executor.queryForObject(new RawSqlStatement("select password from app_users where username='" + database.escapeStringForDatabase(username) + "'"), String.class);
if (password == null) {
throw new PreconditionFailedException("Password not set for " + username, changeLog, this);
}
} catch (DatabaseException e) {
throw new PreconditionErrorException(e.getMessage(), Arrays.asList(new ErrorPrecondition(e, changeLog, this)));
}
}
}