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"))
}
}
Scala 2.9.1 a été récemment publié. Peut-être donner un coup de feu là-bas? –
Je pense que vous devez ajouter plus de trace de pile pour être utile. –
En outre, vous utilisez partagé, unthreadsafe état, ce qui est un grand non- –