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

com.twitter.finagle.memcached.protocol.text.Show.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.finagle.memcached.protocol.text

import com.twitter.finagle.memcached.protocol._
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder
import org.jboss.netty.buffer.{ChannelBuffer, ChannelBuffers}
import com.twitter.finagle.memcached.util.ChannelBufferUtils._
import org.jboss.netty.channel._

class ResponseToEncoding extends OneToOneEncoder {
  private[this] val ZERO          = "0"
  private[this] val VALUE         = "VALUE"

  private[this] val STORED        = "STORED"
  private[this] val NOT_STORED    = "NOT_STORED"
  private[this] val EXISTS        = "EXISTS"
  private[this] val NOT_FOUND     = "NOT_FOUND"
  private[this] val DELETED       = "DELETED"

  def encode(ctx: ChannelHandlerContext, ch: Channel, message: AnyRef): Decoding = message match {
    case Stored()       => Tokens(Seq(STORED))
    case NotStored()    => Tokens(Seq(NOT_STORED))
    case Exists()       => Tokens(Seq(EXISTS))
    case Deleted()      => Tokens(Seq(DELETED))
    case NotFound()     => Tokens(Seq(NOT_FOUND))
    case NoOp()         => Tokens(Nil)
    case Number(value)  => Tokens(Seq(value.toString))
    case Error(cause)   =>
      val formatted = ExceptionHandler.format(cause)
      Tokens(formatted.map { ChannelBuffers.copiedBuffer(_) })
    case InfoLines(lines) =>
      val buffer = ChannelBuffers.dynamicBuffer(100 * lines.size)
      val statLines = lines map { line =>
        val key = line.key
        val values = line.values
        Tokens(Seq(key) ++ values)
      }
      StatLines(statLines)
    case Values(values) =>
      val buffer = ChannelBuffers.dynamicBuffer(100 * values.size)
      val tokensWithData = values map {
        case Value(key, value, casUnique, Some(flags)) =>
          TokensWithData(Seq(VALUE, key, flags), value, casUnique)
        case Value(key, value, casUnique, None) =>
          TokensWithData(Seq(VALUE, key, ZERO), value, casUnique)
      }
      ValueLines(tokensWithData)
  }
}

class CommandToEncoding extends OneToOneEncoder {
  private[this] val GET           = "get"
  private[this] val GETS          = "gets"
  private[this] val DELETE        = "delete"
  private[this] val INCR          = "incr"
  private[this] val DECR          = "decr"

  private[this] val ADD           = "add"
  private[this] val SET           = "set"
  private[this] val APPEND        = "append"
  private[this] val PREPEND       = "prepend"
  private[this] val REPLACE       = "replace"
  private[this] val CAS           = "cas"

  private[this] val GETV          = "getv"
  private[this] val UPSERT        = "upsert"

  private[this] val QUIT          = "quit"
  private[this] val STATS         = "stats"

  def encode(ctx: ChannelHandlerContext, ch: Channel, message: AnyRef): Decoding = message match {
    case Add(key, flags, expiry, value) =>
      TokensWithData(Seq(ADD, key, flags.toString, expiry.inSeconds.toString), value)
    case Set(key, flags, expiry, value) =>
      TokensWithData(Seq(SET, key, flags.toString, expiry.inSeconds.toString), value)
    case Replace(key, flags, expiry, value) =>
      TokensWithData(Seq(REPLACE, key, flags.toString, expiry.inSeconds.toString), value)
    case Append(key, flags, expiry, value) =>
      TokensWithData(Seq(APPEND, key, flags.toString, expiry.inSeconds.toString), value)
    case Prepend(key, flags, expiry, value) =>
      TokensWithData(Seq(PREPEND, key, flags.toString, expiry.inSeconds.toString), value)
    case Cas(key, flags, expiry, value, casUnique) =>
      TokensWithData(Seq(CAS, key, flags.toString, expiry.inSeconds.toString), value, Some(casUnique))
    case Get(keys) =>
      Tokens(Seq[ChannelBuffer](GET) ++ keys)
    case Gets(keys) =>
      Tokens(Seq[ChannelBuffer](GETS) ++ keys)
    case Getv(keys) =>
      Tokens(Seq[ChannelBuffer](GETV) ++ keys)
    case Upsert(key, flags, expiry, value, version) =>
      TokensWithData(Seq(UPSERT, key, flags.toString, expiry.inSeconds.toString), value, Some(version))
    case Incr(key, amount) =>
      Tokens(Seq(INCR, key, amount.toString))
    case Decr(key, amount) =>
      Tokens(Seq(DECR, key, amount.toString))
    case Delete(key) =>
      Tokens(Seq(DELETE, key))
    case Stats(args) => Tokens(Seq[ChannelBuffer](STATS) ++ args)
    case Quit() =>
      Tokens(Seq(QUIT))
  }
}

class ExceptionHandler extends SimpleChannelUpstreamHandler {
  override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) = {
    val formatted = ExceptionHandler.formatWithEol(e.getCause)
    Channels.write(ctx.getChannel, ChannelBuffers.copiedBuffer(formatted:_*))
  }
}

object ExceptionHandler {
  private val DELIMITER     = "\r\n".getBytes
  private val ERROR         = "ERROR".getBytes
  private val CLIENT_ERROR  = "CLIENT_ERROR".getBytes
  private val SERVER_ERROR  = "SERVER_ERROR".getBytes
  private val SPACE         = " ".getBytes
  private val Newlines      = "[\\r\\n]".r

  def formatWithEol(e: Throwable) = format(e) match {
    case head :: Nil => Seq(head, DELIMITER)
    case head :: tail :: Nil => Seq(head, SPACE, tail, DELIMITER)
    case _ => throw e
  }

  def format(e: Throwable) = e match {
    case e: NonexistentCommand =>
      Seq(ERROR)
    case e: ClientError        =>
      Seq(CLIENT_ERROR, Newlines.replaceAllIn(e.getMessage, " ").getBytes)
    case e: ServerError        =>
      Seq(SERVER_ERROR, Newlines.replaceAllIn(e.getMessage, " ").getBytes)
    case t                     =>
      throw t
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy