2011-08-31 10 views
10

Soy nuevo en Scala y Akka así que discúlpeme si esta es una pregunta nueva, pero no puedo encontrar la respuesta en ningún otro lado ...¿Por qué Scala no crea una instancia de un objeto complementario?

Para el registro Estoy usando Scala 2.9.0-1 y Akka 1.1.3 y también han incluido mi configuración de SBT 0.10.1.

He escrito el código que sigue a este mensaje como un experimento en Akka; es una versión de juguete de una base de datos de usuario e instalaciones de registro. La idea básica es que hay un ActorPool de actores de UserServer, cada uno de los cuales tiene una instancia de MemoryUserDatabase que usa STM para interactuar con un Mapa de Usuarios descodificado de las direcciones de correo electrónico de los usuarios, bastante simple, ¿no?

El problema puede ser reproducida por la elaboración del expediente y ejecutar el siguiente en dos consolas diferentes:

consola # 1: toy.service.user._

importación; ServiceRunner.run

consola # 2:

importación toy.service.user._; ClientRunner.run

Ésta es la salida de la consola del servidor (# 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) 

El error es ligeramente más interesante usando 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 

Esta parte operativa del código es:

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]()) 
} 

No entiendo por qué el objeto complementario no se puede inicializar. Lo más extraño es que se puede hacer lo mismo, pero si en la consola del servidor (# 1) por primera vez acceso al objeto Companion:

MemoryUserDatabase

antes de ejecutar el ServerRunner inicializa solo bien y, posteriormente, todo lo demás es simplemente elegante.

¿Alguien puede explicar por qué es esto?

Gracias! Idan

PS. Este es mi primer código de Scala, así que trata de no reírte demasiado ... y cualquier otra sugerencia (estilística, filosófica, teológica, ...) son bienvenidas.

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 fue lanzado recientemente. Tal vez darle una oportunidad allí? –

+0

Creo que necesitas agregar más rastros de la pila para que sean útiles. –

+2

Además, está utilizando estado compartido, no protegido, que es un gran no-no –

Respuesta

1

tuve un problema similar cuando estaba inicializar un TMap en un constructor de un objeto creado durante el arranque de mi aplicación. De alguna manera, mi objeto se estaba creando antes de que se ejecutaran los inicializadores estáticos de STM. Hice perezoso el campo de mi objeto y solucionó el problema.

... en realidad solo retrasó el problema para más adelante en el programa. Grrrr.

+0

Su método funciona bien. Después de agregar el modificador perezoso, el problema parece resuelto. –

0

Me encontré con el mismo problema. Agregar la dependencia 'org.multiverse.multiverse-alpha-unborn' resolvió el problema para mí.

Cuestiones relacionadas