Akka, Spray and Spring Framework

Posted by Jan Machacek

Find me on:

28-Apr-2012 22:41:00

You might be thinking, why do such a thing? Spray [2] will take care of the application's API and Akka provides the actors that represent the functionality of the system. Why then use the Spring Framework? We will not be doing any serious dependency injection, nor will we use Spring MVC (because we have Spray), nor Spring Integration (because we have Akka). However, you may find that some components of the Spring Portfolio are still very useful; and in this post, we will take a look at how to construct an application that uses Spring Data to persist our objects in MongoDB.


The code

Our application starts in the Boot class, which starts the hierarchy of actors. Let's have two main hierarchies, with different supervisor strategies. The infrastructure hierarchy defines actors that deal with the core services that the rest of the application needs; the app hierarchy defines actors that implement the features of our system. Figure 1 shows the hierarchy of actors:

Figure 1. Hierarchy of actors

actors

The figure shows the structure of our code. We will have the InfrastructureActor, which supervises the SpringActor; the SpringActor, in turn supervises the BeanLookupActor. The actors in the infrastructure hierarchy react to the Start() and Stop() messages; the BeanLookupActor understands the LookupBean message.
On the app side, we have the AppActor1 and AppActor2, which react to some application-level messages--by manipulating the Document instances using the DocumentRepository (viz Figure 2).

Figure 2. Application-level actors

actors2

Boot

So, this gives us the source code, starting with the Boot class and the core messages:

case class Start()

case class Stop()

class Boot(system: ActorSystem) {
  val infrastructure = system.actorOf(
    props = Props[InfrastructureActor],
    name = "infrastructure"
  )
  val app = system.actorOf(
    props = Props[AppActor],
    name = "app"
  )

  infrastructure ! Start()
  app ! Start()

}

You can see how the application starts up. We create instance of the Boot class, passing it a reference to the ActorSystem; we then start creating the structures of the "inner" actors by sending the Start() message.

Infrastructure

Let's take a look at how the InfrastructureActor constructs the Spring actors when it receives the Start() message:

class InfrastructureActor extends Actor {
  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 5) {
      case _ => Restart
    }

  val springActor = context.actorOf(
    props = Props[SpringActor],
    name = "spring"
  )

  def receive = {
    case Start() =>
      springActor ! Start()
    case Stop() =>
      springActor ! Stop()
  }

}

Aha! This is the key concept in our every Akka application. We are constructing hierarchy of actors. Akka knows that the InfrastructureActor supervises the SpringActor, because we construct the SpringActor by calling context.actorOf (rather than system.actorOf in the Boot class).
Onwards--to the SpringActor and the BeanLookupActor

case class LookupBean(beanClass: Class[_])

class SpringActor extends Actor {
  var applicationContext: GenericXmlApplicationContext = _

  protected def receive = {
    case Start() =>
      applicationContext =
        new GenericXmlApplicationContext(
          "classpath*:/META-INF/spring/module-context.xml")

      context.actorOf(
        props = Props(new BeanLookupActor(applicationContext)),
        name = "beanLookup"
      )

    case Stop() =>
      applicationContext.destroy()
  }
}

class BeanLookupActor(applicationContext: ApplicationContext)
  extends Actor {

  protected def receive = {
    case GetBean(beanType) =>
      sender ! applicationContext.getBean(beanType)
  }
}

Right. In the InfrastructureActor, we react to the Start() message by starting the SpringActor, which creates the Spring GenericXmlApplicationContext, which is then given to the BeanLookupActor. The BeanLookupActor uses the constructed ApplicationContext to reply to the LookupBean messages. It all looks rather complicated, but this structure clearly isolates the responsibilities of the different actors and allows me to keep the rest of the system loosely coupled. (For completeness, the /META-INF/spring/module-context.xml is the standard Spring context file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   ...>

  <context:spring-configured />
  <context:annotation-config />

  <mongo:mongo id="mongo" host="localhost" />
  <bean id="mongoTemplate"
    class="org.springframework.data.mongodb.core.MongoTemplate" >
    <constructor-arg ref="mongo"/>
    <constructor-arg name="databaseName" value="bigfish"/>
  </bean>
  <mongo:repositories base-package="org.cakesolutions.app" />

  <tx:annotation-driven mode="aspectj" />

</beans>

I find it quite useful to provide a utility singleton that gives me synchronous semantics of the LookupBean request-reply paradigm:

object Bean {
  import akka.pattern.ask

  private implicit val timeout = Timeout(1000)

  def apply[T](implicit manifest: Manifest[T],
               context: ActorContext) = {
    val beanLookup =
      context.actorFor("/user/infrastructure/spring/beanLookup")
    Await.result(
      (beanLookup ? LookupBean(manifest.erasure)).mapTo[T](manifest),
      timeout.duration)
  }

}

How droll--I can now write Bean[DocumentRepository] and get the required bean from the Spring's ApplicationContext in AppActor1's receive function, for example. But why so complex? Why not just have the singleton Bean that constructs Spring's ApplicationContext; why have the SpringActor and then the BeanLookupActor? Well, it all comes down to coupling of the components. Remember that we are constructing the system of actors so that we can get very loosely coupled behaviour. If we relied on the singleton, the actors that use it would be very closely coupled to it; this would make testing very difficult.

And this is the very reason why I am using Spring Data in this small application. I could, of course, implement the code that accesses MongoDB in my actors directly, but I prefer to receive an instance of sharply defined type in my actors. This instance "talks" to MongoDB, but I can easily set up a mock instance for the tests. This is simply good design choice, and in our situation, Spring Data makes the implementation trivial, too.

The application actors

Just like the infrastructure actors, the application actors will too be supervised by some actor; in this case the AppActor, which will react to the Start() and Stop() messages by starting and stopping all actors that form the rest of the application.

class AppActor extends Actor {
  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 100) {
      case _ => Restart
    }

  protected def receive = {
    case Start() =>
      context.actorOf(
        props = Props[AppActor1],
        name = "1"
      )
      context.actorOf(
        props = Props[AppActor2],
        name = "2"
      )
      ...
    case Stop() =>
      ..
  }
}

Spring Data

One of the beans will be a DocumentRepository, which persists the Document instances in MongoDB. Our app-level actors are going to receive instances of the DocumentRepository by sending the LookupBean message to the BeanLookupActor--typically by using the convenience Bean singleton.

(For more detailed discussion of Spring Data, read my previous post.)

Back to the application actors

The main concept I am going to show in the AppActor1 is the fact that I use the Spring-managed, Spring Data-provided DocumentRepository instance; instance that I get by sending the LookupBean message to the BeanLookupActor.

class AppActor1 extends Actor {
  import akka.pattern.ask
  implicit val timeout = Timeout(1000)

  protected def receive = {
    case Msg1(docId) =>
      val repo = Bean[DocumentRepository]

      val doc = repo.findOne(docId)
      doc.someProperty = "Updated"
      repo.save(doc)

      sender ! ReplyToMsg1(doc)
    case Msg2(docId) =>
      ...
  }
}

Testing our AppActor1 is now easily done--I can mock the reply to the LookupBean message from the BeanLookupActor in Akka's TestKit (more on that in the next post.)

I am not going to show AppActor2--presumably, it is very similar to AppActor1 in its structure.

Spray

Finally, let's take the completed Actor system (with the starting point in the Boot class) and use Spray (and Spray-Can) to give it some HTTP API.

class SprayCanBoot(boot: Boot) {
  val system = boot.system

  val mainModule = new AppService {
    implicit def actorSystem = system
  }

  val rootService = system.actorOf(
    props = Props(new SprayCanRootService(
      system.actorOf(Props(new HttpService(mainModule.demoService)))
    )),
    name = "root-service"
  )

  trait AppService extends Directives {
    implicit val timeout = Timeout(10000)

    val demoService = {
      get {
        path("app1/1") {
          completeWith {
            val actor = system.actorFor("/user/app/1")
            actor ! Msg1(1)

            "Sent"
          }
        } ~
        path("app1/2") {
          completeWith {
            ...
          }
        }
      }
    }
  }

  val ioWorker = new IoWorker(system).start()

  val sprayCanServer = system.actorOf(
    Props(
      new HttpServer(ioWorker,
        MessageHandlerDispatch.SingletonHandler(rootService))),
    name = "http-server"
  )

  sprayCanServer ! HttpServer.Bind("localhost", 8080)

  system.registerOnTermination {
    ioWorker.stop()
  }
}

The code is rather long, but in essence, we take the already created instance of the Boot class and add the Spray directives and Spray-Can HTTP server. The main function completes the picture:

object Main extends App {
  val config = ConfigFactory.load("application.conf")
  val system = ActorSystem("App", config)

  new SprayCanBoot(new Boot(system))

  sys.addShutdownHook {
    system.shutdown()
  }

}

Summary

In this post, I showed how to use Spring Framework as a convenient library in Akka application. I am no longer using the Spring Framework as the container where our actors live! However, I still find the functionality included in the projects that make up the Spring Portfolio far too useful to dismiss. With that in mind, I have shown you how to create a small Akka application; how to use the Spring Framework in it, while keeping it "isolated" and "under the hood".

 

Topics: Akka, Spring Framework

Subscribe to Email Updates