Play Framework WebSocket ArrayIndexOutOfBounds Error

Viewed 16

I'm having a WebSocket endpoint exposed by the Play Framework like this:

  def socket: WebSocket = WebSocket.acceptOrResult[JsValue, JsValue] { request =>
    Future.successful(
      if (acceptedSubProtocol(request.headers, appConfig.ocppServerCfg.supportedProtocols)) { // TODO: Get the Protocol via AppConfig
        Right(ActorFlow.actorRef { actorRef =>
          Props(new ProvisioningActor(actorRef))
        })
      } else {
        logger.warn(s"Supported Protocol is not one of ${appConfig.ocppServerCfg.supportedProtocols.mkString(",")} " +
          "in the Sec-WebSocket-Protocol header")
        Left(Forbidden)
      }
    )
  }

The following implicit conversion for the incoming Json which looks like this:

implicit val ocppCallRequestReads2: Reads[OCPPCallRequest] = Reads { jsValue =>
    val messageTypeIdE = (jsValue \ 0).toEither
    val messageIdE = (jsValue \ 1).toEither
    val actionNameE = (jsValue \ 2).toEither
    val payloadE = (jsValue \ 3).toEither

    val yielded = for {
      messageTypeId <- messageTypeIdE
      messageId     <- messageIdE
      actionName    <- actionNameE
      payload       <- payloadE
    } yield {
      OCPPCallRequest( // Here I know all 4 exists, so safe to call head
        messageTypeId.head.as[Int],
        messageId.head.as[String],
        actionName.head.as[String],
        payload
      )
    }
    yielded match {
      case Right(ocppCallRequest) => JsSuccess(ocppCallRequest)
      case Left(errors) =>
        println("****************+ ERRORS")
        errors.messages.foreach(println)
        println("****************+ ERRORS")
        JsError(errors)
    }
  }

And the actual JSON:

[
"19223201",
 "BootNotification",
 {
 "reason": "PowerUp",
  "chargingStation": {
    "serialNumber" : "12345",
    "model" : "",
    "vendorName" : "",
    "firmwareVersion" : "",
    "modem": {
      "iccid": "",
      "imsi": ""
    }
  }
 }
]

What I'm trying to do is to validate the incoming JSON and propogate any errors to the client. When I tried to run my example., I'm not able to pass the error back as a JSON response, but the WebSocket endpoint closes with an internal server error as seen below:

[info] a.a.CoordinatedShutdown - Running CoordinatedShutdown with reason [ApplicationStoppedReason]
[info] a.e.s.Slf4jLogger - Slf4jLogger started
[info] play.api.Play - Application started (Dev) (no global state)
****************+ ERRORS
Array index out of bounds in ["19223201","BootNotification",{"reason":"PowerUp","chargingStation":{"serialNumber":"12345","model":"","vendorName":"","firmwareVersion":"","modem":{"iccid":"","imsi":""}}}]
****************+ ERRORS
[error] p.c.s.c.WebSocketFlowHandler - WebSocket flow threw exception
java.lang.ClassCastException: scala.Tuple2 cannot be cast to play.api.libs.json.JsValue
    at akka.stream.impl.fusing.Map$$anon$1.onPush(Ops.scala:52)
    at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:542)
    at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:496)
    at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:390)
    at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:650)
    at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute(ActorGraphInterpreter.scala:61)
    at akka.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute$(ActorGraphInterpreter.scala:57)
    at akka.stream.impl.fusing.ActorGraphInterpreter$BatchingActorInputBoundary$OnNext.execute(ActorGraphInterpreter.scala:104)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:625)
    at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:800)

Any clues as to how I can control the WebSocket endpoint not to crash into an exception? What happens is that the connection gets closed and I do not want that.

0 Answers
Related