2011-08-31 3 views
10

Je suis nouveau à Scala et Akka alors pardonnez-moi si c'est une question de newb, mais je ne peux pas trouver la réponse ailleurs ...Pourquoi Scala ne parvient-il pas à instancier un objet compagnon?

Pour l'enregistrement j'emploie Scala 2.9.0-1 et Akka 1.1.3 et j'ai également inclus ma configuration SBT 0.10.1.

J'ai écrit le code qui suit ce message comme une expérience dans Akka; c'est une version jouet d'une base de données utilisateur et des installations d'inscription. L'idée de base est qu'il y a un ActorPool d'acteurs UserServer dont chacun a une instance d'une MemoryUserDatabase qui utilise STM pour interagir avec une carte d'utilisateurs clavée des adresses e-mail des utilisateurs - assez simple, non?

Le problème peut être reproduit par la constitution du dossier et en cours d'exécution ce qui suit dans deux consoles séparées:

Console # 1:

importation toy.service.user._; ServiceRunner.run

Console # 2:

toy.service.user._ d'importation; ClientRunner.run

Ceci est la sortie de la console du serveur (# 1)

Aug 31, 2011 5:21:29 PM org.multiverse.api.GlobalStmInstance <clinit> 
INFO: Initializing GlobalStmInstance using factoryMethod 'org.multiverse.stms.alpha.AlphaStm.createFast'. 
Aug 31, 2011 5:21:29 PM org.multiverse.stms.alpha.AlphaStm <init> 
INFO: Created a new AlphaStm instance 
Aug 31, 2011 5:21:29 PM org.multiverse.api.GlobalStmInstance <clinit> 
INFO: Successfully initialized GlobalStmInstance using factoryMethod 'org.multiverse.stms.alpha.AlphaStm.createFast'. 
[ERROR] [8/31/11 5:21 PM] [akka:event-driven:dispatcher:global-3] [LocalActorRef] Availability(foo) 
java.lang.NoClassDefFoundError: Could not initialize class toy.service.user.memory.MemoryUserDatabase$ 
    at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$1.apply(Registration.scala:96) 
    at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$1.apply(Registration.scala:96) 
    at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$2.apply(Registration.scala:96) 
    at toy.service.user.memory.MemoryUserDatabase$$anonfun$getUser$2.apply(Registration.scala:96) 
    at akka.stm.Stm$$anon$1.call(Stm.scala:51) 
    at org.multiverse.templates.TransactionBoilerplate.executeWithTransaction(TransactionBoilerplate.java:279) 
    at org.multiverse.templates.TransactionBoilerplate.executeChecked(TransactionBoilerplate.java:218) 
    at org.multiverse.templates.TransactionBoilerplate.execute(TransactionBoilerplate.java:169) 
    at akka.stm.Stm$class.atomic(Stm.scala:50) 
    at akka.stm.package$.atomic(package.scala:10) 
    at akka.stm.Stm$class.atomic(Stm.scala:47) 
    at akka.stm.package$.atomic(package.scala:10) 
    at toy.service.user.memory.MemoryUserDatabase.getUser(Registration.scala:95) 
    at toy.service.user.UserDatabase$class.available(Registration.scala:88) 
    at toy.service.user.memory.MemoryUserDatabase.available(Registration.scala:92) 
    at toy.service.user.UserServer$$anonfun$receive$1.apply(Registration.scala:77) 
    at toy.service.user.UserServer$$anonfun$receive$1.apply(Registration.scala:76) 
    at akka.actor.Actor$class.apply(Actor.scala:478) 
    at toy.service.user.UserServer.apply(Registration.scala:74) 
    at akka.actor.LocalActorRef.invoke(ActorRef.scala:860) 
    at akka.dispatch.MessageInvocation.invoke(MessageHandling.scala:26) 
    at akka.dispatch.ExecutableMailbox$class.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:214) 
    at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:120) 
    at akka.dispatch.ExecutableMailbox$class.run(ExecutorBasedEventDrivenDispatcher.scala:186) 
    at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.run(ExecutorBasedEventDrivenDispatcher.scala:120) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
    at java.lang.Thread.run(Thread.java:680) 
    at akka.dispatch.MonitorableThread.run(ThreadPoolBuilder.scala:181) 

L'erreur est légèrement plus intéressante en utilisant Scala 2.9.1.final:

Aug 31, 2011 5:38:35 PM org.multiverse.api.GlobalStmInstance <clinit> 
INFO: Initializing GlobalStmInstance using factoryMethod 'org.multiverse.stms.alpha.AlphaStm.createFast'. 
Aug 31, 2011 5:38:35 PM org.multiverse.api.GlobalStmInstance getMethod 
INFO: Failed to initialize GlobalStmInstance through System property 'org.multiverse.api.GlobalStmInstance.factoryMethod' with value 'org.multiverse.stms.alpha.AlphaStm'.'org.multiverse.stms.alpha.AlphaStm.createFast' is not an existing class (it can't be found using the Thread.currentThread.getContextClassLoader). 
[ERROR] [8/31/11 5:38 PM] [akka:event-driven:dispatcher:global-3] [LocalActorRef] Availability(foo) 
java.lang.ExceptionInInitializerError 
    at akka.stm.TransactionFactory.<init>(TransactionFactory.scala:172) 
    at akka.stm.TransactionFactory$.apply(TransactionFactory.scala:122) 
    at akka.stm.Stm$class.$init$(Stm.scala:44) 
    at akka.stm.package$.<init>(package.scala:10) 
    at akka.stm.package$.<clinit>(package.scala) 
    at toy.service.user.memory.MemoryUserDatabase.getUser(Registration.scala:95) 
    at toy.service.user.UserDatabase$class.available(Registration.scala:88) 
    at toy.service.user.memory.MemoryUserDatabase.available(Registration.scala:92) 
    at toy.service.user.UserServer$$anonfun$receive$1.apply(Registration.scala:77) 
    at toy.service.user.UserServer$$anonfun$receive$1.apply(Registration.scala:76) 
    at akka.actor.Actor$class.apply(Actor.scala:478) 
    at toy.service.user.UserServer.apply(Registration.scala:74) 
    at akka.actor.LocalActorRef.invoke(ActorRef.scala:860) 
    at akka.dispatch.MessageInvocation.invoke(MessageHandling.scala:26) 
    at akka.dispatch.ExecutableMailbox$class.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:214) 
    at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:120) 
    at akka.dispatch.ExecutableMailbox$class.run(ExecutorBasedEventDrivenDispatcher.scala:186) 
    at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.run(ExecutorBasedEventDrivenDispatcher.scala:120) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
    at java.lang.Thread.run(Thread.java:680) 
    at akka.dispatch.MonitorableThread.run(ThreadPoolBuilder.scala:181) 
Caused by: java.lang.IllegalArgumentException: Failed to initialize GlobalStmInstance through System property 'org.multiverse.api.GlobalStmInstance.factoryMethod' with value 'org.multiverse.stms.alpha.AlphaStm'.'org.multiverse.stms.alpha.AlphaStm.createFast' is not an existing class (it can't be found using the Thread.currentThread.getContextClassLoader). 
    at org.multiverse.api.GlobalStmInstance.getMethod(GlobalStmInstance.java:82) 
    at org.multiverse.api.GlobalStmInstance.<clinit>(GlobalStmInstance.java:38) 
    ... 22 more 
Caused by: java.lang.ClassNotFoundException: org.multiverse.stms.alpha.AlphaStm 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 
    at org.multiverse.api.GlobalStmInstance.getMethod(GlobalStmInstance.java:76) 
    ... 23 more 

Cette partie du code est:

class MemoryUserDatabase extends UserDatabase { 
    import MemoryUserDatabase._ 

    def getUser(email: String) = atomic { 
    users.get.get(email) 
    } 
    def register(user: User) = atomic { 
    getUser(user.email) match { 
     case None => 
     users alter (_ + (user.email -> user)) 
     true 
     case Some(found) => false 
    } 
    } 
} 
object MemoryUserDatabase { 
    import scala.collection.mutable.{ Map => MutMap } 
    private val users = Ref(MutMap[String, User]()) 
} 

Je ne comprends pas pourquoi l'objet compagnon ne peut pas être initialisé. Le plus étrange est que vous pouvez faire la même chose, mais si dans la console du serveur (# 1) vous devez d'abord accéder au Compagnon Objet:

MemoryUserDatabase

avant d'exécuter le ServerRunner initialisées juste bien et par la suite tout le reste est juste dandy.

Quelqu'un peut-il expliquer pourquoi c'est?

Merci! Idan

PS. C'est mon premier code Scala alors essayez de ne pas trop rigoler ... et toute autre suggestion (stylistique, philisophique, théologique, ...) est la bienvenue.

package toy.service.user 

import scala.collection.mutable.HashMap 

import akka.actor.{ Actor, ActorRef } 
import akka.config.Supervision.{ OneForOneStrategy, Permanent } 
import Actor._ 
import akka.routing._ 
import akka.stm._ 
import akka.actor.TypedActor 
import akka.event.EventHandler 

class User(var email: String, 
      var password: String) extends Serializable 

/** Registration message types. 
*/ 
sealed trait RegistrationMessage 
case class Availability(email: String) extends RegistrationMessage 
case class GetUser(email: String) extends RegistrationMessage 
case class Register(user: User) extends RegistrationMessage 

// Client --------------------------------------- 
class UserClient(defaultTimeout: Int = 1000) { 
    val userService = Actor.remote.actorFor(UserService.USER_SERVICE_ID, "localhost", UserService.USER_SERVICE_PORT) 
    EventHandler.info(this, "remote UserService: id(" + userService.id + "), uuid(" + userService.uuid + ")") 

    def getUser(email: String, timeout: Int = defaultTimeout): Option[User] = (userService !! (GetUser(email), timeout)).as[User] 

    def available(email: String, timeout: Int = defaultTimeout): Boolean = 
    (userService !! (Availability(email), timeout)).as[Boolean].getOrElse(throw new RuntimeException("Oi!")) 

    def register(user: User, timeout: Int = defaultTimeout): Boolean = 
    (userService !! (Register(user), timeout)).as[Boolean].getOrElse(throw new RuntimeException("Got bogus (None) response from " + UserService.USER_SERVICE_ID)) 
} 

// Service Pool --------------------------------- 
object UserService { 
    val USER_SERVICE_ID = "user:service" 
    val USER_SERVICE_PORT = 2662 
    val host = "localhost" 
} 

class UserService extends Actor 
    with DefaultActorPool 
    with BoundedCapacityStrategy 
    with MailboxPressureCapacitor 
    with SmallestMailboxSelector 
    with BasicFilter { 

    import toy.service.user.memory._ 

    def receive = _route // DefaultActorPool's receive 
    def lowerBound = 1 
    def upperBound = 5 
    def pressureThreshold = 1 
    def partialFill = true // never send duplicate messages to same actor (only meaningful if selectionCount > 1) 
    def selectionCount = 1 // How many in pool should receive each message 
    def rampupRate = 0.1 // increase by 10% capacity (# num actors) 
    def backoffRate = 0.50 // halve capacity once backoffThreshold is reached 
    def backoffThreshold = 0.50 
    def instance = actorOf(new UserServer(new MemoryUserDatabase)) 

    override def preStart() { 
    import UserService.{ host, USER_SERVICE_ID, USER_SERVICE_PORT } 

    remote.start(host, UserService.USER_SERVICE_PORT); 
    remote.register(UserService.USER_SERVICE_ID, self) //Register the actorPool with the specified service id 
    EventHandler.info(this, "Prestart: Started UserService(" + self.uuid + ") on %s:%s".format(host, UserService.USER_SERVICE_PORT.toString())) 
    } 
} 

// Service -------------------------------------- 
class UserServer(db: UserDatabase) extends Actor { 

    def receive = { 
    case Availability(email) => self.reply(db.available(email)) 
    case GetUser(email)  => self.reply(db.getUser(email)) 
    case Register(user)  => self.reply(db.register(user)) 
    } 
} 

// Database ------------------------------------- 
trait UserDatabase { 
    def getUser(email: String): Option[User] 
    def register(user: User): Boolean 

    def available(email: String): Boolean = getUser(email) == None 
} 

package memory { 
    class MemoryUserDatabase extends UserDatabase { 
    import MemoryUserDatabase._ 

    def getUser(email: String) = atomic { 
     users.get.get(email) 
    } 

    def register(user: User) = atomic { 
     getUser(user.email) match { 
     case None => 
      users alter (_ + (user.email -> user)) 
      true 
     case Some(found) => false 
     } 
    } 
    } 

    object MemoryUserDatabase { 
    import scala.collection.mutable.{ Map => MutMap } 

    private val users = Ref(MutMap[String, User]()) 
    } 
} 

object ServerRunner { 
    def run() { 
    actorOf[UserService].start() 
    } 
} 

object ClientRunner { 
    def run { 
    val client = new UserClient 
    EventHandler.info(this, client.available("foo")) 
    } 
} 
+0

Scala 2.9.1 a été récemment publié. Peut-être donner un coup de feu là-bas? –

+0

Je pense que vous devez ajouter plus de trace de pile pour être utile. –

+2

En outre, vous utilisez partagé, unthreadsafe état, ce qui est un grand non- –

Répondre

1

J'ai eu un problème similaire quand je TMap un dans l'initialisation d'un constructeur d'un objet créé lors de l'initialisation de mon application. D'une certaine façon, mon objet était en cours de création avant l'initialisation des initialiseurs statiques STM. J'ai rendu le champ de mon objet paresseux et cela a réglé le problème.

... en fait, il a juste retardé le problème à plus tard dans le programme. Grrrr.

+0

Votre méthode fonctionne correctement. Après l'ajout du modificateur paresseux, le problème semble résolu. –

0

J'ai rencontré le même problème. L'ajout de la dépendance 'org.multiverse.multiverse-alpha-unborn' a résolu le problème pour moi.

Questions connexes