Scala 関数型デザイン&プログラミング 6章 Exercise

結構間があいてしまった。

続きを読む

Scala 関数型デザイン&プログラミング 5章 Exercise

半分解いた。半分はまたこんど。

続きを読む

Scala 関数型デザイン&プログラミング 4章 Exercise

Excercise 4.8はあまり自信がない。

続きを読む

Scala 関数型デザイン&プログラミング 3章 Exercise

2問解けなかった。

2015/6/3 Excercise 3.24は解けた

続きを読む

Scala 関数型デザイン&プログラミング 2章 Exercise

社内でFP in Scala勉強会が始まったので、再読がてら解いてみました。

続きを読む

空sbtプロジェクトの作成

giter8での空sbtプロジェクトの作成法をメモ。

$ g8 typesafehub/scala-sbt

ScalatraのAtmosphereはWebSocketで文字列しか送信できない

ScalatraでWebSocketをやるためのサポートとしてはAtmosphereが組み込まれていますが、Scalatra側の問題で文字列しか送信できないようになってしまっているという話。

trait ScalatraBroadcaster extends Broadcaster {

  private[this] val logger: Logger = Logger[ScalatraBroadcaster]
  protected var _resources: ConcurrentLinkedQueue[AtmosphereResource]
  protected var _wireFormat: WireFormat
  protected implicit var _actorSystem: ActorSystem

  def broadcast[T <: OutboundMessage](msg: T, clientFilter: ClientFilter)(implicit executionContext: ExecutionContext): Future[T] = {
    val selectedResources = _resources.asScala filter clientFilter
    logger.trace("Sending %s to %s".format(msg, selectedResources.map(_.uuid)))
    broadcast(_wireFormat.render(msg), selectedResources.toSet.asJava).map(_ => msg)
  }

}
trait WireFormat {
  def name: String
  def supportsAck: Boolean
  def parseInMessage(message: String): InboundMessage
  def parseOutMessage(message: String): OutboundMessage
  def render(message: OutboundMessage): String
}

これがScalatra+Atmosphereでメッセージを送信する場合に最終的に呼ばれるメソッドbroadcastの定義なんですが、継承しているインタフェースのBroadcaster.broadcastの第一引数はObject(Any)なのに対し、WireFormat.renderの型はStringに固定されてしまっています。

しかも、OutboundMessageにBinaryMessageって型があるのに、デフォルト実装では対応なし(空文字の送信になる)。そこはあきらめて、自作WireFormatをAtmosphereSupportを使用しているクラスでimplicit defしてあげましょう。

class BinaryWireFormat extends JacksonSimpleWireformat {
  override def render(message: OutboundMessage) = message match {
    case TextMessage(text) => text
    case JsonMessage(json) => renderJson(json)
    case BinaryMessage(binary) => Base64.getEncoder.encodeToString(binary)
    case _ => ""
  }
}

class MyScalatraServlet extends AtmosphereSupport {
  protected override implicit def wireFormat: WireFormat = new BinaryWireFormat

  atmosphere("/hoge") {
    new AtmosphereClient {
      def receive = {
        case Connected => println("connect")
        case Disconnected(disconnector, Some(error)) => println("disconnect")
        case Error(Some(error)) => println(error)
        case TextMessage(text) => send(new BinaryMessage(/* some Array[Byte] */))
        case JsonMessage(json) => send("not supported")
      }
    }
  }
}

atmosphereのソースも読んでみたが、renderの戻り値型をAnyに変更してArray[Byte]とかを突っ込んだ場合にどんな動きをするかはわからなかった。そこが分かれば、pull requestを出してもいいかもしれない。