2015-09-11 52 views
2

我在Scala/Play中使用Cassandra作为数据库的Web应用程序。我在测试潜在的错误时遇到以下问题。了解play/scala错误处理

下面是我的项目

  1. Application.scala的组件 - >基本控制器

  2. Model.scala - >承装 业务逻辑

  3. CassandraClient.scala - >逻辑连接到 cassandra并运行查询cassandra

CassandraClient.scala看起来像下面

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) 
} 

卡桑德拉连接时,我打电话getValueFromCassandraTable在第一时间首先建立。由于CassandraClient是一个对象,连接到Cassandra的逻辑只被调用一次。

Model.Scala有一些代码可以处理将来由session.execute返回的代码。

例如:请看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()}} 
    } 

现在当上面的示例代码第一次执行时,会执行session.execute()。然后执行getValueFromCassandraTable。 所以我想执行的流程是 代码段以上Models.scala - > session.execute() - > getValueFromCassandraTable()

所以,如果session.execute失败,我应该能够捕捉到它的尝试捕获异常块。但令我惊讶的是,当session.execute失败时catch块没有执行。相反,玩框架会抛出异常。有人可以解释这种行为。

异常堆栈

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] 

林#121 Modle.scala是调用CassandrClient.getValueFromCassandraTable之一。 我通过传递连接到cassandra的错误密码来模仿异常,因此获得AuthroizationException。我希望这个异常能够在Try catch中被捕获。但它没有被捕获。

(注意,这是没有任何关系与未来,当发生异常session.executAysnc甚至没有叫。所以期货尚未开始发挥作用。)

- 编辑 -

看起来像播放吞咽异常并抛出一个错误对象。不知道为什么会这样。

+0

这个问题似乎纯粹是关于对Scala期货的理解。 –

+0

好吧,我不确定。 session.execute()引发异常,这不是异步调用。我不明白的是为什么我的try catch块没有捕获session.execute抛出的异常。 – konquestor

+0

如果使用'recover {case _ => Future(List())}',它会被抓到吗? –

回答

0

也许你有InterruptedException

例如代码不打印“InterruptedException的测试”,如果你不使用的等待。

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") 
} 
1

会话。execute()可能会因IOError等错误而失败。你只捕获异常。

Exception hierarchy

检查,如果你有没有错误,只是微不足道的例外。如果您将堆栈跟踪添加到您的问题将会很好。

你可以试着捕捉任何的Throwable(不这样做,在实际项目中,你可以尝试只为调试)

try { 
    .... your code ... 
} catch { 
    case _ => errorHandler(e) 
} 

这不仅会在未来,如果它的情况下赶上错误。

编辑:

我卡桑德拉数据库活生生的例子。 它显示连接到cassandra时发生异常的用户列表或“出错的字符串”。例如错误的节点或cassandra正在关闭。

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") 
     } 
    } 
+0

是的,你是对的。播放抛出ExceptionInInitializerError,然后由try catch块捕获。但是,我很困惑这样的行为。我的代码抛出DriverException类型的AuthenticationException。玩似乎赶上抛出ExceptionInInitializerError。有关最新情况的任何解释?我发布了堆栈轨道。 – konquestor

+0

这个字符串“private val session = cluster.connect()”不在你的try-catch块中。当您调用“cluster.connect()”时会引发AuthenticationException。它不是由你的代码处理的,所以请在“ExceptionInInitializerError”中打包。我想你在游戏开始时直接运行这些代码。 –

+0

嗯,cluster.connect()是在对象CassandraClient的主体中定义的。所以我相信只有在第一次引用CassandraClient时才会调用它。由于这是一款游戏应用程序。当我为我的服务创建第一个httpcall时,它只会被调用。 – konquestor