MongoClient injection failing with "Unable to create injector"


(Andy Czerwonka) #1

I’m starting a port from Casbah to the supported Mongo driver. I’m getting the following exception when trying to inject it in a Play app that uses runtime DI.

1 error 
com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Error injecting constructor, java.lang.NoSuchMethodError: com.mongodb.ConnectionString.getApplicationName()Ljava/lang/String;
  at database.MongoEnvironmentImpl.<init>(MongoEnvironment.scala:22)
  at database.MongoEnvironmentImpl.class(MongoEnvironment.scala:22)
  while locating database.MongoEnvironmentImpl
  while locating database.MongoEnvironment
    for the 5th parameter of Tyrion.<init>(Tyrion.scala:19)
  at Module.configure(Module.scala:17) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
  while locating Tyrion

It’s trying to inject the MongoEnvironment, 5th parameter…

@Singleton
class Tyrion @Inject()(config: Configuration,
                       lifecycle: ApplicationLifecycle,
                       akka: ActorSystem,
                       legacyMongo: LegacyMongoEnvironment,
                       mongo: MongoEnvironment,
                       es: EncryptionService,
                       implicit val ec: ExecutionContext) {

The Module binding simply adds another line next to the Casbah driver… (3rd line in the configure method)

class Module extends AbstractModule {

  override def configure(): Unit = {
    bind(classOf[Tyrion]).asEagerSingleton()
    bind(classOf[LegacyMongoEnvironment]).to(classOf[LegacyMongoEnvironmentImpl])
    bind(classOf[MongoEnvironment]).to(classOf[MongoEnvironmentImpl])
    bind(classOf[EncryptionService]).to(classOf[KaliumEncryptionService])
    bind(classOf[Emailer]).to(classOf[SMTPMailer])
    bind(classOf[Messenger]).annotatedWith(Names.named("sms")).to(classOf[SmsMessenger])
    bind(classOf[Messenger]).annotatedWith(Names.named("voice")).to(classOf[VoiceMessenger])
  }

}

And lastly, the MongoEnvironment:

trait MongoEnvironment {
  val db: MongoDatabase
}

@Singleton
class MongoEnvironmentImpl @Inject()(config: Configuration) extends MongoEnvironment {
  private val server = config.getOptional[String]("tyrion.mongo.server").getOrElse("localhost")
  private val port = config.getOptional[Int]("tyrion.mongo.port").getOrElse(27017)
  private val database = config.getOptional[String]("tyrion.mongo.database").getOrElse("tyrion")

  private val codecRegistry = fromRegistries(fromProviders(classOf[Invitation2]), DEFAULT_CODEC_REGISTRY)
  private val mongoClient = MongoClient(s"mongodb://$server:$port")
  override val db = mongoClient.getDatabase(database).withCodecRegistry(codecRegistry)
}

It throws on the second last line, as per the stack trace. It can’t find the ConnectionString class. Wondering if anyone has experienced this issue before?


(Greg Methvin) #3

This type of error is usually due to a version mismatch between libraries. (If that method is being called from your own code, it could mean the compiled class files are out of date and you need to do a clean compile.)

I’m not familiar with these mongodb Java libraries, but you should check that the method referenced actually exists in the version you are using. If not, you may be using incompatible library versions.


(Andy Czerwonka) #4

Thanks @greg, that makes complete sense. Now that I think about it, both the deprecated Casbah and the new Scala driver leverage the Java API directly. And unfortunately, different version of that API. Not sure how we’re going to tackle that, I would like to migrate incrementally, but I’m not sure that’s possible now.


(Andy Czerwonka) #5

I took a stab at:

("org.mongodb" %% "casbah" % "3.1.1").exclude("org.mongodb", "mongo-java-driver"),

but of course the new Java API’s won’t support the old Casbah driver. Does anyone have a suggested strategy to upgrade from Casbah to Scala Mongo Driver in an incremental way that doesn’t require me being in a topic branch for a month?