2016-12-11 8 views
1

Надеюсь, вы поможете мне разобраться в этом, пожалуйста. Используя Akka HTTP, Slick и PosgreSQL, я пытаюсь внедрить доску объявлений в качестве упражнения. Вы можете найти последнюю версию в here for a full picture.Переполнение стека для DateTime с использованием спрей-json, Slick и PostgreSQL


Что у меня есть, короче говоря:

У меня есть таблицы, подобные этим:

final class Posts(tag: Tag) extends Table[Post](tag, "POSTS") with CustomColumnTypes { 
    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) 
    def threadId = column[Long]("THREAD_ID") 
    def secretId = column[String]("SECRET") 
    def pseudonym = column[String]("PSEUDONYM") 
    def email = column[String]("EMAIL") 
    def content = column[String]("CONTENT") 
    def created = column[DateTime]("CREATED") 

final class Threads(tag: Tag) extends Table[Thread](tag, "THREADS") { 
    def threadId = column[Long]("THREAD_ID", O.PrimaryKey, O.AutoInc) 
    def subject = column[String]("SUBJECT") 

и домен модели:

case class Post(
    postId: Option[Long] = None, 
    threadId: Option[Long], 
    secretId: String, 
    pseudonym: String, 
    email: String, 
    content: String, 
    created: DateTime = DateTime.now) 

case class Thread(
    threadId: Option[Long] = None, 
    subject: String 

case class NewThreadWithPost(
    postId: Option[Long] = None, 
    subject: String, 
    secretId: String, 
    pseudonym: String, 
    email: String, 
    content: String) 

с пользовательским протоколом выписанные например:

trait TextboardJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport with DateTimeHelper { 
    implicit object DateTimeFormat extends RootJsonFormat[DateTime] { 
    def read(value: JsValue) = value match { 
     case dt: JsValue => value.convertTo[DateTime] 
     case _ => deserializationError("DateTime expected") 
    } 
    def write(c: DateTime) = JsString(c.toString) 
    } 

    implicit val threadFormat = jsonFormat2(Thread.apply) 
    implicit val postFormat = jsonFormat7(Post.apply) 
    implicit val newThread = jsonFormat6(NewThread.apply) 
} 

и маршруты, как это:

def route: Route = { 
... 
      post { 
      entity(as[Post]) { post => 
       complete(createPost(Some(threadId), post).toJson) } 
... 
      post { 
      entity(as[NewThread]) { thread => 
       (master ? CreateNewThread(thread)).mapTo[NewThread] 
       complete(StatusCodes.Created) } 
... 

То, что я намерен делать, и где он не:

Я намерен добавить новую тему и новую запись с помощью этого метода , и он отлично работает:

def createNewThread(nt: NewThread) = { 
    exec(threads += Thread(None, nt.subject)) 
    exec(posts += Post(None, lastId, secretId, nt.pseudonym, nt.email, nt.content, DateTime.now)) 
    } 

Wha т не работает на все это, казалось бы, так метод я использую для создания сообщения:

def createPost(threadId: Option[Long], p: Post) = { 
    exec(posts returning posts.map(_.secretId) 
     += Post(None, p.threadId, secretId, p.pseudonym, p.email, p.content, DateTime.now)) 
    } 

Что происходит?

[error] Uncaught error from thread [default-akka.actor.default-dispatcher-7] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[default] 
[error] java.lang.StackOverflowError 
[error] at main.scala.textboard.TextboardJsonProtocol$DateTimeFromat$.read(Protocol.scala:22) 
[info] [ERROR] [SECURITY][12/11/2016 23:03:22.301] [default-akka.actor.default-dispatcher-7] [akka.actor.ActorSystemImpl 
(default)] Uncaught error from thread [default-akka.actor.default-dispatcher-7] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled [error] at spray.json.JsValue.convertTo(JsValue.scala:31) <- repeated tens of times 
[error] at main.scala.textboard.TextboardJsonProtocol$DateTimeFormat$.read(Protocol.scala:23) <- repeated tens of times 
[error] at main.scala.textboard.TextboardJsonProtocol$DateTimeFormat$.read(Protocol.scala:21) <- repeated tens of times 
Java.lang.RuntimeException: Nonzero exit code returned from runner: -1 at scala.sys.package$.error(package.scala:27) 
[trace] Stack trace suppressed: run last compile:run for the full output. 
[error] (compile:run) Nonzero exit code returned from runner: -1 
[error] Total time: 18 s, completed 2016-12-11 23:19:16 

То, что я уже пытался

  • Пытались изолировать причину, по которой это не происходит, когда я использую createNewThread().
  • Выполнено с RootJsonFormat [DateTime], чтобы исправить неявный читатель.
  • Выполнено с использованием DSL-маршрута, позволяющего использовать или избегать использования актеров.
  • Пытался избежать передачи нити в качестве опции.

ответ

4

Вы получили бесконечный цикл в DateTimeFormat. Вы звоните convertTo в пределах read, а convertTo звонит read (см. spray-json source).

использовать что-то вроде

implicit object DateJsonFormat extends RootJsonFormat[DateTime] { 

    private val parser : DateTimeFormatter = ??? // your parser here 

    override def write(obj: DateTime) = JsString(parser.print(obj)) 

    override def read(json: JsValue) : DateTime = json match { 
     case JsString(s) => parser.parseDateTime(s) 
     case _ => throw new Exception("Malformed datetime") 
    } 
    }