Reputation: 1308
I have a web application in Scala/Play with Cassandra as the database. I ran into following issue while testing for potential errors.
Below are the components of my project
Application.scala -> Basic controller
Model.scala -> Holds the business logic
CassandraClient.scala -> Logic to connect to cassandra and run query on cassandra
CassandraClient.scala looks like below
object CassandraClient {
private val cluster = Cluster.builder()
.withLoadBalancingPolicy(
new WhiteListPolicy(new RoundRobinPolicy(), nodes))
.withSocketOptions(socketOptions)
.addContactPointsWithPorts(nodes)
.withCredentials(CASSANDRA_USERNAME, CASSANDRA_PWD).build()
}
private val session = cluster.connect()
def getValueFromCassandraTable(token:String) = {
var query = QueryBuilder.select.all()...
seesion.execute(query)
}
The Cassandra connection is first established when i call getValueFromCassandraTable the first time. And since CassandraClient is an Object, the logic to connect to Cassandra only gets called once.
Model.Scala has some code to handle future returned by session.execute.
For eg: Look at sample code below in Model.scala.
def getTitles(titles: String)(implicit ctxt: ExecutionContext): Future[List[<Sometype>]] = {
try{
CassandraClient.getValueFromCassandraTable(token).toScalaFuture.map { rows =>
rows.map(row => row("value").toList
}.recover { case e: Exception =>List()}
}
} catch{case e:Exception =>
Logger.info("THIS DOES NOT EXECUTE??")
Future{List()}}
}
Now when the above sample code gets executed the first time, session.execute(), gets executed. And then getValueFromCassandraTable is executed. So i thought that the flow of execution is Code snippet above in Models.scala -> session.execute() -> getValueFromCassandraTable()
So, if session.execute fails, i should be able to capture it in try catch exception block. But to my surprise catch block is not executed when session.execute fails. Instead play framework throws an exception. Can someone explain this behavior.
Exception Stack
Caused by: java.lang.RuntimeException: java.lang.ExceptionInInitializerError
at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:498) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.10-2.4.2.jar:2.4.2]
at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at scala.Option.map(Option.scala:145) ~[scala-library-2.10.5.jar:na]
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM$2.apply(Iteratee.scala:741) ~[play-iteratees_2.10-2.4.2.jar:2.4.2]
Caused by: java.lang.ExceptionInInitializerError: null
at models.Model$.getTitles(Model.scala:121) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:48) ~[classes/:na]
at models.Model$.getMatchingTitles(Model.scala:56) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle$1.apply(Application.scala:15) ~[classes/:na]
at controllers.Application$$anonfun$searchTitle$1.apply(Application.scala:15) ~[classes/:na]
at play.api.mvc.ActionBuilder$$anonfun$async$1.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anonfun$async$1.apply(Action.scala:456) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:533) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.Action$.invokeBlock(Action.scala:530) ~[play_2.10-2.4.2.jar:2.4.2]
at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:493) ~[play_2.10-2.4.2.jar:2.4.2]
Caused by: com.datastax.driver.core.exceptions.AuthenticationException: Authentication error on host /10.65.5.44:9042: Username and/or password are incorrect
at com.datastax.driver.core.Connection$8.apply(Connection.java:368) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$8.apply(Connection.java:338) ~[cassandra-driver-core-2.1.6.jar:na]
at com.google.common.util.concurrent.Futures$ChainingListenableFuture.run(Futures.java:861) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.executeListener(ExecutionList.java:156) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:145) ~[guava-16.0.1.jar:na]
at com.google.common.util.concurrent.AbstractFuture.set(AbstractFuture.java:185) ~[guava-16.0.1.jar:na]
at com.datastax.driver.core.Connection$Future.onSet(Connection.java:1170) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:1000) ~[cassandra-driver-core-2.1.6.jar:na]
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:922) ~[cassandra-driver-core-2.1.6.jar:na]
Lin# 121 in Modle.scala is the one which calls CassandrClient.getValueFromCassandraTable. I am mimicking the exception by passing a wrong password connecting to cassandra, hence getting AuthroizationException. I expect this exception to be caught in Try catch. But it is not caught.
(Note that this is has nothing to do with Future, as session.executAysnc is not even called when the exception occurs. So futures have yet not come into play.)
--EDIT--
Looks like Play is swallowing Exception and throwing an Error object. Not sure why is that happening.
Upvotes: 3
Views: 1871
Reputation: 8263
session.execute() could fail with the error like IOError. And you catch only Exception.
Check if you got no error, just trivial exception. Would be nice if you will add stack trace to your question.
You can try to catch any Throwable (do not do it in real project, you can try it only for the debug)
try {
.... your code ...
} catch {
case _ => errorHandler(e)
}
it will not catch error only in the case if it in the Future.
EDIT:
Live example on my cassandra database. It shows a list of users or "something going wrong" string in the case of exception during connection to the cassandra. For example wrong node or cassandra is shutting down.
package controllers
import com.datastax.driver.core.Cluster
import com.datastax.driver.core.Session
import scala.collection.JavaConversions._
import play.api._
import play.api.mvc._
class Application extends Controller {
def index = Action {
try{
var b = new StringBuilder
for (row <- CassandraClient.getValueFromCassandraTable()) {
b ++= row.getString("user_id")
b ++= "\n"
}
Ok(b.toString())
} catch {
case _ => InternalServerError("something going wrong")
}
}
}
object CassandraClient {
private val cluster = Cluster.builder()
.addContactPoint("localhost")
.withPort(9042)
.build()
val session = cluster.connect()
def getValueFromCassandraTable() = {
session.execute("SELECT * FROM mykeyspace.users")
}
}
Upvotes: 1
Reputation: 109
Maybe you have InterruptedException
For example code dont print "InterruptedException test" if you dont use Await.
try {
val exception = Future {
throw new InterruptedException("exception")
}.recover { case e: Exception => "ok" }
exception.onComplete {
case Success(a) => println(a)
case Failure(err) => println(err)
}
//Await.result(exception, 1 seconds)
} catch {
case e: Exception => println("InterruptedException test")
}
Upvotes: 0