2010-02-17 9 views
1

J'ai un problème avec ma compréhension de la bibliothèque d'acteur standard dans Scala. Dans le code ci-dessous j'ai créé un simple swing, qui devrait essentiellement tester s'il est capable de se connecter à un serveur PostgreSQL. Cependant, cela ne va pas si loin, j'utilise Actors puisque l'interface utilisateur se bloquerait en faisant le travail nécessaire pour se connecter à la base de données. Lorsque er j'utiliser cette ligne (ce qui signifie que j'utilise des acteurs au lieu d'un seul fil)Scala Les acteurs suspendent inattendu lors de la connexion à une base de données

PostgresCheck ! new GetInfo() 

Swing ne sera jamais mis à jour. Cependant, si je commente la ligne et utilise les trois lignes suivantes. (ce qui signifie que les acteurs ne seront pas utilisés)

 val result = PostgresCheck.checkPostgreSQL 
     if (result == "OK") pgText.background = GREEN else pgText.background = RED 
     pgText.text = result 

Le Swing se fige mais après environ 25 secondes, le swing sera mis à jour.

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" 
} 
+0

Pouvez-vous sauvegarder une trace de pile lorsque votre application se bloque? Sur les fenêtres, CTRL-BREAK dans la fenêtre de la console, sur Unix, 'kill -QUIT pid', où pid est l'ID du processus. – retronym

+0

Sous les paramètres de configuration standard, CTRL- \ génère un signal QUIT sous Unix et Linux. –

+0

Etes-vous sûr que l'initialisation de PostgresCheck est déjà exécutée? (C'est-à-dire si vous mettez un 'println (" Running ")' après le 'start()', le voyez-vous?) Est-ce que vous le faites dans la déclaration de réaction de guiActor? –

Répondre

2

Vous envoyez le message en dehors d'un acteur, semble-t-il. Essayez ceci:

Actor.actor { PostgresCheck ! new GetInfo() } 

Vous ne savez pas si cela aidera, mais c'est un conseil standard.

Et, maintenant que j'y pense, à qui la réponse sera-t-elle envoyée? Vous répondez à l'expéditeur inexistant. Je suppose que vous voulez la réponse à guiActor, mais je ne vous vois pas le faire.

+0

Je ne fais pas de différence si j'utilise votre méthode suggérée. Le fil de l'événement GUI crée simplement un message et le met dans la file d'attente, donc cela ne devrait pas faire de différence. Bon endroit je n'ai pas remarqué que j'ai envoyé la réponse au mauvais endroit, 'expéditeur' devrait être remplacé par 'Example.guiActor'. – Stefan

1

Bon on y va, le problème était lié à la ligne

sender ! new Info(checkPostgreSQL) 

Il aurait en fait dû

Example.guiActor! new Info(checkPostgreSQL) 

Pour une raison liée à la bibliothèque Acteur suspend effectivement dans l'attente du connexion à la base de données, et ne retournera pas en raison d'un expéditeur inconnu. Par exemple, les lignes suivantes entraînent l'impression d'une seule ligne dans la console avec "1".

 val db = new Database(myPgSQL) 
     println("1") 
     // Freezes while doing this method 
     val con: Connection = myPgSQL.getConnection 
     println("2") 

Lors de la modification de la ligne mentionnée, le code se comporte comme prévu.