flyway: Java Migrations are scanned, found but then ignored

Which version and edition of Flyway are you using?

Community Edition Flyway 6.0.0 (but I experienced the problem with 5.2.0 and 5.2.4 and 6.0.0-beta2)

If this is not the latest version, can you reproduce the issue with the latest one as well?

(Many bugs are fixed in newer releases and upgrading will often resolve the issue) It’s the last version when I’m writing the issue.

Which client are you using? (Command-line, Java API, Maven plugin, Gradle plugin)

Gradle Plugin. I experienced the problem with Gradle 4.9, and now with Gradle 5.4.1

Which database are you using (type & version)?

Oracle 18c

Which operating system are you using?

Centos 7

What did you do?

(Please include the content causing the issue, any relevant configuration settings, the SQL statement that failed (if relevant) and the command you ran.)

I’m running flywayInfo, or flywayMigrate, and I see that flyway knows about my SQL migrations, but ignores completely my Java migrations. It’s like they don’t exist. When I run with debug logs, or attach a debugger to the gradle task execution, I see that the classes are scanned properly, and actually added to the classes list in the Scanner. But when the code reaches Scanner.getClasses, the statement if (!implementedInterface.isAssignableFrom(clazz)) { is returning false for all my classes. implementedInterface is org.flywaydb.core.api.migration.JavaMigration, and clazz is my class, e.g. cern.acctesting.db.migrations.base.V1_1__InsertEnum

V1_1__InsertEnum is declared the following way

import org.flywaydb.core.api.migration.BaseJavaMigration;
...
public class V1_1__InsertEnum extends BaseJavaMigration {
...
What did you expect to see?

I expect my classes to be assignable from the JavaMigration interface, and flywayInfo to take them into account and flywayMigrate to apply them.

What did you see instead?

I cannot run my migrations and my classes seem not to be assignableFrom JavaMigration.

It seems to be a problem with the ClassLoader maybe. Because a simple unit test like this is fine

    @Test
    public void isAssignableFrom(){
        assertTrue(JavaMigration.class.isAssignableFrom(V1_1__InsertEnum.class));
    }

I tried the command line tool, after producing a jar containing my migrations. It’s even more confusing because some Java migrations are recognized (e.g. V1_1__InsertEnum is ok), but others are not, while all are configured the smae.

Thank you for your help Jc ps: here is a confused and confusing post I put on stackoverflow a while ago, maybe it will give you some hints: https://stackoverflow.com/questions/57311404/most-but-not-all-java-flyway-migrations-are-skipped

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 23

Most upvoted comments

I am having this issue. Starting up the spring server applies jdbc migrations, but running gradle flywayInfo says they are in the missing or future state.

If I run gradle flywayClean then gradle flywayInfo then I see only the SQL migrations detected and pending

If I run gradle flywayMigrate then gradle flywayInfo then I see only the SQL migrations were applied. JDBC migrations are totally ignored.

Thank you for your precious help @alextercete, I would not have made it without you! Greetings Jc

@alextercete It seems you got a good lead with the apply from!

With the project that you attached, I have no issue, until I do the apply from.

For instance here is what you can do: Here’s the content of the build.gradle:

buildscript {
    dependencies {
        classpath 'com.h2database:h2:1.4.197'
    }
}

plugins {
    id 'java'
    id 'idea'
}

apply from: 'src/flyway.gradle'

group 'org.flyway.sample'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile "org.flywaydb:flyway-core:6.0.1"
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

Here’s the content of the src/flyway.gradle:

import org.flywaydb.gradle.FlywayPlugin

buildscript {
    repositories {
        maven { url 'http://artifactory.cern.ch/development' }
    }
    dependencies {
        classpath 'org.flywaydb:flyway-gradle-plugin:6.0.1'
    }
}
project.getPlugins().apply(org.flywaydb.gradle.FlywayPlugin.class)


project.dependencies {
    /* Local Groovy needed to avoid Eclipse errors */
    runtime localGroovy()
    compile "org.flywaydb:flyway-gradle-plugin:6.0.1"
}

flyway {
    url = 'jdbc:h2:file:./target/foobar'
    user = 'sa'
    locations = [
            'classpath:db/migration'
    ]
}

And I can reproduce the issue this way in both multi and non-multi project.

@jcgarnier Thanks for all your help so far, I think we’re getting to the bottom of it.

It looks like this StackOverflow answer explains this behaviour, particularly in this [slightly modified] bit:

ClassLoader loader = new URLClassLoader(urls, null);
Class<?> c = loader.loadClass("net.torokpeter.Foo");
System.out.println(Foo.class.isAssignableFrom(c)); // false

My hypothesis is that there are two class loaders in play and that the one we’re getting from Gradle doesn’t have the “current” class loader as its parent, and that’s why the isAssignableFrom check fails. I’ll do some debugging to test this.

This is what I expect:

13:41:17.449 [QUIET] [system.out] [DEBUG] - Adding directory to Classpath: file:/opt/garnierj/intellijws/acctesting/lhc-hwc-acctesting-db/build/classes/java/main/
13:41:27.887 [QUIET] [system.out] [DEBUG] - Adding directory to Classpath: file:/opt/garnierj/intellijws/acctesting/lhc-hwc-acctesting-db/build/resources/main/
13:41:33.951 [QUIET] [system.out] [DEBUG] - Adding directory to Classpath: file:/opt/garnierj/intellijws/acctesting/lhc-hwc-acctesting-db/build/classes/java/test/
13:41:42.341 [QUIET] [system.out] [DEBUG] - Adding directory to Classpath: file:/opt/garnierj/intellijws/acctesting/lhc-hwc-acctesting-db/build/resources/test/

Then the ClassPathScanner behaves as expected (the delay in time is because I attached a debugger to the gradle process):

14:06:14.887 [QUIET] [system.out] [DEBUG] - Found class: cern.acctesting.db.migrations.base.V1_1__InsertEnum
14:06:26.052 [QUIET] [system.out] [DEBUG] - Found class: cern.acctesting.db.migrations.base.V1_2__InsertDefaultCampaign
14:07:17.991 [QUIET] [system.out] [DEBUG] - Found class: cern.acctesting.db.migrations.base.V2_5__IncreaseCommentColumnLength
14:08:17.387 [QUIET] [system.out] [DEBUG] - Found class: cern.acctesting.db.migrations.base.V2_8_0__Add_HWC_EE_EXPERT_Role

And the classes are added to the Scanner.classes field:

classes = {ArrayList@12085}  size = 4
 0 = {Class@11975} "class cern.acctesting.db.migrations.base.V1_1__InsertEnum"
 1 = {Class@12004} "class cern.acctesting.db.migrations.base.V1_2__InsertDefaultCampaign"
 2 = {Class@12048} "class cern.acctesting.db.migrations.base.V2_5__IncreaseCommentColumnLength"
 3 = {Class@12065} "class cern.acctesting.db.migrations.base.V2_8_0__Add_HWC_EE_EXPERT_Role"

But later they’re filtered out: unassignableJavaMigration