How a newcomer can understand Akka’s principles of work? Couple of examples from the documentation could be not enough. From the other side, a simple application which shows Akka in action is a very good way for demonstration of Actors capabilities. So let’s start the acquaintance with the simple Akka use case!

Task

At start we need to consider following task:

Get all content from files which are located in the “resources” directory and write it in one resulting text file.

Very convenient when a task could be explained in one sentence. And even more convenient when a task is solved with Akka.

Project setup

I work in Intellij IDEA and use SBT, here is my build.sbt file:

name := """akkaTutorial"""

version := "0.1"

scalaVersion := "2.11.7"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.4.1"
)

The entire project structure is displayed below. Don’t panic, we will go through a creation of each class and object from this screenshot.

akka-project-structure

The last thing which can be useful for you in this section is the link to the GitHub repository.

Developing a simple Akka application

According to the task, we need to get a content from the text files which are stored in the “resources” folder and then write it to the result file. How it should be done using Actors? Those of you who have read the Akka reference, probably remember that an Actor which receives a large task can broke the task on smaller chunks and pass them on execution to its children.

Actors-hierarchy

In general we can separate main task on two different activities:

  1. Scan folder with files
  2. Process files

Actually also we can add one more Actor for writing of final results, but I will not do so. Let it remains as an opportunity for you to modify and enhance my app.

Here is a code of FileReaderActor it plays role of children in this app:

package com.actor

import java.io.File

import akka.actor.{PoisonPill, Props, Actor}
import akka.event.Logging

import scala.collection.mutable.ListBuffer
import scala.io.Source

class FileReaderActor extends Actor {

  val log = Logging.getLogger(context.system, this)

  def receive = {
    case f: File => {
      log.info(s"Reading file ${f.getName}")
      var words = new ListBuffer[String]
      Source.fromFile(f).getLines().foreach(line => words += line )
      sender() ! words.toList
      self ! PoisonPill
    }
    case _ => log.info("Still waiting for a text file")
  }

}

object FileReaderActor {
  def props = Props(new FileReaderActor)
}

This Actor works just with File messages. It reads it, sends a List[String] as a response to a parent Actor and then sends to self the PoisonPill as a result it dies, because it has only single responsibility – read the text and send the result to the parent.

Now let’s take a look at the FolderScannerActor. In some sense it’s the main Actor in the app, because it initiates a process of file reading. It has two Int fields filesNumber and responsesNumber. Based on them we know when to start writing the result file. A logic is simple, when the responsesNumber equals to the filesNumber, that’s means all files were processed and we can write the results.

package com.actor

import java.io.File
import akka.actor.{Props, Actor}
import akka.event.Logging
import scala.collection.mutable.ListBuffer

class FolderScannerActor extends Actor {
  import FolderScannerActor._

  val log = Logging.getLogger(context.system, this)

  var filesNumber = 0;
  var responsesNumber = 0;
  var words = new ListBuffer[String]

  def receive = {
    case path: String => {
      log.info(s"Scanning ${path}")
      val directory = new File(path)
      val files = getFilesFromFolder(directory)
      filesNumber = files.size
      files.foreach(file => context.actorOf(FileReaderActor.props) ! file)
    }
    case wordsList: List[String] => {
      log.info(s"New words are received ${wordsList}")
      responsesNumber += 1
      words insertAll(words.size, wordsList)
      if (filesNumber == responsesNumber) {
        writeResults(words.toList)
      }
    }
    case _ => log.info("Nothing to scan...")
  }
}

object FolderScannerActor {

  def props = Props(new FolderScannerActor)

  def getFilesFromFolder(folder: File): List[File] = {
    if (folder.exists && folder.isDirectory) {
      println("FILES EXIT")
      folder.listFiles
        .toList
    }
    else {
      println("FILES DOES NOT EXIT")
      List[File]()
    }
  }

  def writeResults(words: List[String]) = {
    import java.nio.file.{Paths, Files}
    import scala.collection.JavaConverters._
    //Location where you want to write results
    val path = "/Users/Alex/Downloads/results/result.txt"
    val resultPath = Paths.get(path)
    if (Files.exists(resultPath))
      Files.delete(resultPath)
    Files.createFile(resultPath)
    Files.write(Paths.get(path), words.asJava)
  }
}

As you noticed the FolderScannerActor creates new FileReaderActor per each file in the folder which it scans. This approach should provide for us fast, non-blocking and concurrent processing of all files.

One of possible message types which FolderScannerActor handles is List[String]. This case is exactly for the children responses.

I tried to develop this app as simple as possible, but in the same time I wanted to make it like a real app which does useful job. So forgive me the redundant code in the companion object.

Here is the code for demonstration:

package com.actor

import akka.actor.ActorSystem

object Demo {
  def main(args: Array[String]): Unit = {

    val system = ActorSystem.create("file-reader")
    val scanner = system.actorOf(FolderScannerActor.props, "scanner")
    val directoryPath = getClass.getResource("/a-words").getPath

    scanner ! directoryPath

  }
}

After running the code above you will see the results in the text file.

That’s it. Leave your comments about the app, I’ll be really appreciated to read them. Also subscribe to updates of the “Fruzenshtein Notes”, I’m planning to write a lot of stuff about Akka, Scala and BigData.

About The Author

Mathematician, programmer, wrestler, last action hero... Java / Scala architect, trainer, entrepreneur, author of this blog

Close