2011-08-31 62 views
10

我是新来Scala和阿卡所以请原谅我,如果这是对于新手的问题,但我找不到答案其他地方...为什么Scala无法实例化Companion对象?

对于我使用Scala的2.9.0-1和阿卡记录1.1.3,并包含了我的SBT 0.10.1设置。

我已经写下了这封邮件的代码,作为Akka的实验;它是用户数据库和注册设施的玩具版本。基本思想是有一个UserPool Actor ActorPool,每个ActorPool都有一个MemoryUserDatabase的实例,它使用STM与用户的电子邮件地址关联的用户图进行交互 - 非常简单,对吧?

该问题可以通过编译文件并运行在两个单独的控制台以下再现:

控制台#1:

进口toy.service.user._; ServiceRunner.run

控制台#2:

进口toy.service.user._; ClientRunner.run

这是从服务器控制台输出(#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) 

错误是使用稍微更有趣的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 

该执行部分的代码是:

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

我不明白为什么Companion对象无法初始化。最奇怪的是,你可以做同样的事情,但如果在服务器控制台(#1)你第一次访问协同对象:

运行ServerRunner之前它只是初始化

MemoryUserDatabase罚款,随后一切都只是花花公子。

任何人都可以解释为什么这是?

谢谢! Idan

PS。这是我的第一个Scala代码,所以尽量不要太难以发笑......并且欢迎任何其他建议(风格,哲学,神学......)。

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。也许在那里拍一下? –

+0

我认为你需要添加更多的堆栈跟踪以供任何使用。 –

+2

另外,你正在使用共享的,unthreadsafe状态,这是一个很大的禁止 –

回答

1

我在我的应用程序启动过程中创建的对象的构造函数中初始化TMap时遇到了类似的问题。不知何故,我的对象是在STM静态初始化器运行之前创建的。我让我的对象的领域很懒,并且解决了这个问题。

...实际上它只是将问题推迟到程序的后面。 GRRRR。

+0

你的方法工作正常。在添加懒惰修饰符后,问题似乎解决了。 –

0

我遇到了同样的问题。添加依赖'org.multiverse.multiverse-alpha-unborn'解决了我的问题。