All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jove.notebook.services.Kernel.scala Maven / Gradle / Ivy

The newest version!
package jove
package notebook
package services

import jove.MessageSocket.Channel
import jove.notebook.components.{NotebookSession, Session, KernelWS}
import jove.protocol.Output.ConnectReply
import org.http4s.websocket.WebsocketBits._

import scalaz._, Scalaz._
import scalaz.concurrent.Task
import scalaz.stream.{Sink, Process}
import org.http4s.dsl._
import org.http4s.server.websocket._

import argonaut._, Argonaut._

object Kernel extends LazyLogging {
  object ChannelStr {
    def unapply(s: String): Option[Channel] =
      s match {
        case "iopub" =>
          Some(Channel.Publish)
        case "shell" =>
          Some(Channel.Requests)
        case "hb" =>
          Some(Channel.Heartbeat)
        case "stdin" =>
          Some(Channel.Stdin)
        case "control" =>
          Some(Channel.Control)
        case _ =>
          None
      }
  }

  def apply(kernelWs: KernelWS, session: Session): Plan = {
    case req @ (GET -> Root / "api" / "kernels" / kernelId / ChannelStr(channel)) =>

      logger info s"WS at $kernelId, $channel requested (query string: ${req.params})"

      val res =
        for {
          sessionId <- req.params.get("session_id") toRightDisjunction new Exception("No session specified")
          c <- get(kernelId, sessionId, channel, kernelWs, session)
        } yield {

          WS(c._1, c._2)
        }

      res.leftMap{ e =>
        logger error s"Launching kernel: $e (${e.getStackTrace mkString "\n"})"
        throw e
      }.merge
  }

  def get(kernelId: String, sessionId: String, channel: Channel, kernelWs: KernelWS, session: Session): Throwable \/ (Process[Task, WebSocketFrame], Sink[Task, WebSocketFrame]) = {
    logger info s"WS at $kernelId, $channel requested"

    def sinkFor(_process: RawMessage => Unit): Sink[Task, WebSocketFrame] =
      if (channel == Channel.Requests)
        Process.constant {
          case Text(t, _) =>
            Task {
              t.parse match {
                case -\/(err) =>
                  logger warn s"Error while parsing $t on $channel: $err"
                case \/-(msg) =>
                  logger info s"Received msg $msg on $channel"

                  _process(RawMessage(
                    Nil,
                    (msg.hcursor --\ "header").focus.fold("")(_.spaces2),
                    (msg.hcursor --\ "parent_header").focus.fold("")(_.spaces2),
                    (msg.hcursor --\ "metadata").focus.fold("")(_.spaces2),
                    (msg.hcursor --\ "content").focus.fold("")(_.spaces2)
                  ))
              }
            }

          case other =>
            Task {
              logger warn s"Message with unknown type on $channel: $other"
            }
        }
      else
        Process.constant {
          case msg =>
            Task {
              logger warn s"Got unexpected message on channel $channel: $msg"
            }
        }

    session.sessionOrNew(kernelId, sessionId) map {
      case NotebookSession.EmbeddedNotebookSession(_, _, interpreter) =>
        val sink =
          sinkFor(Handler(
            kernelWs.socket(sessionId),
            ConnectReply(-1, -1, -1, -1),
            interpreter
          ))

        (kernelWs.process(sessionId, channel).map{ m =>
          val m0 = m.protocolUp.toJsonStr
          logger info s"Sending $m0 to browser"
          Text(m0)
        }, sink)

      case NotebookSession.RemoteNotebookSession(_, _, remoteSocket) =>
        val sink = sinkFor(remoteSocket.send(channel, _))

        (kernelWs.remoteProcess(remoteSocket, sessionId, channel).map { m =>
          val m0 = m.protocolUp.toJsonStr
          logger info s"Sending $m0 to browser"
          Text(m0)
        }, sink)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy