I have a use case where I need to enforce one of my application class (a singleton service) is loaded before HikariCP initializes and creates connection to the database.
As HikariCP is initialized "automatically" by Play out-of-the-box, I don't know how to enforce it.
Basically, I have a class like the following:
@Singleton
class MyClassThatNeedToBeLoadedBeforeHikariCP() {
// Setup some global state for OpenTelemetry
}
If I wanted to initialize it before another class of my own, I would use regular DI:
@Singleton
class OtherClassToLoadAfterMyClass() @Inject()(myClass: MyClassThatNeedToBeLoadedBeforeHikariCP) {
}
But this "other class" is actually HikariCP.
How to define a dependency (in terms of Guice) between HikariCP and a class of my own?
To give the complete context, I need to do this because I'm using a JDBC driver that tries to setup some global state as well and if initialized before my MyClassThatNeedToBeLoadedBeforeHikariCP
class, then my class will fail to be initialized.
This is because under the hood, OpenTelemetry global state can only be defined once.
See related discussion in the issue tracked of the JDBC driver (Trino).
And to help reach visibility on this topic, here's the stack trace one might get if encountering the same issue:
Caused by: java.lang.IllegalStateException: GlobalOpenTelemetry.set has already been called. GlobalOpenTelemetry.set must be called only once before any calls to GlobalOpenTelemetry.get. If you are using the OpenTelemetrySdk, use OpenTelemetrySdkBuilder.buildAndRegisterGlobal instead. Previous invocation set to cause of this exception.
at io.opentelemetry.api.GlobalOpenTelemetry.set(GlobalOpenTelemetry.java:104)
at io.opentelemetry.sdk.OpenTelemetrySdkBuilder.buildAndRegisterGlobal(OpenTelemetrySdkBuilder.java:85)
at com.myapp.metrics.setup.OTELService.<init>(OTELService.scala:41)
^^^^^^^^^ == MyClassThatNeedToBeLoadedBeforeHikariCP
at com.myapp.metrics.setup.OTELService$$FastClassByGuice$$1270092a.newInstance(<generated>)
at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:89)
...
Caused by: java.lang.Throwable
at io.opentelemetry.api.GlobalOpenTelemetry.set(GlobalOpenTelemetry.java:112)
at io.opentelemetry.api.GlobalOpenTelemetry.get(GlobalOpenTelemetry.java:82)
at io.trino.jdbc.NonRegisteringTrinoDriver.instrumentClient(NonRegisteringTrinoDriver.java:68)
at io.trino.jdbc.NonRegisteringTrinoDriver.connect(NonRegisteringTrinoDriver.java:62)
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358)
The solution is to:
MyClassThatNeedToBeLoadedBeforeHikariCP
classSee relevant part of the Play documentation.
In Play configuration file:
play.modules.disabled += "play.api.db.HikariCPModule"
play.modules.enabled += "com.myapp.modules.HikariCPModuleWithCustomDependency"
And create the following module:
class HikariCPModuleWithCustomDependency extends SimpleModule(
bind[ConnectionPool].to[HikariCPConnectionPoolWithCustomDependency]
)
@Singleton
class HikariCPConnectionPoolWithCustomDependency @Inject() (
environment: Environment,
@nowarn myClass: MyClassThatNeedToBeLoadedBeforeHikariCP
)
extends HikariCPConnectionPool(environment) {}