Need help with scalang?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

boundary
204 Stars 25 Forks Other 266 Commits 9 Opened issues

Description

Scalang is a scala wrapper that makes it easy to write services that interface with erlang.

Services available

!
?

Need anything else?

Contributors list

h1. Introduction

Scalang is a message passing and actor library that allows Scala and Erlang applications to easily communicate. Scalang includes a full implementation of the Erlang distributed node protocol. It provides an actor oriented API that can be used to interact with Erlang nodes in an idiomatic, OTP compliant way. Scalang is built on Netty for its networking layer and Jetlang for its actor implementation.

h1. Installation

From Maven:


  
    
      Boundary Public Repo
      http://maven.boundary.com/artifactory/repo
    
  

  
    
      com.boundary
      scalang-scala_2.9.1
      0.18
    
  

From SBT:


  val boundaryPublic = "Boundary Public Repo" at "http://maven.boundary.com/artifactory/repo"

  val scalang = "com.boundary" %% "scalang" % "0.18"

Scalang currently supports Scala 2.9.1. Scalang also requires "epmd":http://www.erlang.org/doc/man/epmd.html in order to run.

h1. Usage

h2. Starting

The main entry point for Scalang's API is the node class. An instance of the Node class is a self contained representation of an Erlang Node, distinct in the set of nodes participating in a distributed system. Multiple nodes may be run within the same JVM. Start a node with the following invocation:


    val node = Node("[email protected]", "cookie")

Starting a Scalang node like this will register it with the local "epmd":http://erlang.org/doc/man/epmd.html instance, which must be running on the local host. Scalang will then be available to receive and make connections to other Erlang nodes. In this case, its node name would be [email protected] and its magic cookie is @[email protected] Any Erlang or Scalang node which shares this magic cookie can now connect to this node and send messages using the node name.

h2. Processes

Scalang shares Erlang's concept of a process, a lightweight actor that is capable of sending messages to other processes, either local or remote. You can define your own processes by subclassing the Process class. The following code defines and then spawns a process:


  class MyProcess(ctx : ProcessContext) extends Process(ctx) {
    override def onMessage(msg : Any) {
      log.info("received %s", msg)
    }
  }

  val pid = node.spawn[MyProcess]("my_process")

  //send to the pid
  node.send(pid, "hey there")

  //send to the regname
  node.send("my_process", "you wanna party?")

h2. Message Passing

Message passing / receiving is the main means by which processes may interact with the outside world. Processes receive messages via the message handler method onMessage. Only one message may be handled by a process at a time unless that process has been spawned with the reentrant option.

The following shows a simple echo server and client that demonstrate message sending.


  class EchoServer(ctx : ProcessContext) extends Process(ctx) {
    override def onMessage(msg : Any) = msg match {
      case (pid : Pid, request : Any) =>
        pid ! request
      case m =>
        log.error("sorry I don't understand %s.", m)
    }
  }


  val server = node.spawn[EchoServer]("echo_server")

  val client = node.spawn { mbox =>
    mbox.send(server, (client,'derp))
    val received = mbox.receive
    println("received " + received)
  }

Messages can also easily be passed from a remote node. Scalang supports Erlang's convention of addressing messages to the tuple of a registered name and a node name.


  val remoteNode = Node("remote", cookie)
  val client = node.spawn { mbox =>
    mbox.send(('echo_server, Symbol("[email protected]")), "heyo!")
  }

The above code will send a message to the process registered as "echo_server" on the node named "[email protected]".

h2. Error Handling

Scalang implements the Erlang concept of links. A link is a bidirectional relationship between two processes. If one of the processes exits the link will break and the other process will receive an exit notification. The default behavior of a process during exit notification is for the receiving process to exit with the same error message that was delivered with the link breakage. Creating a link between two processes requires both pids.

Processes that must implement custom behavior may override the trapExit method.


  class ExitHandler(ctx : ProcessContext) extends Process(ctx) {
    override def onMessage(msg : Any) = msg match {
      case _ => log.info("derp %s", msg)
    }

    override def trapExit(from : Pid, msg : Any) {
      log.warning("got exit notification from %s reason %s", from, msg)
    }
  }

h2. Serialization

Scalang messages are serialized into "Erlang's external term format":http://www.erlang.org/doc/apps/erts/erlextdist.html. Serialization automatically happens when messages are either sent from or received by a Scalang process. For the most part Scalang provides a 1-1 mapping of Erlangs terms onto Scala types. The type mappings are illustrated below.

| From Erlang | To Scala | | Small Integer | Int | | Integer | Int | | Float | Double | | Boolean | Boolean | | Atom | Symbol | | Reference | "Reference":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/Reference.scala | | Port | "Port":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/Port.scala | | Pid | "Pid":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/Pid.scala | | Small Tuple | Tuple | | Large Tuple | "BigTuple":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/BigTuple.scala | | String | String | | List | List | | Binary | "ByteBuffer":http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html | | Small Bignum | Long | | Large Bignum | BigInt | | Fun | "Fun":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/Fun.scala | | Bistring | "Bitstring":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/BitString.scala |

| From Scala | To Erlang | | Byte | Small Integer | | Int | Integer | | Long | Small Bignum | | Double | Float | | Symbol | Atom | | Reference | Reference | | Port | Port | | Pid | Pid | | Fun | Fun | | String | String | | List | List | | BigInteger | Large Bignum | | Array[Byte] | Binary | | ByteBuffer | Binary | | BitString | Bitstring | | Tuple | Tuple | | BigTuple | Tuple |

h2. Rich Type Mappings

Sometimes the built-in type mappings in Scalang are not sufficient for an application's message format. Scalang provides the TypeFactory trait for client code to provide custom decoding behavior. A TypeFactory is invoked when Scalang comes across a term that looks like an erlang record: a tuple where the first element is an atom. The createType method is called with the first tuple element as the name and the arity of the tuple.


  object StructFactory extends TypeFactory {
    def createType(name : Symbol, arity : Int, reader : TermReader) : Option[Seq[Any]] = {
      try {
        reader.mark
        (name,arity) match {
          case ('struct,2) => Some(readMap(reader))
          case _ => None
        }
      }
    }

    protected def readSeq(reader : TermReader) : Map[Symbol,Any] = {
      val proplist = reader.readAs[List[Symbol,Any]]
      proplist.toMap
    }
  }

  val node = Node("test", cookie, NodeConfig(
    typeFactory = StructFactory))

The above code will spawn a Scalang node that uses the StructFactory singleton to decode into a map any arity 2 tuples that begin with the atom @[email protected] Anything else will get decoded with the normal type mappings.

A more complex example is the "CaseClassFactory":https://github.com/boundary/scalang/blob/master/src/main/scala/scalang/node/CaseClassFactory.scala. It will attempt to decode Erlang records into Scala "case classes":http://www.scala-lang.org/node/107 reflectively.

h2. Services

Most modern Erlang applications are built using the OTP framework, and in particular the "genserver":http://www.erlang.org/doc/designprinciples/genserverconcepts.html. In order to more effectively interface with genserver based processes, Scalang has a special kind of process known as a service. Services respond to casts and calls like a genserver and allow you to send casts and calls to gen_servers running in an Erlang VM.


  case class EchoServiceArgs(name : String)
  class EchoService(ctx : ServiceContext[EchoServiceArgs]) extends Service(ctx) {
    val EchoServiceArgs(name) = ctx.args

    override def handleCall(tag: (Pid, Reference), request: Any): Any = { 
      name + " " + request
    }   

    override def handleCast(request : Any) {
      log.info("Can't echo a cast. %s", request)
    }   

    override def handleInfo(request : Any) {
      log.info("A wild message appeared. %s, request")
    }   
  }

  val node = Node("test", cookie)
  val pid = node.spawnService[EchoService, EchoServiceArgs]("echo", EchoServiceArgs("test_echo"))

This will spawn a new process with the registered name "echo" and it will be initialized with the argument "test_echo". The handleCall method will automatically send its return value to the caller. Casts are meant to be one directional, therefore the return value of handleCast is discarded. The handleInfo method is invoked when a message shows up without the appropriate call or cast semantics.

h1. Roadmap

h2. Upgrade Node Protocol

Scalang uses the same version of Erlang's node protocol as JInterface. This means that for some operations Scalang nodes are not treated as full fledged members of the cluster. For instance, features like monitors and atom caches are not currently supported. Additionally, Scalang nodes will appear as "hidden nodes":http://www.erlang.org/doc/reference_manual/distributed.html#id82462. This is disadvantageous for Scalang's goal of transparent interoperability.

h2. Supervision Trees

Scalang needs its own implementation of Erlang OTP's supervision tree. All of the primitives are in place to support supervision trees currently, so an implementation of the actual supervisor process is all that's needed.

h2. Pre-emptable Actors

Scalang uses Jetlang's thread pool based actor implementation. There is currently no API for pre-empting these types of Jetlang actors. Therefore either Jetlang needs to be patched to allow this or another actor backend needs to be chosen.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.