0

I'm using phantom to connect to Apache Cassandra and want to configure the connector at runtime, i.e. I want to parse some configuration file, extract a list of Cassandra databases and pass that somehow to my Database object.

I followed this guide to have an additional layer DatabaseProvider between Database and my service. Hence, I can provide a static DatabaseProvider like this:

object ProdConnector {
  val connector = ContactPoints(Seq("dev-cassndr.containers"), 9042)
    .keySpace("test")
}

object ProdDatabase extends MyDatabase(ProdConnector.connector)

trait ProdDatabaseProvider extends MyDatabaseProvider {
  override def database: MyDatabase = ProdDatabase
}

and in my main function I do

val service = new MessageService with ProdDatabaseProvider {}

How can I achieve the same result at runtime without the singleton objects?

I made several attempst but always got NullPointerExceptions. My current approach is to have a Cassandra configuration object which is read by Jackson from a file:

case class CassandraConfigurator(
  contactPoints: Seq[String],
  keySpace: String,
  port: Int = 9042,
) {
  @JsonCreator
  def this() = this(null, null, 9042)

  val connection: CassandraConnection = {
    val p = ContactPoints(contactPoints, port)
    p.keySpace(keySpace)
  }
}

my entry point then extends StreamApp from fs2

object Main extends StreamApp[IO] {
  override def stream(args: List[String], reqShutdown: IO[Unit])
      : Stream[IO, ExitCode] = {
    val conf: CassandraConfigurator = ???
    val service = new MyService with MyDatabaseProvider {
      override def database: MyDatabase = new MyDatabase(conf.connection)
    }

    service.database.create()
    val api = ApiWithService(service).getApi
    BlazeBuilder[IO].bindHttp(80, "0.0.0.0").mountService(api, "/").serve
  }
}

This results in the following error:

12:44:03.436 [pool-10-thread-1] INFO  com.datastax.driver.core.GuavaCompatibility - Detected Guava >= 19 in the classpath, using modern compatibility layer
12:44:03.436 [pool-10-thread-1] INFO  com.datastax.driver.core.GuavaCompatibility - Detected Guava >= 19 in the classpath, using modern compatibility layer
12:44:03.463 [pool-10-thread-1] DEBUG com.datastax.driver.core.SystemProperties - com.datastax.driver.NEW_NODE_DELAY_SECONDS is undefined, using default value 1
12:44:03.463 [pool-10-thread-1] DEBUG com.datastax.driver.core.SystemProperties - com.datastax.driver.NEW_NODE_DELAY_SECONDS is undefined, using default value 1
12:44:03.477 [pool-10-thread-1] DEBUG com.datastax.driver.core.SystemProperties - com.datastax.driver.NOTIF_LOCK_TIMEOUT_SECONDS is undefined, using default value 60
12:44:03.477 [pool-10-thread-1] DEBUG com.datastax.driver.core.SystemProperties - com.datastax.driver.NOTIF_LOCK_TIMEOUT_SECONDS is undefined, using default value 60
java.lang.NullPointerException
    at com.outworkers.phantom.connectors.ContactPoints$.$anonfun$apply$3(ContactPoint.scala:101)
    at com.outworkers.phantom.connectors.DefaultSessionProvider.<init>(DefaultSessionProvider.scala:37)
    at com.outworkers.phantom.connectors.CassandraConnection.provider$lzycompute(CassandraConnection.scala:46)
    at com.outworkers.phantom.connectors.CassandraConnection.provider(CassandraConnection.scala:41)
    at com.outworkers.phantom.connectors.CassandraConnection.session$lzycompute(CassandraConnection.scala:52)
    at com.outworkers.phantom.connectors.CassandraConnection.session(CassandraConnection.scala:52)
    at com.outworkers.phantom.database.Database.session$lzycompute(Database.scala:36)
    at com.outworkers.phantom.database.Database.session(Database.scala:36)
    at com.outworkers.phantom.ops.DbOps.$anonfun$createAsync$2(DbOps.scala:66)
    at com.outworkers.phantom.builder.query.execution.ExecutionHelper$.$anonfun$sequencedTraverse$2(ExecutableStatements.scala:71)
    at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:304)
    at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:37)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Exception: sbt.TrapExitSecurityException thrown from the UncaughtExceptionHandler in thread "run-main-0"
[error] java.lang.RuntimeException: Nonzero exit code: 1
[error]     at sbt.Run$.executeTrapExit(Run.scala:124)
[error]     at sbt.Run.run(Run.scala:77)
[error]     at sbt.Defaults$.$anonfun$bgRunTask$5(Defaults.scala:1168)
[error]     at sbt.Defaults$.$anonfun$bgRunTask$5$adapted(Defaults.scala:1163)
[error]     at sbt.internal.BackgroundThreadPool.$anonfun$run$1(DefaultBackgroundJobService.scala:366)
[error]     at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
[error]     at scala.util.Try$.apply(Try.scala:209)
[error]     at sbt.internal.BackgroundThreadPool$BackgroundRunnable.run(DefaultBackgroundJobService.scala:289)
[error]     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error]     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error]     at java.lang.Thread.run(Thread.java:748)
Thomas Bach
  • 103
  • 7
  • What is the reason for the NPE? Do you have a stack trace? – flavian Feb 28 '18 at 00:51
  • Just updated the question with my current approach and the full stack trace plus logs of the occuring error. My guess it is kind of the same issue as https://stackoverflow.com/questions/18807564/implicit-parameters-and-nullpointerexception-in-scala, but I don't know how to hack around it… – Thomas Bach Feb 28 '18 at 11:53

1 Answers1

0

In your case there's nothing implicit here, and the runtime stuff looks like it should be simple. Your problem is more than likely Jackson failing to read from the file. To test that try the below, if it's successfully trying to connect to local Cassandra, then that's your problem.

object Main extends StreamApp[IO] {
  override def stream(args: List[String], reqShutdown: IO[Unit])
      : Stream[IO, ExitCode] = {

    val service = new MyService with MyDatabaseProvider {
      override def database: MyDatabase = new MyDatabase(Connector.default)
    }

    service.database.create()
    val api = ApiWithService(service).getApi
    BlazeBuilder[IO].bindHttp(80, "0.0.0.0").mountService(api, "/").serve
  }
}

Are you sure val conf: CassandraConfigurator = ??? is being properly initialised? If that is what you have in code, I am not surprised you are getting the NPE.

flavian
  • 28,161
  • 11
  • 65
  • 105