Scala Actors suspends unexpected when connecting to a database
문제
I have a problem with my understanding of the standard actor library in Scala. In the code below I have created a simple swing, which basically should test if it is able to connect to a postgreSQL server. However it doesnt make it that far, I use Actors since the UI otherwise would freeze up while doing the work needed to connect to the database. When er i use this line (meaning that I use actors instead of a single thread)
PostgresCheck ! new GetInfo()
The Swing will never be updated. However if I comment the line out and use the next three lines. (meaning the actors wont be used)
val result = PostgresCheck.checkPostgreSQL
if (result == "OK") pgText.background = GREEN else pgText.background = RED
pgText.text = result
The Swing will freeze but after about 25 seconds the swing will be updated.
import dbc.Database
import dbc.vendor.PostgreSQL
import java.awt.Dimension
import java.net.URI
import java.sql.Connection
import swing.event._
import swing._
import actors.Actor
import java.awt.Color._
import scala.actors.Actor._
case class Info(reply: String)
case class GetInfo()
object Example extends SimpleSwingApplication {
val pgButton = new Button("Check PostgreSQL")
val pgText = new TextArea("Not Checked Yet")
val pgPanel = new GridPanel(1, 2)
pgPanel.contents += pgButton
pgPanel.contents += pgText
def top = new MainFrame {
title = "StateChecker"
contents = pgPanel
}
listenTo(pgButton)
reactions += {
case e: ButtonClicked if (e.source.eq(pgButton)) => {
PostgresCheck ! new GetInfo()
//val result = PostgresCheck.checkPostgreSQL
//if (result == "OK") pgText.background = GREEN else pgText.background = RED
//pgText.text = result
}
}
val guiActor = new Actor {
def act() = {
loop {
react {
case e: String => {
val result = e
if (result == "OK") pgText.background = GREEN else pgText.background = RED
pgText.text = result
}
case e => println(e.toString)
}
}
}
}
guiActor.start
}
object PostgresCheck extends Actor {
def checkPostgreSQL() = {
try {
val db = new Database(myPgSQL)
val con: Connection = myPgSQL.getConnection // Freezes while doing this method
val statement = con.createStatement
if (statement.getResultSet.getMetaData.getColumnCount == 1) "OK"
else statement.getWarnings.toString
}
catch {
case e => e.toString
}
}
def act() = {
loop {
react {
case e: GetInfo => {
sender ! new Info(checkPostgreSQL)
}
}
}
}
start()
}
object myPgSQL extends PostgreSQL {
val uri = new URI("jdbc:postgresql://whatever.com")
val user = "1234"
val pass = "1234"
}
해결책
You are sending the message outside an actor, it seems. Try this:
Actor.actor { PostgresCheck ! new GetInfo() }
Not sure if it will help, but it is standard advise.
And, now that I think of it, to whom will the answer be sent? You are replying to the non-existent sender. I suppose you want the answer going to guiActor
, but I don't see you doing so.
다른 팁
Okay here we go, the problem was related to the line
sender ! new Info(checkPostgreSQL)
It should actually have been
Example.guiActor! new Info(checkPostgreSQL)
For some reason related to the Actor library it actually suspends when waiting for the database connection, and wont return because of an unknown sender. For instance the following lines result in a printout of just a single line in console with "1".
val db = new Database(myPgSQL)
println("1")
// Freezes while doing this method
val con: Connection = myPgSQL.getConnection
println("2")
When changing the mentioned line, the code behave as expected.