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

se.fortnox.reactivewizard.dbmigrate.LiquibaseMigrate Maven / Gradle / Ivy

The newest version!
package se.fortnox.reactivewizard.dbmigrate;

import jakarta.inject.Inject;
import liquibase.CatalogAndSchema;
import liquibase.Contexts;
import liquibase.LabelExpression;
import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.changelog.ChangeLogHistoryServiceFactory;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.executor.ExecutorService;
import liquibase.ext.TimeoutLockService;
import liquibase.lockservice.LockServiceFactory;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.InputStreamList;
import se.fortnox.reactivewizard.db.DbDriver;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * Runs liquibase migrations for each file on the classpath named according to config. In a development environment this
 * is may be multiple files. In a production environment (fatjar) this is often a single file.
 */
public class LiquibaseMigrate {

    private final List liquibaseList;

    @Inject
    public LiquibaseMigrate(LiquibaseConfig liquibaseConfig) throws LiquibaseException, IOException {
        JdbcConnection conn = new JdbcConnection(getConnection(liquibaseConfig));

        Enumeration resources = this.getClass()
                .getClassLoader()
                .getResources(liquibaseConfig.getMigrationsFile());
        if (!resources.hasMoreElements()) {
            throw new RuntimeException("Could not find migrations file " + liquibaseConfig.getMigrationsFile());
        }

        TimeoutLockService.setRenewalConnectionCreator(() -> createDatabaseConnectionFromConfiguration(liquibaseConfig));

        liquibaseList = new ArrayList<>();
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            String file = url.toExternalForm();
            int jarFileSep = file.lastIndexOf('!');
            String loggedFileName = file.substring(jarFileSep + 1);
            Liquibase liquibase = new Liquibase(loggedFileName, new UrlAwareClassLoaderResourceAccessor(file), conn);
            liquibase.getDatabase().setDefaultSchemaName(liquibaseConfig.getSchema());

            liquibaseList.add(liquibase);
        }
    }

    private Database createDatabaseConnectionFromConfiguration(LiquibaseConfig configuration) {
        try {
            return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(getConnection(configuration)));
        } catch (DatabaseException e) {
            throw new RuntimeException(e);
        }
    }

    private Connection getConnection(LiquibaseConfig conf) {
        try {
            DbDriver.loadDriver(conf.getUrl());
            Connection connection = DriverManager.getConnection(conf.getUrl(), conf.getUser(), conf.getPassword());
            if (conf.getSchema() != null) {
                connection.setSchema(conf.getSchema());
            }
            return connection;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Run this migration.
     *
     * @throws LiquibaseException on error
     */
    public void run() throws LiquibaseException {
        for (Liquibase liquibase : liquibaseList) {
            liquibase.update((String) null);
        }
    }

    /**
     * Drop all db objects.
     *
     * @throws DatabaseException on error
     */
    public void drop() throws DatabaseException {
        for (Liquibase liquibase : liquibaseList) {
            liquibase.dropAll();
            break;
        }
        LockServiceFactory.getInstance().resetAll();
    }

    /**
     * Force drop all db objects.
     *
     * @throws DatabaseException on error
     */
    public void forceDrop() throws DatabaseException {
        for (Liquibase liquibase : liquibaseList) {
            Database database = liquibase.getDatabase();
            CatalogAndSchema schema = new CatalogAndSchema(database.getDefaultCatalogName(), database.getDefaultSchemaName());
            try {
                liquibase.checkLiquibaseTables(false, null, new Contexts(), new LabelExpression());
                database.dropDatabaseObjects(schema);
            } catch (DatabaseException e) {
                throw e;
            } catch (Exception e) {
                throw new DatabaseException(e);
            } finally {
                LockServiceFactory.getInstance().getLockService(database).destroy();
                LockServiceFactory.getInstance().resetAll();
                ChangeLogHistoryServiceFactory.getInstance().resetAll();
                ExecutorService executorService = Scope.getCurrentScope().getSingleton(ExecutorService.class);
                executorService.reset();
            }
            return;
        }
    }

    /**
     * Rollback to latest changeset in all migration files.
     *
     * @throws LiquibaseException on error
     */
    public void rollback() throws LiquibaseException {
        for (Liquibase liquibase : liquibaseList) {
            liquibase.rollback(1, null);
        }
    }

    void exit() {
        System.exit(0);
    }

    class UrlAwareClassLoaderResourceAccessor extends ClassLoaderResourceAccessor {
        private final String realFileName;

        public UrlAwareClassLoaderResourceAccessor(String realFileName) {
            this.realFileName = realFileName;
        }

        @Override
        public InputStreamList openStreams(String relativeTo, String path) throws IOException {
            if (realFileName.endsWith(path)) {
                path = realFileName;
            }

            if (path.startsWith("file:") || path.startsWith("jar:file:")) {
                URL url = new URL(path);
                InputStream resourceAsStream = url.openStream();
                InputStreamList inputStreamList = new InputStreamList();
                URI uri;
                try {
                    uri = url.toURI();
                } catch (URISyntaxException e) {
                    throw new RuntimeException(e);
                }
                inputStreamList.add(uri, resourceAsStream);

                return inputStreamList;
            }
            return super.openStreams(relativeTo, path);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy