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"))
}
}
Scala 2.9.1 fue lanzado recientemente. Tal vez darle una oportunidad allí? –
Creo que necesitas agregar más rastros de la pila para que sean útiles. –
Además, está utilizando estado compartido, no protegido, que es un gran no-no –