Scala: Code interpretation at runtime

With today’s post, we’ll dive into Scala code generation on the fly: at runtime. We have to be cautious of not mixing concepts with Scala macros, which generate code at compile-time. These make use of Scala type system, which is much safer than generating code at runtime.

House-of-cards-but-why

When is it useful to make use of this mechanism then? We’ll try to shed light on this by following a very simple example, getting abstract of real implementation (that you may find at Scalera’s Github).

The problem: “da” serializer

Let’s suppose a not-so-wild case, which consists on communicating two services via an event bus (we’ll get abstract of its implementation: it could be a message queue like Kafka, Akka streams, …).

The main idea is the following:

Sender-receiver-schema

The producer knows how to send and the consumer has an associated callback for message arrivals:

trait Producer{
  def produce(message: Any): Try[Unit]
}
trait Consumer{
  val consume: Any => Unit
}

Both producer and consumer services know the message types that may arrive. In our example, they could be one of these:

case class Foo(att1: Int, att2: String)
case class Bar(att1: String)

If the producer wants to send a message using the event bus, it will have to serialize it somehow (JSON, Byte array, XML, …) so, by the time it reaches the opposite end, the consumer will start the inverse process (deserialization) and will get the original message.

…nothing weird so far.

Whydoeseverythinghavetobesocomplicated

If we have a JSON serializer …

trait JsonSer[T] {
  def serialize(t: T): String
  def deserialize(json: String): T
}

and we serialize our message …

implicit val fooSerializer: JsonSer[Foo] = ???
val foo: Foo = ???
producer.send(implicitly[JsonSer[Foo]].serialize(foo))

How do we know which deserializer to use when the consumer gets the message?

Option 1: Try every possible serializer until one of them works

In our consumer, we’d have:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = {
    case message: String =>
      Seq(barSerializer, fooSerializer).flatMap { ser =>
        Try(ser.deserialize(message)).toOption
      }.headOption.fold(ifEmpty = println("Couldn't deserialize")) {
        case bar: Bar => println("it's a bar!")
        case foo: Foo => println("it's a foo!")
        case _ => println("it's ... something!")
      }
  }
}

A lil’ bit coarse, right? If the proper serializer is the last of a 100 list, we would have tried and failed with 99 serializers before (Such a waste of CPU!).

4d8

Besides, we could also consider the case of having a different type deserializer, but it fits with the received message, so it would partially or wrongly deserialize the message.

Option 2: Add the message type to the message itself

We could add an extra layer wrapping the message for indicating the message type that it contains. This way, when receiving it, we could determine the serializer type we have to use.

MessageWrapper

For writing the type, we’ll make use of Scala’s TypeTags, getting info about the T contained message type.


//Wrapper for the message (and its serializer)

import scala.reflect.runtime.universe.{TypeTag, typeTag}

case class Message[T: TypeTag](content: T){
  val messageType: Message.Type = typeTag[T].tpe.toString
}
object Message {

  type Type = String

  def typeFrom(msg: String): Message.Type = ???

  implicit def messageSer[T:TypeTag:JsonSer]: JsonSer[Message[T]] = ???

}

//We'll make use of it for sending

producer.produce(messageSer[Foo].serialize(Message(foo)))

//And we redefine the consumer

lazy val consumer = new Consumer {
    override val consume: Any => Unit = {
      case message: String =>
        Message.typeFrom(message) match {

          case "org.scalera.reflect.runtime.Bar" =>
            println("it's a bar!")
            val value = messageSer[Bar].deserialize(message).content
            println(value.att1)

          case "org.scalera.reflect.runtime.Foo" =>
            val value = messageSer[Foo].deserialize(message).content
            println("it's a foo!")
            println(value.att2)

          case _ =>
            println("it's ... something!")
        }
    }
  }

As you can see, we don’t have to try every possible serializer anymore. Instead of that, from the message type we’ve extracted from the Message wrapper we have added, we’re able to use the proper deserializer.

But it is also true that we have to add an extra case for each string that represents the message type. Wouldn’t it be nice to deserialize somehow and to have the defined case only for the already deserialized objects? (Something similar what we first tried but without trying all posible serializers). Something like:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = { msg =>
    genericDeserialize(msg) match {
      case f: Foo =>
      case b: Bar =>
      case _ =>
    }
  }
}

Option 2-cool: Serializers ‘under the hood’

For achieving something similar, we have to get focused on that genericDeserialize method: Which signature should it have? Initially, something like this:

def genericDeserialize(msg: String): Any

An Any? Seriously? My fellows, at runtime, we have no idea about the type we can get. We just know that, from a String, we’ll get ‘some….thing’. The match that applies to that Any will allow us to go from something totally abstract to more concrete types.

At this point is where both reflect library and Scala compiler appear.

reflect.Toolbox

The Toolbox API allows parsing strings and getting the resulting AST (abstract syntax tree). From that AST, it is able to evaluate the expression and returns an instance of an Any as well.

For instantiating a Toolbox and use type references, we hace to add as SBT dependencies the following:

libraryDependencies ++= Seq(
  "org.scala-lang" % "scala-compiler" % "2.11.8",
  "org.scala-lang" % "scala-reflect" % "2.11.8")

For example, if we wanted to parse the "2".toInt + 4 operation,

import scala.tools.reflect.ToolBox
import scala.reflect.runtime.{universe => ru}
import ru._

//  Scala compiler tool box
val tb = ru.runtimeMirror(
  this.getClass.getClassLoader).mkToolBox()

println(ru.showRaw(tb.parse("2".toInt + 4")))

we would get the abstract syntax tree generated as a String (by using showRaw):

Apply(Select(Select(Literal(Constant("2")), TermName("toInt")), TermName("$plus")), List(Literal(Constant(4))))

If we use the toolbox for evaluating the parsed expression,

println(tb.eval(tb.parse("2".toInt + 4")))

we’ll get an Any that represents the resulting value of the sum:

6

“Da” serializer

Once we’ve seen how it generally works, we apply the same principle to our serializer, so the expression we’re going to try to interpret is similar to:

{
  import scala.reflect._;
  import spray.json._;
  import org.scalera.reflect.runtime._;
  import MySprayJsonImplicits._;
  import MyJsonSerImplicits._;

  implicitly[JsonSer[Message[$messageType]]]
}

where MySprayJsonImplicits and MyJsonSerImplicits represent the objects that contain both the Spray implicits for JsonFormat and the JsonSer implicits that we have defined before.

$messageType represents the concrete type to deserialize that we would have got by using the TypeTag (as seen before).

If we adapt it to our code, we’ll get something similar to:

object GenSer {

  import scala.tools.reflect.ToolBox
  import scala.reflect.runtime.{universe => ru}
  import ru._

  //  Scala compiler tool box
  private val tb = ru.runtimeMirror(this.getClass.getClassLoader).mkToolBox()

  def genericDeserialize(msg: String)(serContainers: Seq[AnyRef]): Any = {

    val messageType = Message.typeFrom(msg)

    val serContainersImport = serContainers.map(container =>
      "import " + container.toString.split("\\$").head + "._").mkString(";\n")

    val expr =
      s"
         |{
         |  import scala.reflect._;
         |  import spray.json._;
         |  import org.scalera.reflect.runtime._;
         |  $serContainersImport;
         |
         |  implicitly[JsonSer[Message[$messageType]]]
         |}
        ".stripMargin

    tb.eval(tb.parse(expr))
      .asInstanceOf[JsonSer[Message[Any]]]
      .deserialize(msg).content
  }

}

If you take a look, we’ve empowered the generic deserialization method notation to hold a sequence of objects to import, so we won’t make explicit which object contains the Spray implicits and which one contains our JsonSer‘s.

val serContainersImport = serContainers.map(container =>
  "import " + container.toString.split("\\$").head + "._").mkString(";\n")

It is also noteworthy that, when deserializing, we get a Message[Any]; so we’ll have to get the ‘content’ field that represents the raw value of Any type that holds the deserialized message.

The result

So finally, we can now make use of our function for generically deserialize and let our consumer code be ‘swaggy’:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = { 
    case msg: String =>
      genericDeserialize(msg)(Seq(case3,Message)) match {
        case bar: Bar => println("it's a bar!")
        case foo: Foo => println("it's a foo!")
        case _ => println("it's ... something!")
      }
  }
}

Conclusions

Being able to evaluate code at runtime let us do a lot of interesting things when we want to interpret String based types. However, these kind of techniques won’t care about the so worthy and safe Scala type system, so these are tools to be used with a lil’ bit of care.

It is also kind of expensive in time terms to evaluate these expressions. I would recommend to use a type cache in this case. Something really simple like a map:

type TypeName = String
var cache: Map[TypeName, JsonSer[Message[_]]]

And when invoking the method, we’ll check if the type evidence (JsonSer) we’re looking for is already stored in the map. If so, we’ll use it, otherwise, we’ll create and store it in the cache, using it as result.

post-28553-Steve-Jobs-mind-blown-gif-HD-T-pVbd

Easy peasy…
Peace out!

Anuncios

Scala: Interpretación de código en runtime

En el post de hoy vamos a ver cómo generar código de Scala al vuelo: en tiempo de runtime. Hemos de ser cautos de no confundirlo con las macros de Scala, las cuales generan código en tiempo de compilación. Estas últimas hacen uso del sistema de tipos de Scala, lo cual es infinitamente más seguro que generar código en tiempo de ejecución.

House-of-cards-but-why

¿Cúando es útil hacer uso de este mecanismo entonces? Trataremos de arrojar luz siguiendo un ejemplo muy sencillo, abstrayéndonos de la verdadera implementación, la cual podéis encontrar en el Github de Scalera

El problema: “el” serializador

Supongamos un caso (no tan descabellado) en el que queramos comunicar dos servicios vía un bus de eventos (nos abstraeremos de la implementación: podría ser una cola de mensajes tipo Kafka, un stream de Akka, …).

El punto de partida es el siguiente:

Sender-receiver-schema

El productor sabe enviar y el consumidor tiene asociado un callback para cuando llegue un mensaje:

trait Producer{
  def produce(message: Any): Try[Unit]
}
trait Consumer{
  val consume: Any => Unit
}

Tanto el servicio productor como el consumidor conocen los tipos de mensajes que pueden llegar, en nuestro ejemplo serán estos:

case class Foo(att1: Int, att2: String)
case class Bar(att1: String)

Si el servidor quiere enviar un mensaje usando el bus de eventos, deberá serializarlo de alguna forma (JSON, array de bytes, XML, …) para que, al llegar al otro extremo, el consumidor realice el proceso inverso (deserialización) y obtenga el mensaje original.

…hasta aquí nada extraño.

Whydoeseverythinghavetobesocomplicated

Si tenemos un serializador para JSONs …

trait JsonSer[T] {
  def serialize(t: T): String
  def deserialize(json: String): T
}

y serializamos nuestro mensaje…

implicit val fooSerializer: JsonSer[Foo] = ???
val foo: Foo = ???
producer.send(implicitly[JsonSer[Foo]].serialize(foo))

¿Cómo sabemos qué deserializador usar cuando el consumidor reciba el mensaje?

Opción 1: Probar con todos hasta que uno encaje

En nuestro consumidor tendríamos:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = {
    case message: String =>
      Seq(barSerializer, fooSerializer).flatMap { ser =>
        Try(ser.deserialize(message)).toOption
      }.headOption.fold(ifEmpty = println("Couldn't deserialize")) {
        case bar: Bar => println("it's a bar!")
        case foo: Foo => println("it's a foo!")
        case _ => println("it's ... something!")
      }
  }
}

Un poco burdo, ¿no? Si el serializador correcto es el último de 100, estaríamos probando y fallando con 99 antes de dar con el correcto (¡qué desperdicio de CPU!).

4d8

Además, también podría darse el caso de que hubiera un deserializador para otro tipo, pero que encaja y es capaz de deserializar parcial o erróneamente el mensaje.

Opción 2: Añadir el tipo al mensaje

Podríamos añadir una capa por encima al mensaje e indicar el tipo de mensaje que es. Así, al recibirlo, podríamos determinar el tipo de serializador que necesitamos usar.

MessageWrapper

Para escribir el tipo, nos apoyaremos en los TypeTag de Scala, obteniendo información sobre el tipo de mensaje contenido T.


//Wrapper for the message (and its serializer)

import scala.reflect.runtime.universe.{TypeTag, typeTag}

case class Message[T: TypeTag](content: T){
  val messageType: Message.Type = typeTag[T].tpe.toString
}
object Message {

  type Type = String

  def typeFrom(msg: String): Message.Type = ???

  implicit def messageSer[T:TypeTag:JsonSer]: JsonSer[Message[T]] = ???

}

//We'll make use of it for sending

producer.produce(messageSer[Foo].serialize(Message(foo)))

//And we redefine the consumer

lazy val consumer = new Consumer {
    override val consume: Any => Unit = {
      case message: String =>
        Message.typeFrom(message) match {

          case "org.scalera.reflect.runtime.Bar" =>
            println("it's a bar!")
            val value = messageSer[Bar].deserialize(message).content
            println(value.att1)

          case "org.scalera.reflect.runtime.Foo" =>
            val value = messageSer[Foo].deserialize(message).content
            println("it's a foo!")
            println(value.att2)

          case _ =>
            println("it's ... something!")
        }
    }
  }

Cómo podéis ver, ya no tenemos que ir probando con todos los serializadores posibles hasta que demos con uno que funcione; sino que, a partir del tipo de mensaje (extraido del envoltorio de tipo Message que hemos añadido), somos capaces de usar el deserializador adecuado.

Pero también es cierto, que tenemos que añadir un case por cada string que representa el tipo. ¿No sería mejor poder deserializar (como sea) y que el case solo esté definido sobre los objetos ya deserializados (como antes pero sin probar a lo loco con mil serializadores)? Algo de este estilo:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = { msg =>
    genericDeserialize(msg) match {
      case f: Foo =>
      case b: Bar =>
      case _ =>
    }
  }
}

Opción 2-guay: Serializadores ‘under the hood’

Para conseguir algo parecido, debemos de centrarnos en ese método genericDeserialize: ¿qué signatura debería tener? Inicialmente algo del siguiente estilo:

def genericDeserialize(msg: String): Any

¿Un Any? ¿En serio? Amigos, en tiempo de runtime, no tenemos ni idea del tipo que nos puede llegar. Solo sabemos que, a partir de un String, vamos a obtener un ‘algo’. El match que se aplica sobre dicho Any nos permitirá pasar de algo totamente abstracto a tipos más concretos.

Es en este punto, donde entra en juego la librería de reflect y el compilador de scala.

reflect.Toolbox

La api de Toolbox permite parsear cadenas de texto y obtener el AST (abstract syntax tree) resultante. Del mismo modo, a partir del AST es capaz de evaluar la expresión devolviendo una instancia de un tipo determinado.

Para poder instanciar un Toolbox y usar referencias a tipos, es preciso añadir como dependencias a nuestro proyecto las librerías de scala-compiler y scala-reflect:

libraryDependencies ++= Seq(
  "org.scala-lang" % "scala-compiler" % "2.11.8",
  "org.scala-lang" % "scala-reflect" % "2.11.8")

Si por ejemplo quisiéramos parsear la operación "2".toInt + 4,

import scala.tools.reflect.ToolBox
import scala.reflect.runtime.{universe => ru}
import ru._

//  Scala compiler tool box
val tb = ru.runtimeMirror(
  this.getClass.getClassLoader).mkToolBox()

println(ru.showRaw(tb.parse(""""2".toInt + 4""")))

nos devolverá el árbol sintáctico generado como un string (usando showRaw):

Apply(Select(Select(Literal(Constant("2")), TermName("toInt")), TermName("$plus")), List(Literal(Constant(4))))

Si a partir de la expresión parseada, usamos el toolbox para evaluarla,

println(tb.eval(tb.parse(""""2".toInt + 4""")))

obtendremos un Any que representa el valor resultante de la suma:

6

“El” serializador

Una vez visto el funcionamiento general, aplicamos el mismo principio a nuestro serializador, de manera que la expresión que vamos a intentar interpretar es algo similar a:

{
  import scala.reflect._;
  import spray.json._;
  import org.scalera.reflect.runtime._;
  import MySprayJsonImplicits._;
  import MyJsonSerImplicits._;

  implicitly[JsonSer[Message[$messageType]]]
}

donde MySprayJsonImplicits y MyJsonSerImplicits representan los objetos que contienen tanto los implícitos de Spray para JsonFormat que hemos definido, como los implícitos de la type class JsonSer que hemos definido nosotros.

$messageType representa el tipo concreto a deserializar que habremos obtenido previamente usando el TypeTag (como hemos visto antes).

Si lo adaptamos a nuestro código obtendremos algo similar a:

object GenSer {

  import scala.tools.reflect.ToolBox
  import scala.reflect.runtime.{universe => ru}
  import ru._

  //  Scala compiler tool box
  private val tb = ru.runtimeMirror(this.getClass.getClassLoader).mkToolBox()

  def genericDeserialize(msg: String)(serContainers: Seq[AnyRef]): Any = {

    val messageType = Message.typeFrom(msg)

    val serContainersImport = serContainers.map(container =>
      "import " + container.toString.split("\\$").head + "._").mkString(";\n")

    val expr =
      s"""
         |{
         |  import scala.reflect._;
         |  import spray.json._;
         |  import org.scalera.reflect.runtime._;
         |  $serContainersImport;
         |
         |  implicitly[JsonSer[Message[$messageType]]]
         |}
        """.stripMargin

    tb.eval(tb.parse(expr))
      .asInstanceOf[JsonSer[Message[Any]]]
      .deserialize(msg).content
  }

}

Si os fijáis, hemos permitido en la notación del método de deserialización genérica, que se le pase una secuencia de objetos a importar, de manera que no se explicita qué objeto es el que contiene los implícitos de Spray y qué otro contiene los de nuestro JsonSer.

val serContainersImport = serContainers.map(container =>
  "import " + container.toString.split("\\$").head + "._").mkString(";\n")

También cabe destacar que, al deserializar, obtenemos un Message[Any]; por lo que posteriormente hay que obtener el campo ‘content’ que representa el valor plano de tipo Any que representa el mensaje deserializado.

El resultado

Ahora sí, podemos usar nuestra función para deserializar de manera genérica y dejar el código de nuestro consumidor de manera ‘reshulona’:

lazy val consumer = new Consumer {
  override val consume: Any => Unit = { 
    case msg: String =>
      genericDeserialize(msg)(Seq(case3,Message)) match {
        case bar: Bar => println("it's a bar!")
        case foo: Foo => println("it's a foo!")
        case _ => println("it's ... something!")
      }
  }
}

Conclusiones

Poder interpretar código en tiempo de runtime da bastante juego cuando queremos interpretar tipos en base a String’s. Ahora bien, interpretar código en runtime se pasa por el forro el sistema de tipos que tanta seguridad aporta en Scala, por lo que son herramientas a usar con algo de cuidado.

También es algo costoso en tiempo la evaluación de estas expresiones. Os recomendaría que usarais una cache de tipos. Algo tan sencillo como un mapa:

type TypeName = String
var cache: Map[TypeName, JsonSer[Message[_]]]

Y al ejecutar el método, comprobar si existe ya un JsonSer almacenado en el mapa. De ser así, usamos ese y en caso contrario, lo creamos, lo almacenamos en la caché y lo devolvemos como resultado.

post-28553-Steve-Jobs-mind-blown-gif-HD-T-pVbd

Easy peasy…
¡Agur de limón!

Graffiti Rules: playing with JSON [Snow]

A few months ago we talked about Spray, a toolkit that allowed us to build REST APIs in an easy way with a pretty complete DSL.

One of the components belonging to this toolkit is spray-json. This module allows us to serialize and deserialize our objects to/from a JSON format. Today we’ll see how to work with it quickly and easily.

How to create serializers to different classes? Well, there are two options depending on how the class we want to serialize is defined.

Easy option: when we have a case class

Well, that’s a piece of cake. The only thing we need to use is the jsonFormatN() method where N represents the number of arguments of the case class apply method. Let’s see it with an example:

case class Character(name: String, family: String, isDead: Boolean)

object MyJsonProtocol extends DefaultJsonProtocol {

 implicit val characterFormat = jsonFormat3(Character.apply)

}

As you can see, in order to create a serializer for the Character case class, we create an implicit value with the help of jsonFormat3 (as it has 3 attributes). Such an implicit object will be defined inside a trait which will extend from DefaultJsonProtocol. In this trait, the serializers for Scala basic types (Int, String, Boolean…) are defined… Easy peasy.

tumblr_mfuulmem1p1qem4feo1_250

Less easy option: when we don’t have a case class or we want to serialize a case class in a different way.

In such a case, we’ll need to implement how the serialization and deserialization of the given type should be. To do so, we may need to create an implicit object extending from RootJsonFormat[T] where T is the type we want to serialize. Such a trait contains two methods that need to be implemented. On the one hand, there is the write method, which converts a typeT in a Json, and on the other a method read, performing the reverse process. We’ll now see an example with the same type as before:

class Character(val name: String, val family: String, val isDead: Boolean)

object MyJsonProtocol extends DefaultJsonProtocol {

  implicit object CharacterJsonFormat extends RootJsonFormat[Character] {

    def write(c: Character) =
      JsArray(JsString(c.name), JsString(c.family), JsBoolean(c.isDead))

    def read(value: JsValue) = value match {
      case JsArray(Vector(JsString(name), JsString(family), JsBoolean(isDead))) =>
        new Character(name, family, isDead.toBoolean)
      case _ => throw new DeserializationException("Character expected")
    }
  }
}

As can be observed, it is a bit more tedious but not too complicated.

And how do we use it? We just have to import the object where we have defined the implicits and call the methods toJson or convertTo[T].

import MyJsonProtocol._

val json = Character("Jon Snow", "Stark", ???).toJson //....You know nothing!!!!
// Returns {"name": "Jon Snow", "family": "Stark", "isDead": ???}
val jonSnow = json.convertTo[Character]

giphy

Besides, if we use Spray’s REST API, the transformation will be done in a transparent way and it won’t be necessary to call  toJson or convertTo explicitely. But we’ll leave that to other post. Next week we’ll be talking about this library so we’d better see something of it before 🙂

See you all!

Grafitti Rules: jugando con JSON [Snow]

Hace ya unos meses hablamos de Spray, un toolkit que nos permitía construir API’s REST de una forma sencilla con un DSL bastante completo.

Uno de los componentes que forman el toolkit es spray-json. Con este modulo podemos serializar y deserializar nuestros objetos a un formato JSON. Hoy vamos a ver como trabajar con él de forma muy rápida y sencilla.

¿Cómo crear los serializadores de las distintas clases? Pues existen dos opciones en función de cómo esté definida la clase que queramos serializar.

Opción fácil: cuando tenemos una case class

Esto está tirado. Lo único que tenemos que utilizar es el método jsonFormatN() donde N representa el número de argumentos que tiene el método apply de la case class. Veamos un ejemplo:

case class Character(name: String, family: String, isDead: Boolean)

object MyJsonProtocol extends DefaultJsonProtocol {

 implicit val characterFormat = jsonFormat3(Character.apply)

}

Como se puede ver, para crear un serializador para la case class Character creamos un valor implícito con la ayuda de jsonFormat3 (ya que tiene 3 atributos). Dicho objeto implícito lo definiremos dentro de un trait que extenderá de DefaultJsonProtocol. En este trait están definidos los serializadores para los tipos básicos de Scala: Int, String, Boolean … Fácil y sencillo.

tumblr_mfuulmem1p1qem4feo1_250

Opción menos fácil: cuando no tenemos una case class o queremos serializar una case class de una forma distinta.

En este caso, es necesario picarnos a mano como debe ser la serialización y la deserialización del tipo en cuestión. Para ello, es necesario crear un implicit object que extienda de RootJsonFormat[T] donde T es el tipo que se quiere serializar. Dicho trait contiene dos métodos a implementar. Por un lado existe un método write, que convierte un tipo T en un Json, y por otro lado un tipo read, que realiza el proceso inverso. A continuación vemos un ejemplo con el mismo tipo que antes:

class Character(val name: String, val family: String, val isDead: Boolean)

object MyJsonProtocol extends DefaultJsonProtocol {

  implicit object CharacterJsonFormat extends RootJsonFormat[Character] {

    def write(c: Character) =
      JsArray(JsString(c.name), JsString(c.family), JsBoolean(c.isDead))

    def read(value: JsValue) = value match {
      case JsArray(Vector(JsString(name), JsString(family), JsBoolean(isDead))) =>
        new Character(name, family, isDead.toBoolean)
      case _ => throw new DeserializationException("Character expected")
    }
  }
}

Como se puede observar, es un poco más tedioso pero no demasiado complicado.

¿Y cómo lo usamos? Basta con importar el objeto donde hemos definido los implícitos y realizar llamadas a los métodos toJson o convertTo[T].

import MyJsonProtocol._

val json = Character("Jon Snow", "Stark", ???).toJson //....You know nothing!!!!
// Returns {"name": "Jon Snow", "family": "Stark", "isDead": ???}
val jonSnow = json.convertTo[Character]

giphy

Además, si usamos la API REST de Spray, la transformación se hará de forma transparente y no será necesario llamar a toJson o convertTo de forma explícita. Pero esto para otro post. La semana que viene, se hará una pequeña mención a esta librería, por lo que no venía mal dar unas pequeñas pinceladas antes 🙂
Agur de limón!

Graffiti Rules: Hello World in Spray

Today we are going to get started with a tool to develop REST API: Spray. In order to create those APIs, Spray defines a DSL (Domain Specific Language) to define the routes and responses to the HTTP requests we receive. There are other options to create this type of APIs such as scalatra, Play, Lift… However, Spray serves us well as we will later speak about its successor: Akka-HTTP. But not so fast, let’s go bit by bit.

computers20graffiti20error20technology20explorer201440x90020wallpaper_www-wallpaperfo-com_98

The aim of this post is to make an introductory incursion. We can count ourselves lucky if we come to implement a Hello World with Spray by the end of it. To do that, it is required to know which are the modules with which the framework is built. These are some of main ones:
  • spray-can: HTTP server that will allow us to run our API.
  • spray-routing: DSL to describe the routes used to define the desired behaviour. Unlike other frameworks (such as Play), the routes are defined by code, instead of using a text file or a configuration file.
  • spray-http: module in charge of modelling and managing the HTTP protocol.
  • spray-json: module used for the serialization and deserialization of messages in JSON format. It can be replaced with other JSON serialization modules such as play-json, jackson…
  • spray-testkit: small DSL to test the endpoints in a really simple way.

This first example, due to its simplicity, won’t need all of them. We’ll be using the rest of them further on. So, the first thing we need to do is to build our SBT file with the needed dependencies. In our case, we will only be using spray-can,spray-routing and akka. Thus, we add those dependencies to the build.sbt file:

val akkaV = "2.3.9"
val sprayV = "1.3.3"

libraryDependencies ++= Seq(
    "io.spray" %% "spray-can" % sprayV,
    "io.spray" %% "spray-routing" % sprayV,
    "com.typesafe.akka" %% "akka-actor" % akkaV
  )

Once the configuration is ready, we shall start writing the Scala code. It is important to know that Spray is built on top of Akka. As we’ll see, much of its functionality is based on its use of actors.

First, we define our route. In such a route we will simply mark that we a GET is received, a Hello World will be sent as response:

import spray.routing._
trait ApiRoute extends HttpService {

 val myApiRoute =
   get {
     complete("Hello World!")
   }
}

As can be appreciated, a mixin with HttpService is required. Furthermore, if a response needs to be returned, it is also mandatory for it to be encapsulated in a complete method. The part of spray-routing that we’re using here is the get directive, which defines the behaviour towards GET requests (quite logical, as it should).

Now, we have to define an actor that uses that route. This is really simple as it is completely based in Akka. The only rare (or new) thing is that we’ll have to define its behaviour with a method called runRoute, which requires a route. And which route is this? Well, naturally, it’ll be the one we’ve just defined in the ApiRoute trait:

import akka.actor.Actor
import spray.routing._

class ApiActor extends Actor with ApiRoute {

  def actorRefFactory = context

  def receive = runRoute(myApiRoute)
}

Now, all that remains is to run the API and bind it to a port. For this, the first thing we need to do is to create an ActorSystem. Once it is created, we create an actor of the type ApiActor (the actor we’ve just defined in the previous paragraph). Finally, we send a Bind message to it with the IP and port to which it should be binded:

import akka.actor.{ ActorSystem, Props }
import akka.io.IO
import akka.util.Timeout
import akka.pattern.ask

import spray.can.Http

import scala.concurrent.duration._

object Main extends App {

  implicit val system = ActorSystem("My-System")
  val apiActor = system.actorOf(Props[ApiActor])

  implicit val timeout = Timeout(3 seconds)

  IO(Http) ? Http.Bind(apiActor, interface = "localhost", port = 8080)

}

And this is it. Now, we just have to execute the application, open our favourite browser and go to localhost:8080 to see a wonderful yet surprising ‘HELLO WORLD!’ 🙂

tumblr_mamiy1kp641qcp26yo1_400

Graffiti Rules: Hello Word con Spray

Hoy toca empezar con una herramienta para realizar API REST: Spray. Para construir dichas API’s, Spray define un DSL (Lenguaje Específico de Dominio) con el que definir cuales son las rutas y las reacciones a las llamadas HTTP que recibamos. Existen otras alternativas para realizar este tipo de API’s: scalatra, Play, Lift … Sin embargo, este viene perfecto para luego hablar de su evolución: Akka-HTTP. Pero no vayamos tan rápido, vamos poco a poco.

computers20graffiti20error20technology20explorer201440x90020wallpaper_www-wallpaperfo-com_98

El objetivo de este POST es hacer una incursión de iniciación. Nos podemos ir contentos si conseguimos realizar un Hello World con Spray. Para ello, lo principal es conocer cuales son los módulos en los que se compone el framework. Aquí van algunos de los principales:

  • spray-can: servidor HTTP que nos permitirá levantar nuestra API.
  • spray-routing: DSL de definición de rutas para definir el comportamiento deseado. A diferencia de otros frameworks (como Play), las rutas están definidas en código en vez de en un fichero de texto o de configuración.
  • spray-http: módulo que se encarga de modelar y manejar el protocolo HTTP.
  • spray-json: módulo para serialización y deserialización de mensajes en formato JSON. Es reemplazable por otros módulos de serialización a JSON como play-json, jackson…
  • spray-testkit: pequeño DSL para realizar testing de los endpoints de una manera muy sencilla.

En este primer ejemplo, por su sencillez, no necesitaremos todos ellos. Ya iremos utilizando otros módulos más adelante. Por ello, lo primero que tenemos que hacer es construir nuestro fichero SBT con las dependencias necesarias. En nuestro caso nos basta con spray-can, spray-routing y akka. Por tanto, añadimos dichas dependencias al fichero build.sbt:

val akkaV = "2.3.9"
val sprayV = "1.3.3"

libraryDependencies ++= Seq(
    "io.spray" %% "spray-can" % sprayV,
    "io.spray" %% "spray-routing" % sprayV,
    "com.typesafe.akka" %% "akka-actor" % akkaV
  )

Una vez que tenemos la configuración lista, toca ponernos a escribir el código Scala. Es importante saber que Spray está construido sobre Akka. Como vamos a ver, mucha de la funcionalidad se basa en utilizar actores.

En primer lugar vamos a definir nuestra ruta. En dicha ruta simplemente marcaremos que si recibimos un GET responderemos con un Hello World:

import spray.routing._
trait ApiRoute extends HttpService {

 val myApiRoute =
   get {
     complete("Hello World!")
   }
}

Como se puede ver, es necesario hacer mixin de HttpService. Además, siempre que queramos devolver alguna respuesta será necesario encapsularlo en un método complete. La parte de spray-routing que utilizamos es la directiva get, que define el comportamiento para las peticiones de tipo GET (muy lógico todo).

Ahora, tenemos que definir un actor que utilice esta ruta. Esto es muy sencillo ya que se basa totalmente en Akka. Lo único raro (o nuevo) es que tendremos que definir su comportamiento basándonos en un método llamado runRoute que define una ruta. ¿Y cuál será dicha ruta? Pues, por supuesto, será la que acabamos de definir en el trait ApiRoute:

import akka.actor.Actor
import spray.routing._

class ApiActor extends Actor with ApiRoute {

  def actorRefFactory = context

  def receive = runRoute(myApiRoute)
}

Ahora queda lanzar la API y atarla a un puerto. Lo primero que necesitaremos realizar es crear un ActorSystem. Una vez creado el ActorSystem, creamos un actor de tipo ApiActor (el actor que acabamos definir en el paso anterior). Finalmente le enviamos un mensaje de tipo Bind indicando la IP y el puerto donde se debe conectar:

import akka.actor.{ ActorSystem, Props }
import akka.io.IO
import akka.util.Timeout
import akka.pattern.ask

import spray.can.Http

import scala.concurrent.duration._

object Main extends App {

  implicit val system = ActorSystem("My-System")
  val apiActor = system.actorOf(Props[ApiActor])

  implicit val timeout = Timeout(3 seconds)

  IO(Http) ? Http.Bind(apiActor, interface = "localhost", port = 8080)

}

Y ya está. Ahora solo queda arrancar la aplicación, abrir nuestro navegador favorito e ir a localhost:8080 para poder ver un maravilloso a la par que sorprendente “HELLO WORLD!” 🙂

tumblr_mamiy1kp641qcp26yo1_400