Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Cannot invoke "String.length()" because "path" is null #3028

Open
kareemelzayat opened this issue Jul 17, 2024 · 10 comments
Assignees

Comments

@kareemelzayat
Copy link
Contributor

kareemelzayat commented Jul 17, 2024

Expected Behavior

I initiated a micronaut project with Kotlin on Java 21 and the following features:

  • Micronaut Data Hibernate Reactive
  • Liquibase
  • Postgres
  • JDBC-Hikari
  • Testcontainers

I am expecting that the generated test file to work out of the box and the test passes. The test file looks like this:

@MicronautTest
class DemoTest {

    @Inject
    lateinit var application: EmbeddedApplication<*>

    @Test
    fun testItWorks() {
        Assertions.assertTrue(application.isRunning)
    }

}

Actual Behaviour

Instead, I am getting the following error:

Bean definition [org.hibernate.SessionFactory] could not be loaded: Error instantiating bean of type  [org.hibernate.boot.SessionFactoryBuilder]

Message: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Cannot invoke "String.length()" because "path" is null
Path Taken: SessionFactoryPerJpaConfigurationFactory.buildHibernateSessionFactoryBuilder(SessionFactoryBuilder sessionFactoryBuilder) --> SessionFactoryPerJpaConfigurationFactory.buildHibernateSessionFactoryBuilder([SessionFactoryBuilder sessionFactoryBuilder]) --> SessionFactoryPerJpaConfigurationFactory.buildHibernateSessionFactoryBuilder([Metadata metadata],JpaConfiguration jpaConfiguration)
io.micronaut.context.exceptions.BeanInstantiationException: Bean definition [org.hibernate.SessionFactory] could not be loaded: Error instantiating bean of type  [org.hibernate.boot.SessionFactoryBuilder]

Looking at the console a bit deeper, it seems that the SQL client URL is not properly calculated:

10:57:13.974 [Test worker] INFO  o.h.r.pool.impl.DefaultSqlClientPool - HR000011: SQL Client URL [jdbc:tc:postgresql:16.3:///db]

Notice the literal value jdbc:tc:postgresql:16.3:///db, which should instead look something like this jdbc:postgresql://localhost:55958/test?loggerLevel=OFF (the generated URL of the testcontainer)

Steps To Reproduce

1- Start a Micronaut Projec with Kotlin on Java 21 and Gradle

2- Install the dependencies:

  • Micronaut Data Hibernate Reactive:
    kapt("io.micronaut.data:micronaut-data-processor")
    implementation("io.micronaut.data:micronaut-data-hibernate-reactive")
    implementation("io.micronaut.data:micronaut-data-tx-hibernate")
    implementation("io.micronaut.sql:micronaut-hibernate-reactive")
  • PostgreSQL DB:
    implementation("io.vertx:vertx-pg-client")
    runtimeOnly("org.postgresql:postgresql")
  • Liquibase:
     implementation("io.micronaut.liquibase:micronaut-liquibase")
  • Testcontainers:
    testImplementation("org.testcontainers:postgresql")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:testcontainers")

3- Adjust the config files:

  • application.conf:
liquibase {
  datasources {
    default {
      change-log = "classpath:db/liquibase-changelog.json"
    }
  }
}

datasources {
  default {
    db-type = "postgres"
    dialect = "POSTGRES"
    driverClassName = "org.postgresql.Driver"
    password = "${DATABASE_PW:password}"
    url = "jdbc:postgresql://${DATABASE_URL:`localhost:5432`}/mydb"
    username = "${DATABASE_USER:sa}"
  }
}

jpa {
  default {
    reactive = true
    entity-scan {
      packages = "com.mstqr.domain"
    }
    properties {
      hibernate {
        connection {
          db-type = "${datasources.default.db-type}"
          password = "${datasources.default.password}"
          url = "${datasources.default.url}"
          username = "${datasources.default.user}"
        }
        show_sql = true
        hbm2ddl {
          auto = "none"
        }
      }
    }
  }
}
  • application-test.conf:
datasources {
  default {
    driverClassName = "org.testcontainers.jdbc.ContainerDatabaseDriver"
    url = "jdbc:tc:postgresql:16.3:///db"
  }
}

jpa {
  default {
   reactive = true
    properties {
      hibernate {
        connection {
          driverClassName = ${datasources.default.driverClassName}
          url = ${datasources.default.url}
        }
        hbm2ddl {
          auto = "none"
        }
      }
    }
  }
}

4- Run any test via ./gradlew test

5- Check the console for the error

Environment Information

JDK version: 21
Kotlin version: 1.9.24
Build Tool: Gradle Kotlin
Test framework: Junit5

Example Application

https://github.com/kareemelzayat/demo-micronaut

Version

4.5.0

@kareemelzayat
Copy link
Contributor Author

@dstepanov it seems there is an issue integrating Micronaut Data Hibernate Reactive with Testcontainers and Liquibase.

I tried everything I could to make it work, the test always fails.

@dstepanov
Copy link
Contributor

Please create a sample app

@kareemelzayat
Copy link
Contributor Author

@lengors
Copy link

lengors commented Sep 14, 2024

Any updates on this? I had the same issue, even without liquibase.

@danpoland
Copy link

danpoland commented Nov 17, 2024

Bump for updates. I am having the same issue with a brand new project without any migration management tooling.

@radovanradic
Copy link
Contributor

radovanradic commented Nov 18, 2024

I investigated this issue and not sure Micronaut can support hibernate reactive with test containers. This is where it is failing in org.hibernate.reactive.pool.impl.DefaultSqlClientPoolConfiguration

@Override
	public SqlConnectOptions connectOptions(URI uri) {
		String scheme = uri.getScheme();
		String path = scheme.equals( "oracle" )
				? oraclePath( uri )
				: uri.getPath();

		String database = path.length() > 0
				? path.substring( 1 )
				: "";

because the url getting here is from tc url: tc:postgresql:14:///postgres and Micronaut is not able to populate actual URL because it doesn't control test container startup.

Instead, you can use Micronaut Test Resources with hibernate reactive. In setup similar to this, would need to have application-prod.properties (or application-dev.properties) and application-test.properties so test resources can figure out it needs to populate connection properties and not inherit from application.properties .

@danpoland
Copy link

I investigated this issue and not sure Micronaut can support hibernate reactive with test containers. This is where it is failing in org.hibernate.reactive.pool.impl.DefaultSqlClientPoolConfiguration

@Override
	public SqlConnectOptions connectOptions(URI uri) {
		String scheme = uri.getScheme();
		String path = scheme.equals( "oracle" )
				? oraclePath( uri )
				: uri.getPath();

		String database = path.length() > 0
				? path.substring( 1 )
				: "";

because the url getting here is from tc url: tc:postgresql:14:///postgres and Micronaut is not able to populate actual URL because it doesn't control test container startup.

Instead, you can use Micronaut Test Resources with hibernate reactive. In setup similar to this, would need to have application-prod.properties (or application-dev.properties) and application-test.properties so test resources can figure out it needs to populate connection properties and not inherit from application.properties .

Thanks for the information. I have always been confused by the difference between Micronaut Test-Resources and Micronaut's testcontainer integration. I know they are two separate items in Microanut Launch. The test-resources documentation can be found here: https://micronaut-projects.github.io/micronaut-test-resources/2.7.0/guide/. Does anyone know where the testcontainers integration documentation can be found?

@radovanradic
Copy link
Contributor

I don't think there is separate documentation or support for testcontainers in Micronaut. This is why there is Micronaut Test Resources that sets up container and for example DB connections using test containers.
Without that, then I guess users need to start containers manually and setup connection, similar like this test in hibernate-reactive https://github.com/hibernate/hibernate-reactive/blob/main/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java

@danpoland
Copy link

I did a little experimenting using Micronaut Launch, creating a seed project with just the testcontainers integration, excluding Micronaut Test Resources.

The resulting gradle file only includes the following test related dependencies:

    testImplementation("io.micronaut:micronaut-http-client")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:postgresql")
    testImplementation("org.testcontainers:testcontainers")

It produces an application-test.yml file with a single line:

jpa.default.properties.hibernate.connection.url: jdbc:tc:postgresql:14:///postgres

I wrote a simple test and confirmed database interactions worked as expected. All without the use of Micronaut Test Resources and without explicitly wiring up a testcontainer. I assumed based on the testcontainers documentation that I would have to configure a container somewhere. I imagine the magic is in jdbc url jdbc:tc:postgresql:14:///postgres I just haven't figured out where the wiring is.

Either way, thanks for the help. I will look into the test resources solution.

@radovanradic
Copy link
Contributor

I would expect this driver is declared somewhere org.testcontainers.jdbc.ContainerDatabaseDriver which is able to resolve url from the configured tc: url.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

No branches or pull requests

5 participants