Creating First Akka Application

Jason Goodwin

November 2015

In this article by Jason Goodwin, the author of the book Learning Akka, we have covered how to set up your environment and how to create a project, we can proceed with creating some actor code in Akka and then understand how to validate this code. We will be using simple build tool(SBT), which is the preferred build tool for Scala projects, Play Framework and Activator also use this build tool under the hood. It's not complex and we will use it only for managing dependencies and building, testing, and running the applications. So, it should not be an obstacle to learn Akka.

(For more resources related to this topic, see here.)

Add Akka to build.sbt

We will now open the application (either Java or Scala) in our favorite IDE. The scaffolding Activator that is created is not for an Akka project, so we will need to add Akka dependencies first. We will add both, the Akka core, Akka module (akka-actor) and the Akka test-kit, which contains tools to easily allow us to test the actors.

In the build.sbt file, you will see something roughly similar to this for a Scala project. Note that the dependencies are actually Maven dependencies. All the Maven dependencies can easily be added. The Java and Scala projects will be more or less identical; however, the Java project will have a Junit dependency instead of a Scala test:

name := """akkademy-db-java"""

version := "1.0"

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.3.6",
"com.typesafe.akka" %% "akka-testkit" % "2.3.6" % "test",
"junit"            % "junit"           % "4.11" % "test",
"com.novocode"     % "junit-interface" % "0.10" % "test"
)

To Include Akka, we need to add a new dependency.

Your dependencies will look something like the following for Java:

libraryDependencies ++= Seq(
"com.typesafe.akka" % "akka-actor_2.11" % "2.3.6",
"junit"             % "junit"           % "4.11" % "test",
"com.novocode"     % "junit-interface" % "0.10" % "test"
)

They will look something like the following for Scala:

name := """akkademy-db-scala"""

version := "1.0"

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.3.3",
"com.typesafe.akka" %% "akka-testkit" % "2.3.6" % "test",
"org.scalatest" %% "scalatest" % "2.1.6" % "test" )

A note on getting the right Scala version with %%

As Scala does not have any kind of binary compatibility across major versions, libraries will often be built and published across several versions of Scala. To have SBT, try to resolve the dependency built for the correct Scala version for your project. You can change the dependency declared in the build.sbt file to use two percent symbols after the group ID, instead of specifying the Scala version in the artifact ID

For example, in a Scala 2.11 project, these two dependencies are equivalent, as shown in the following code:

"com.typesafe.akka" % "akka-actor_2.11" % "2.3.3"
"com.typesafe.akka" %% "akka-actor" % "2.3.3"

Adding other dependencies from Maven Central

All Maven dependencies can be added here from http://www.mvnrepository.com. You can check that for any artifact in this link, there is an sbt tab that will give you a line to add the dependency.

Creating Your First Actor

In this section, we will create an actor that receives a message and updates its internal state by storing the values from the message into a map. This is a humble beginning of our distributed database.

Make the Message First

We're going to begin our in-memory database with a SetRequest message that will store a key (String) and a value (any Object) in the memory. You can think of it as a combination of both, an insert and an update in one, or similar to the set operation on a Map.

Remember, our actor has to get the message from his mailbox and check the instruction in this message. We use the class/type of the message to determine the instruction. The contents of this message type describe the exact details of how to fulfill the contract of the API. In this case, we will describe the key as a String and the value as an Object inside the message so that we know what to store.

Messages should always be immutable in order to avoid strange and unexpected behavior. This should be done primarily by ensuring you and your team not to do unsafe things across execution contexts/threads. Also remember that these messages may not simply be destined for a local actor, but for another machine. If possible, mark everything val (Scala) or final (java) and use immutable collections and types, such as those found in Google Guava (Java) or the Scala Standard Library.

Java

Here is our Set message in Java as an immutable object. This is a fairly standard approach to immutable objects in Java. This sight will be familiar to any skilled Java developer. You should generally prefer immutability in all of your code.

package com.akkademy.messages;

public class SetRequest {
   private final String key;
   private final Object value;

   public Set(String key, Object value) {
this.key = key;
this.value = value;
   }

   public String getKey() {
       return key;
   }

   public Object getValue() {
       return value;
   }
}

Scala

In Scala, we have a much more succinct way of defining immutable messages—the case class. The case class allows us to create an immutable message.Values can only be set once in the constructor. Then, they are read from the following fields:

package com.akkademy.messages
case class SetRequest(key: String, value: Object)

This is it for the messages.

Define Actor Response to the Message

Now that we have created the message, we can create the actor and describe the behavior that the actor will take in responseto our message. In our very early example here, we are going to do two things:

  1. Log the message.
  2. Store the contents of any Set message for later retrieval.

We'll have a look at the Java8 actor first.

Java – AkkademyDb.java

The following code represents the Java code to implement Akkademydb.java

package com.akkademy;

import akka.actor.AbstractActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
import com.akkademy.message.SetRequest;

import java.util.HashMap;
import java.util.Map;

public class AkkademyDb extends AbstractActor {
   protected final LoggingAdapter log = Logging.getLogger(context().system(), this);
   protected final Map<String, Object> map = new HashMap<>();

   private AkkademyDb(){
       receive(ReceiveBuilder.
                       match(SetRequest.class, message | {
log.info("Received set request –key: {} value: {}", message.getKey(), message.getValue());
map.put(message.getKey(), message.getValue());
                      }).
matchAny(o ->log.info("received unknown message {}", o)).build()
       );
   }
}

The actor is a Java class that extends AbstractActor (a Java8Akka Actor API). We have to create the logger and the map in the class as protected members so that we can access them in test cases later in the article.

In the constructor, we call receive. The receive method takes a ReceiveBuilder, which has several methods that is chained together to produce the final ReceiveBuilder. With this, we can describe how the actor should behave in response to different message types. We have defined two behaviors here and we will look at them one at a time.

First, we will define the behavior to respond to any SetRequest messages:

match(SetRequest.class, message | {
log.info("Received Set request: {}", message);
map.put(message.getKey(), message.getValue());
                       }).

The ReceiveBuilder's match method in the Java8api is somewhat similar to a case statement except that we can match on class types. More formally, this is pattern matching.

Then, the match method call will notify if the message is of type SetRequest.class, take that message, log it, and put a new record in the map using the key and value of this Set message.

Second, we will define a catch all to simply log any unknown message:

matchAny(o ->log.info("received unknown message"))

Scala – AkkademyDb.scala

Scala is a natural fit because the language has pattern matching as a first-class language construct. Now, we'll have a look at the Scala equivalent code:

package com.akkademy

import akka.actor.Actor
import akka.event.Logging
import scala.collection.mutable.HashMap
import com.akkademy.messages.SetRequest

class AkkademyDb extends Actor {
val map = new HashMap[String, Object]
val log = Logging(context.system, this)

override def receive = {
   case SetRequest(key, value) => {
log.info("received SetRequest - key: {} value: {}", key, value)
map.put(key, value)
   }
   case o =>log.info("received unknown message: {}", o);
}
}

In the Scala API, we will mix in the Actor trait, define the map and logger as we did in Java, and then implement the receive method. The receive method on the Actor super-type returns the Receive which, in the Akka source, is defined as a partial function, shown as follows:

type Receive = scala.PartialFunction[scala.Any, scala.Unit]

We will define the behavior for the response to the SetRequest message using pattern matching to produce the partial function. We can extract the key and the value variables for clearer code using pattern matching semantics:

case SetRequest(key, value)

The behavior is used to simply log in the request, and then to set the key/value in the map:

case SetRequest(key, value) => {
log.info("received SetRequest - key: {} value: {}", key, value)
map.put(key, value)
   }

Finally, we will add a catch-all case to simply log in unknown messages:

case o =>log.info("received unknown message: {}", o);

This it for the actor. Now, we have to validate that we did everything correctly.

Validating the Code with Unit Tests

While books covering frameworks may print to the console or create web pages that are suitable evidence to know that our code is working, we're going to be using unit tests to validate code and demonstrate its use. Library code and services often don't have an API that is easy to interact with. or otherwise to observe, testing is generally how these components are validated in mostly every project. This is an important skill to have under the belt for any serious developer.

Akka Test kit

Akka provides a test kit, which consists mostly anything that you would ever need to test your actor code. We have included the test kit dependencies earlier when we set up our project. For reference, the SBT dependency that is placed in build.sbt is given here:

"com.typesafe.akka" %% "akka-testkit" % "2.3.6" % "test"

We're going to use the TestActorRef generic here from the test kit, instead of a normal ActorRef. TestActorRef does two things. First, it makes the actor's API synchronous so that we don't need to think about concurrency in our tests; second, it gives us access to the underlying Actor object.

To be clear, Akka hides the actual Actor (AkkademyDb), and instead, gives a reference to the actor that you send messages to. This encapsulates the actor to enforce the message from passing, as nobody can access the actual object instance.

Next, we will look at the source code and then explore it line by line.

Java

The following is the source code for the Akka toolkit:

package com.akkademy;

import static org.junit.Assert.assertEquals;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.TestActorRef;
import com.akkademy.messages.SetRequest;
import org.junit.Test;

public class AkkademyDbTest {

ActorSystem system = ActorSystem.create();

   @Test
   public void itShouldPlaceKeyValueFromSetMessageIntoMap() {
TestActorRef<AkkademyDb>actorRef = TestActorRef.create(system, Props.create(AkkademyDb.class));
actorRef.tell(new SetRequest("key", "value"), ActorRef.noSender());

AkkademyDbakkademyDb = actorRef.underlyingActor();
assertEquals(akkademyDb.map.get("key"), "value");
   }

}

Scala

The following source code represents interaction with the actor:

package com.akkademy

import akka.util.Timeout
import org.scalatest.{BeforeAndAfterEach, FunSpecLike, Matchers}
import akka.actor.ActorSystem
import com.akkademy.messages.SetRequest
import akka.testkit.TestActorRef
import scala.concurrent.duration._

class AkkademyDbSpec extends FunSpecLike with Matchers with BeforeAndAfterEach {
implicit val system = ActorSystem()

describe("akkademyDb") {
   describe("given SetRequest"){
     it("should place key/value into map"){
valactorRef = TestActorRef(new AkkademyDb)
actorRef ! SetRequest("key", "value")

valakkademyDb = actorRef.underlyingActor
akkademyDb.map.get("key") should equal(Some("value"))
     }
   }
}
}

This is the first time that we are looking forward to interacting with an actor so that there is some kind of new code and behavior. Some of it is test specific, and some of it is related to make an interaction with the actor.

We've described an Actor System as a place where actors and their addresses reside. The first thing that we need to do before creating the actor is to get a reference to an actor system. We will create one as a field in the test, shown as follows:

//Java
ActorSystem system = ActorSystem.create();

//Scala
implicit val system = ActorSystem()

After creating the actor system, we can now create our actor in the actor system. As mentioned earlier, we're going to use the Akka Test kit to create a TestActorRef, which has a synchronous API and allows us to get at the underlying actor. We will create the actor in our actor system here:

//Java
TestActorRef<AkkademyDb>actorRef = TestActorRef.create(system, Props.create(AkkademyDb.class));

//Scala
valactorRef = TestActorRef(new AkkademyDb)

We call the AkkaTestkitTestActorRef create method to pass in the actor system that we created (it implicitly passes in Scala) and a reference to the class. Actor instances are hidden away so that the act of creating an actor in our actor system returns a ActorRef (in this case, TestActorRef), which we can send messages to. The system and class references are enough for Akka to create this simple actor in our actor system. So, we have successfully created our first actor.

We will communicate with an actor via message-passing. We will place a message into an actor's mailbox with tell or ! in Scala, which is still read as tell. We will define that there is nobody present make a response for this message as a parameter of the tell method in Java. In Scala, outside of an actor, this is implicit:

//Java
actorRef.tell(new SetRequest("key", "value"), ActorRef.noSender());

//Scala
actorRef ! SetRequest("key", "value")

As we are using TestActorRef, the call to tell will not continue until the request is processed. This is fine as a look at our first actor, but it's important to note that this example does not expose the asynchronous nature of the Actor's API. This is not the usual behavior. Tell is an asynchronous operation that immediately returns for normal usage.

Finally, we need to make sure that the behavior is correct by asserting that the actor placed the value into its map. To do this, we will get a reference to the underlying Actor instance, inspect the map by calling get (key), and ensure that the value is there:

//Java
AkkademyDbakkademyDb = actorRef.underlyingActor();
assertEquals(akkademyDb.map.get("key"), "value");

//Scala
valakkademyDb = actorRef.underlyingActor
akkademyDb.map.get("key") should equal(Some("value"))

This is it for the creation of our first simple test case. This basic pattern can be built for unit testing Actors synchronously.

Running the Test

We're almost there! Now that we've built our tests, we can go to the command line and run activator to start the activator cli. Next, we can run 'clean' to tidy up any kind of garbage and then 'test' which will fire off the tests. To do this in one step, we can run activator clean test.

You will see something like the following for the Java Junit test:

[INFO] [01/12/2015 23:09:24.893] [pool-7-thread-1] [akka://default/user/$$a] Received Set request: Set{key='key', value=value}
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[success] Total time: 7 s, completed 12-Jan-2015 11:09:25 PM

If you're using Scala, then scala-test will give you a bit nicer output:

[info] AkkademyDbSpec:
[info] akkademyDb
[info] - should place key/value from Set message into map
[info] Run completed in 1 second, 990 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.

The output will give you some information about how many tests were run, how many tests failed; if there are any errors, it will indicate where the failures occurred so that you can further investigate them. Once you have a test in place on a behavior, you can be confident that any changes or refactorings that you apply did not break the behavior.

Summary

We have officially started our journey into building a scalable and distributed applications using Akka.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Learning Akka

Explore Title