私はScalaとAkkaを初めて使用するので、これが新しい質問である場合はご容赦ください。しかし、他のどこにも答えが見つかりません...
記録のために、私はScala2.9.0-1とAkka1.1.3を使用しており、SBT0.10.1セットアップも含めています。
このメッセージに続くコードは、Akkaでの実験として作成しました。これは、ユーザーデータベースと登録機能のおもちゃバージョンです。基本的な考え方は、UserServerアクターのActorPoolがあり、それぞれがSTMを使用してユーザーの電子メールアドレスをキーオフしたユーザーのマップと対話するMemoryUserDatabaseのインスタンスを持っているということです-非常に単純ですよね?
この問題は、ファイルをコンパイルし、2つの別々のコンソールで以下を実行することで再現できます。
コンソール#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)
このエラーは、Scala2.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]())
}
コンパニオンオブジェクトを初期化できない理由がわかりません。最も奇妙な部分は、同じことを実行できることですが、サーバーコンソール(#1)で最初にコンパニオンオブジェクトにアクセスする場合は、次のようになります。
MemoryUserDatabase
ServerRunnerを実行する前は、正常に初期化され、その後、他のすべてが正常に初期化されます。
誰かがこれがなぜであるか説明できますか?
ありがとう!イダン
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"))
}
}