Skip to content

Not able to implement custom implementations of LiquibaseMigrator  #325

@geofrey-s

Description

@geofrey-s

Issue description

Expected
Allow custom implementation of the LiquibaseMigrator through inheritence such that one can customize different aspects when running the liquibase migrations, for instance, in my case I am trying apply migrations across all the different tenant's database schemas (Postgres) by dynamically setting the liquibaseSchema config

Actual
When I try to run the application, I am the error log below

ERROR io.micronaut.runtime.Micronaut - Error starting Micronaut server: Error loading bean [xyz.abc.MigrationHandler]: failed to access class io.micronaut.liquibase.LiquibaseMigrationRunner from class xyz.abc.$MigrationHandler$Definition$Exec (io.micronaut.liquibase.LiquibaseMigrationRunner and xyz.abc.$MigrationHandler$Definition$Exec are in unnamed module of loader 'app')
io.micronaut.context.exceptions.BeanContextException: Error loading bean [xyz.abc.MigrationHandler]: failed to access class io.micronaut.liquibase.LiquibaseMigrationRunner from class xyz.abc.$MigrationHandler$Definition$Exec (io.micronaut.liquibase.LiquibaseMigrationRunner and xyz.abc.$MigrationHandler$Definition$Exec are in unnamed module of loader 'app')
	at io.micronaut.context.DefaultBeanContext.findBeanCandidates(DefaultBeanContext.java:2096)
	at io.micronaut.context.DefaultApplicationContext.findBeanCandidates(DefaultApplicationContext.java:260)
	at io.micronaut.context.DefaultBeanContext.findBeanCandidatesInternal(DefaultBeanContext.java:3348)
	at io.micronaut.context.DefaultBeanContext.getBeanDefinitions(DefaultBeanContext.java:808)
	at io.micronaut.context.DefaultBeanContext.getBeanDefinitions(DefaultBeanContext.java:802)
	at io.micronaut.context.DefaultBeanContext.loadCreatedListeners(DefaultBeanContext.java:1825)
	at io.micronaut.context.DefaultBeanContext.initializeEventListeners(DefaultBeanContext.java:1799)
	at io.micronaut.context.DefaultBeanContext.readAllBeanDefinitionClasses(DefaultBeanContext.java:3325)
	at io.micronaut.context.DefaultBeanContext.finalizeConfiguration(DefaultBeanContext.java:3684)
	at io.micronaut.context.DefaultBeanContext.start(DefaultBeanContext.java:341)
	at io.micronaut.context.DefaultApplicationContext.start(DefaultApplicationContext.java:194)
	at io.micronaut.runtime.Micronaut.start(Micronaut.java:75)
	at io.micronaut.runtime.Micronaut.run(Micronaut.java:323)
	at io.micronaut.runtime.Micronaut.run(Micronaut.java:309)
	at xyz.abc.Application.main(Application.groovy:10)
Caused by: java.lang.IllegalAccessError: failed to access class io.micronaut.liquibase.LiquibaseMigrationRunner from class xyz.abc.$MigrationHandler$Definition$Exec (io.micronaut.liquibase.LiquibaseMigrationRunner and xyz.abc.$MigrationHandler$Definition$Exec are in unnamed module of loader 'app')
	at xyz.abc.$MigrationHandler$Definition$Exec.<clinit>(Unknown Source)
	at xyz.abc.$MigrationHandler$Definition.<init>(Unknown Source)
	at xyz.abc.$MigrationHandler$Definition.<init>(Unknown Source)
	at xyz.abc.$MigrationHandler$Definition$Reference.load(Unknown Source)
	at io.micronaut.context.AbstractInitializableBeanDefinitionReference.load(AbstractInitializableBeanDefinitionReference.java:145)
	at io.micronaut.context.DefaultBeanContext.findBeanCandidates(DefaultBeanContext.java:2094)
	... 14 common frames omitted

Process finished with exit code 1

Here is a trimed down custom implementation of the LiquibaseMigrator, I am using Groovy, though I've adapted it for java too.

package xyz.abc

import io.micronaut.context.ApplicationContext;
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.core.naming.NameResolver;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.jdbc.DataSourceResolver;
import io.micronaut.liquibase.LiquibaseConfigurationProperties;
import io.micronaut.liquibase.LiquibaseMigrator;
import liquibase.resource.ResourceAccessor;
import jakarta.inject.Singleton;

import javax.sql.DataSource;

@Singleton
class MigrationHandler extends LiquibaseMigrator{
    public MigrationHandler(
            ApplicationContext applicationContext,
            ResourceAccessor resourceAccessor,
            DataSourceResolver dataSourceResolver) {
        super(applicationContext, resourceAccessor, dataSourceResolver);
    }

    @Override
    public DataSource onCreated(BeanCreatedEvent<DataSource> event) {
        DataSource dataSource = (DataSource)event.getBean();
        if (event.getBeanDefinition() instanceof NameResolver) {
            ((NameResolver)event.getBeanDefinition()).resolveName().flatMap((name) -> {
                return this.applicationContext.findBean(LiquibaseConfigurationProperties.class, Qualifiers.byName(name));
            }).ifPresent((cfg) -> {
                DataSource unwrappedDataSource = this.dataSourceResolver.resolve(dataSource);
                this.runMigrations(cfg, unwrappedDataSource);
            });
        }

        return dataSource;
    }

    protected void runMigrations(LiquibaseConfigurationProperties configs, DataSource dataSource){
        String[] schemas =  new String[]{"tenant1", "tenant2"};

        Arrays.stream(schemas).forEach((tenant) -> {
            configs.setLiquibaseSchema(tenant)
            this.run(configs, dataSource);
        });
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions