Here’s a 30-second “ping-pong” demo using Akka Actors:
The source code
If you want the source code, you can get it from the GitHub link shown at the end of this post. First, here’s a quick description of it.
The code is in two files, PingPong.scala and PingPongPanel.scala.
PingPong.scala contains three actors:
PingPongMainFrameActor
It also kicks the action off in the PingPongTest object, which extends the App trait. Here’s the source code for PingPong.scala:
package com.alvinalexander.akkademos
import akka.actor._
import java.awt._
import javax.swing._
import scala.util.Random
/**
* Case objects used by the actors.
*/
case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage
case object DisplayMainFrame
case object DoPing
case object DoPong
/**
* The Ping actor. Notice that its constructor takes an ActorRef.
*/
class Ping(pong: ActorRef) extends Actor {
var count = 0
val MAX_NUM_PINGS = 100
val mainFrameActor = context.actorSelection("../mainFrameActor")
def incrementCounter { count += 1 }
def receive = {
case StartMessage =>
incrementCounter
pong ! PingMessage
case PongMessage =>
incrementCounter
if (count > MAX_NUM_PINGS) {
sender ! StopMessage
println("ping stopped")
context.stop(self)
} else {
sender ! PingMessage
mainFrameActor ! DoPing
Thread.sleep(125) // blocking! bad!
}
}
}
/**
* The Pong actor.
*/
class Pong extends Actor {
val mainFrameActor = context.actorSelection("../mainFrameActor")
def receive = {
case PingMessage =>
sender ! PongMessage
mainFrameActor ! DoPong
Thread.sleep(125) // blocking! bad!
case StopMessage =>
println("pong stopped")
context.stop(self)
}
}
/**
* An Actor to handle all the interactions with the JFrame.
*/
class MainFrameActor extends Actor {
val WIDTH = 600
val HEIGHT = 400
val pingPongPanel = new PingPongPanel
val mainFrame = new JFrame {
setMinimumSize(new Dimension(WIDTH, HEIGHT))
setPreferredSize(new Dimension(WIDTH, HEIGHT))
}
configureMainFrame
def receive = {
case DisplayMainFrame => showMainFrame
case DoPing => doAction("PING")
case DoPong => doAction("PONG")
case _ =>
}
def doAction (action: String) {
SwingUtilities.invokeLater(new Runnable {
def run {
action match {
case "PING" => pingPongPanel.doPing
case "PONG" => pingPongPanel.doPong
}
}
})
}
def configureMainFrame {
mainFrame.setTitle("Akka Ping Pong Demo")
mainFrame.setBackground(Color.BLACK)
mainFrame.getContentPane.add(pingPongPanel)
mainFrame.setLocationRelativeTo(null)
}
def showMainFrame {
SwingUtilities.invokeLater(new Runnable {
def run {
mainFrame.setVisible(true)
}
})
}
}
/**
* The "main" part of the application.
*/
object PingPongTest extends App {
// create the actor system
val actorSystem = ActorSystem("PingPongSystem")
// create the actors
val mainFrameActor = actorSystem.actorOf(Props[MainFrameActor], name = "mainFrameActor")
val pong = actorSystem.actorOf(Props[Pong], name = "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), name = "ping")
// display the main frame (jframe)
mainFrameActor ! DisplayMainFrame
Thread.sleep(5*1000)
// start the action
ping ! StartMessage
// shut down the actor system
//actorSystem.shutdown
}
Notice that there’s some blocking in that actor code. That’s bad, don’t do that! I had to do it to slow down the actors so I could see the circles. Otherwise the code finishes almost instantly. (I can improve the painting algorithm shown below, but the code still finishes almost instantly without the intentional delays.)
Without much discussion at this time, here’s the code from the PingPongPanel.scala file:
package com.alvinalexander.akkademos
import javax.swing.JPanel
import java.awt.{Color, Graphics}
import scala.util.Random
class PingPongPanel extends JPanel {
val panelWidth = 600
val panelHeight = 400
val ballDiameter = 40
val halfPanelWidth = panelWidth / 2
val halfPanelHeight = panelHeight / 2
var currentX = 0
var currentY = 0
var color = Color.GREEN
private val r = new Random
def doPing {
var x1 = r.nextInt(halfPanelWidth)
if (x1 < ballDiameter) x1 = ballDiameter
if (x1 > (halfPanelWidth-ballDiameter)) x1 = halfPanelWidth-ballDiameter
currentX = x1
currentY = getRandomY
color = Color.GREEN
repaint()
}
def doPong {
var x1 = r.nextInt(halfPanelWidth) + halfPanelWidth
if (x1 > (panelWidth-ballDiameter)) x1 = panelWidth-ballDiameter
currentX = x1
currentY = getRandomY
color = Color.YELLOW
repaint()
}
def getRandomY = {
var y = r.nextInt(panelHeight)
if (y < ballDiameter) y = ballDiameter / 2
if (y > (panelHeight-ballDiameter)) y = panelHeight-2*ballDiameter
y
}
override def paintComponent(g: Graphics) {
g.clearRect(0, 0, panelWidth, panelHeight)
// draw mid-court line
g.setColor(Color.WHITE)
g.drawLine(halfPanelWidth, 0, halfPanelWidth, panelHeight)
// draw circle (ball)
g.setColor(color)
g.fillOval(currentX, currentY, ballDiameter, ballDiameter)
}
}
More information
Sorry for the rush, I’m a little short on time today, but you can clone the source code from this GitHub project:
I’ll add more discussion here over time, but right now I’m really running late for something else.

