Handle Every Event in Your Akka Application

akka-logoEvent here, event there, events flying everywhere. Post about checking that every Akka event will finally find its home.

Akka and reactive, event-based applications are new approach to creating software. We are using Akka pretty intensively in our current Scala-based project. Events fit our use cases especially well as we are communicating with external API which might be slow. This could damage user experience when handled using traditional synchronous approach. But luckily, our requests can be performed asynchronously so passing them to Actor seemed a good idea.

When things get ouf of control

But while being cool and very useful, events can still hurt project when handled by inexperienced hands. Asynchronous nature makes application flow hard to understand at first glance. And each time you add a new actor or event type to your system, probability that you will forget to handle something properly increases.

Let’s look at the example class, this is an actor handling events associated with Image tags and comments:

class YourActor extends Actor {
    override def receive = {
        case event: ImageTagged =>
          doSomething()
        case event: OtherImageTaggedByFriend =>
          doSomething2()
        case event: MostMotedUserImage =>
          doSomething3()
        case event: MostCommentedFriendImageChosen =>
          doSomething4()
      }
}

and when you add next event, let’s say MostLikedFriendImage you can easily forget to add handler case section in actor, especially if there is more than one actor listening for this type of event.

DRY violating solution

There is one simple solution that will allow to detect forgotten handlers. We can add case _ to each actor:

class YourActor extends Actor {
    override def receive = {
        case event: ImageTagged =>
          doSomething()
        case event: OtherImageTaggedByFriend =>
          doSomething2()
        case event: MostMotedUserImage =>
          doSomething3()
        case event: MostCommentedFriendImageChosen =>
          doSomething4()
        case event: _ :
          logger.error("Received unknown event " + event.getClass.toString)
      }
}

And while it looks pretty ok for one or two actors, adding same code fragment to multiple actors is troublesome and violates DRY principle. But, what is most dangerous, someone in your team could forget to add it (as someone said “Every manual task that can be forgotten, will be forgotten”). So maybe we should pursue better solution?

React on ANY unhandled event

event-shall-not-unhandled

Luckily, we are not stuck with our error-prone approach. When actor can not handle event that was passed to him UnhandledMessage is raised and published to ActorSystem’s EventStream.

So to handle every forgotten event we could create listener and subscribe it to EventStream:

class UnhandledMessageListener extends Actor {

  val logger = LoggerFactory.getLogger(getClass)


  override def receive = {
    case message: UnhandledMessage =>
      logger.error(s"CRITICAL! No actors found for message ${message.getMessage}"))

      if (!Environment.isProduction) {
        // Fail fast, fail LOUD
        logger.error("Shutting application down")
        System.exit(-1)
      }
  }
}

And subscribing code fragment:

val actorSystem = ActorSystem.create("projectActorSystem")

 val listener = actorSystem.actorOf(Props(new UnhandledMessageListener()))
 actorSystem.eventStream.subscribe(listener, classOf[UnhandledMessage])

and that’s it. Now every time there is an event that wasn’t handled by actor, we will know about it, especially when application is deployed in a non-production environment!

Reference: Handle Every Event in Your Akka Application from our JCG partner Tomasz Dziurko at the Code Hard Go Pro blog.
Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


+ eight = 12



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books