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

io.quarkus.liquibase.runtime.LiquibaseRecorder Maven / Gradle / Ivy

package io.quarkus.liquibase.runtime;

import java.util.Locale;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.sql.DataSource;

import io.quarkus.agroal.runtime.AgroalDataSourceUtil;
import io.quarkus.arc.ActiveResult;
import io.quarkus.arc.Arc;
import io.quarkus.arc.InactiveBeanException;
import io.quarkus.arc.InjectableInstance;
import io.quarkus.arc.SyntheticCreationalContext;
import io.quarkus.liquibase.LiquibaseFactory;
import io.quarkus.runtime.ResettableSystemProperties;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.annotations.Recorder;
import liquibase.Liquibase;
import liquibase.lockservice.LockServiceFactory;

@Recorder
public class LiquibaseRecorder {

    private final RuntimeValue config;

    public LiquibaseRecorder(RuntimeValue config) {
        this.config = config;
    }

    public Supplier liquibaseCheckActiveSupplier(String dataSourceName) {
        return new Supplier() {
            @Override
            public ActiveResult get() {
                // Flyway beans are inactive when the datasource itself is inactive.
                var dataSourceBean = AgroalDataSourceUtil.dataSourceInstance(dataSourceName).getHandle().getBean();
                var dataSourceActive = dataSourceBean.checkActive();
                if (!dataSourceActive.value()) {
                    return ActiveResult.inactive(
                            String.format(Locale.ROOT,
                                    "Liquibase for datasource '%s' was deactivated automatically because this datasource was deactivated.",
                                    dataSourceName),
                            dataSourceActive);
                }

                // Note: When quarkus.liquibase.enabled is set to false, Liquibase beans are still available.
                //       The property only controls automatic execution on startup.
                // TODO should we change quarkus.liquibase.enabled (see ^) to align on other extensions?
                //   See https://github.com/quarkusio/quarkus/issues/42244.
                //   We'd have something like quarkus.liquibase.startup.enabled controlling startup behavior,
                //   and *if necessary* quarkus.liquibase.active controlling bean availability
                //   (though IMO controlling that at the datasource level would be enough).
                return ActiveResult.active();
            }
        };
    }

    public Function, LiquibaseFactory> liquibaseFunction(String dataSourceName) {
        return new Function, LiquibaseFactory>() {
            @Override
            public LiquibaseFactory apply(SyntheticCreationalContext context) {
                DataSource dataSource = context.getInjectedReference(DataSource.class,
                        AgroalDataSourceUtil.qualifier(dataSourceName));
                LiquibaseFactoryProducer liquibaseProducer = context.getInjectedReference(LiquibaseFactoryProducer.class);
                return liquibaseProducer.createLiquibaseFactory(dataSource, dataSourceName);
            }
        };
    }

    public void doStartActions(String dataSourceName) {
        if (!config.getValue().enabled) {
            return;
        }

        var dataSourceConfig = config.getValue().getConfigForDataSourceName(dataSourceName);
        if (!dataSourceConfig.cleanAtStart && !dataSourceConfig.migrateAtStart) {
            return;
        }

        InjectableInstance liquibaseFactoryInstance = Arc.container().select(LiquibaseFactory.class,
                LiquibaseFactoryUtil.getLiquibaseFactoryQualifier(dataSourceName));
        if (!liquibaseFactoryInstance.isResolvable()
                || !liquibaseFactoryInstance.getHandle().getBean().isActive()) {
            return;
        }

        LiquibaseFactory liquibaseFactory = liquibaseFactoryInstance.get();
        try (Liquibase liquibase = liquibaseFactory.createLiquibase();
                ResettableSystemProperties resettableSystemProperties = liquibaseFactory
                        .createResettableSystemProperties()) {
            if (dataSourceConfig.cleanAtStart) {
                liquibase.dropAll();
            }
            if (dataSourceConfig.migrateAtStart) {
                var lockService = LockServiceFactory.getInstance()
                        .getLockService(liquibase.getDatabase());
                lockService.waitForLock();
                try {
                    if (dataSourceConfig.validateOnMigrate) {
                        liquibase.validate();
                    }
                    liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels());
                } finally {
                    lockService.releaseLock();
                }
            }
        } catch (InactiveBeanException e) {
            // These exceptions should be self-explanatory
            throw e;
        } catch (Exception e) {
            throw new IllegalStateException("Error starting Liquibase", e);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy