6.1 Simple exception handling

An actor terminates automatically when an exception that is not handled inside the actor's body is thrown. One possible symptom of such a situation is that other actors wait indefinitely for messages from the dead actor. Since, by default, terminating actors do not generate any feedback, it can be quite time-consuming to find out what happened and why.

The way to guard against actors that silently terminate because of unhandled exceptions is to invoke a global exception handler whenever an exception propagates out of the actor's body. You can do this by subclassing Actor (or its super-trait Reactor, which will be described in Chapter 11) and overriding its exceptionHandler member. It is defined as follows (omitting the method's modifiers):

  def exceptionHandler: PartialFunction[Exception, Unit]

As you can see, a parameterless method returns a partial function that you can apply to instances of java.lang.Exception. Whenever an exception that would normally cause the actor to terminate is thrown inside an actor's body, the runtime system checks whether the actor's exceptionHandler matches the given exception. If so, the exceptionHandler partial function is applied to the exception. After that, the actor terminates normally.

    object A extends Actor {
      def act() {
        react {
          case 'hello =>
            throw new Exception("Error!")
        }
      }
      override def exceptionHandler = {
        case e: Exception =>
          println(e.getMessage())
      }
    }
Listing 6.1 - Defining an actor-global exception handler.

Listing 6.1 shows how to override the exceptionHandler method so that it returns a custom partial function. The actor itself handles a single message consisting of the Symbol 'hello.[1] Let's interact with the A actor using Scala's interpreter shell:

  scala> A.start()
  res0: scala.actors.Actor = A$@1ea414e
  
scala> A ! 'hello Error!

As expected, A's overridden exceptionHandler method runs, printing the message string attached to the thrown exception, which is just "Error!".

This form of exception handling using exceptionHandler works well together with control-flow combinators, such as loop. You can use the combinators to resume the normal execution of an actor after handling an exception. For example, let's modify the A actor's act method in Listing 6.1 as follows:

  def act() {
    var lastMsg: Option[Symbol] = None
    loopWhile (lastMsg.isEmpty || lastMsg.get != 'stop) {
      react {
        case 'hello =>
          throw new Exception("Error!")
        case any: Symbol =>
          println("your message: " + any)
          lastMsg = Some(any)
      }
    }
  }

The invocation of react is now wrapped inside a loopWhile that tests whether the last received message is equal to 'stop, in which case the actor terminates. Now, if the actor receives a 'hello message, it throws the exception, which is handled as before. However, instead of terminating, the actor simply resumes its execution by continuing with the next loop iteration. This means that the actor is ready to receive more messages after the exception has been handled.

Let's try this out in the interpreter:

  scala> A.start()
  res0: scala.actors.Actor = A$@1cb048e
  
scala> A ! 'hello Error!
scala> A.getState res2: scala.actors.Actor.State.Value = Suspended
scala> A ! 'hi your message: 'hi
scala> A ! 'stop your message: 'stop
scala> A.getState res5: scala.actors.Actor.State.Value = Terminated

Note that after sending 'hello the actor eventually suspends waiting for the next message. You can use the getState method to query an actor's execution state. It returns values of the Actor.State enumeration, which is defined in the Actor object. The Suspended state indicates that the actor has invoked react and is now waiting for a matching message. Therefore, we can continue to interact with the actor by sending it a 'hi message. After the actor receives a 'stop message, its loopWhile loop finishes and the actor terminates normally. The final state value is Terminated.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset