Merge branch 'master' into promisestream
Conflicts: akka-actor/src/main/scala/akka/dispatch/Future.scala
This commit is contained in:
commit
42ec626b67
166 changed files with 10815 additions and 5403 deletions
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.testing
|
||||
|
||||
import akka.serialization.Serializer
|
||||
import com.google.protobuf.Message
|
||||
import org.codehaus.jackson.map.ObjectMapper
|
||||
import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, ByteArrayInputStream }
|
||||
import akka.util.ClassLoaderObjectInputStream
|
||||
import sjson.json._
|
||||
|
||||
class ProtobufSerializer extends Serializer {
|
||||
val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]])
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
if (!obj.isInstanceOf[Message]) throw new IllegalArgumentException(
|
||||
"Can't serialize a non-protobuf message using protobuf [" + obj + "]")
|
||||
obj.asInstanceOf[Message].toByteArray
|
||||
}
|
||||
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]], classLoader: Option[ClassLoader] = None): AnyRef = {
|
||||
if (!clazz.isDefined) throw new IllegalArgumentException(
|
||||
"Need a protobuf message class to be able to serialize bytes using protobuf")
|
||||
clazz.get.getDeclaredMethod("parseFrom", ARRAY_OF_BYTE_ARRAY: _*).invoke(null, bytes).asInstanceOf[Message]
|
||||
}
|
||||
}
|
||||
object ProtobufSerializer extends ProtobufSerializer
|
||||
|
||||
class JavaJSONSerializer extends Serializer {
|
||||
private val mapper = new ObjectMapper
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
val bos = new ByteArrayOutputStream
|
||||
val out = new ObjectOutputStream(bos)
|
||||
mapper.writeValue(out, obj)
|
||||
out.close
|
||||
bos.toByteArray
|
||||
}
|
||||
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]], classLoader: Option[ClassLoader] = None): AnyRef = {
|
||||
if (!clazz.isDefined) throw new IllegalArgumentException(
|
||||
"Can't deserialize JSON to instance if no class is provided")
|
||||
val in =
|
||||
if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes))
|
||||
else new ObjectInputStream(new ByteArrayInputStream(bytes))
|
||||
val obj = mapper.readValue(in, clazz.get).asInstanceOf[AnyRef]
|
||||
in.close
|
||||
obj
|
||||
}
|
||||
}
|
||||
object JavaJSONSerializer extends JavaJSONSerializer
|
||||
|
||||
class SJSONSerializer extends Serializer {
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] =
|
||||
sjson.json.Serializer.SJSON.out(obj)
|
||||
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]], cl: Option[ClassLoader] = None): AnyRef = {
|
||||
if (!clazz.isDefined) throw new IllegalArgumentException(
|
||||
"Can't deserialize JSON to instance if no class is provided")
|
||||
|
||||
import sjson.json.Serializer._
|
||||
val sj = new SJSON with DefaultConstructor { val classLoader = cl }
|
||||
sj.in(bytes, clazz.get.getName)
|
||||
}
|
||||
}
|
||||
object SJSONSerializer extends SJSONSerializer
|
||||
|
|
@ -23,7 +23,7 @@ object ActorFireForgetRequestReplySpec {
|
|||
case "Send" ⇒
|
||||
self.reply("Reply")
|
||||
case "SendImplicit" ⇒
|
||||
self.sender.get ! "ReplyImplicit"
|
||||
self.channel ! "ReplyImplicit"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import akka.testing._
|
|||
import akka.util.duration._
|
||||
import akka.testing.Testing.sleepFor
|
||||
import akka.config.Supervision.{ OneForOneStrategy }
|
||||
import akka.actor._
|
||||
import akka.dispatch.Future
|
||||
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||
import java.lang.IllegalStateException
|
||||
import akka.util.ReflectiveAccess
|
||||
|
||||
object ActorRefSpec {
|
||||
|
||||
|
|
@ -68,26 +69,189 @@ object ActorRefSpec {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OuterActor(val inner: ActorRef) extends Actor {
|
||||
def receive = {
|
||||
case "self" ⇒ self reply self
|
||||
case x ⇒ inner forward x
|
||||
}
|
||||
}
|
||||
|
||||
class FailingOuterActor(val inner: ActorRef) extends Actor {
|
||||
val fail = new InnerActor
|
||||
|
||||
def receive = {
|
||||
case "self" ⇒ self reply self
|
||||
case x ⇒ inner forward x
|
||||
}
|
||||
}
|
||||
|
||||
class FailingInheritingOuterActor(_inner: ActorRef) extends OuterActor(_inner) {
|
||||
val fail = new InnerActor
|
||||
}
|
||||
|
||||
class InnerActor extends Actor {
|
||||
def receive = {
|
||||
case "innerself" ⇒ self reply self
|
||||
case other ⇒ self reply other
|
||||
}
|
||||
}
|
||||
|
||||
class FailingInnerActor extends Actor {
|
||||
val fail = new InnerActor
|
||||
|
||||
def receive = {
|
||||
case "innerself" ⇒ self reply self
|
||||
case other ⇒ self reply other
|
||||
}
|
||||
}
|
||||
|
||||
class FailingInheritingInnerActor extends InnerActor {
|
||||
val fail = new InnerActor
|
||||
}
|
||||
}
|
||||
|
||||
class ActorRefSpec extends WordSpec with MustMatchers {
|
||||
import ActorRefSpec._
|
||||
import akka.actor.ActorRefSpec._
|
||||
|
||||
"An ActorRef" must {
|
||||
|
||||
"not allow Actors to be created outside of an actorOf" in {
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
new Actor { def receive = { case _ ⇒ } }
|
||||
fail("shouldn't get here")
|
||||
}
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
val a = Actor.actorOf(new Actor {
|
||||
Actor.actorOf(new Actor {
|
||||
val nested = new Actor { def receive = { case _ ⇒ } }
|
||||
def receive = { case _ ⇒ }
|
||||
}).start()
|
||||
fail("shouldn't get here")
|
||||
}
|
||||
|
||||
def refStackMustBeEmpty = Actor.actorRefInCreation.get.headOption must be === None
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingOuterActor(Actor.actorOf(new InnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new OuterActor(Actor.actorOf(new FailingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingInheritingOuterActor(Actor.actorOf(new InnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingOuterActor(Actor.actorOf(new FailingInheritingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingInheritingOuterActor(Actor.actorOf(new FailingInheritingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingInheritingOuterActor(Actor.actorOf(new FailingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new OuterActor(Actor.actorOf(new InnerActor {
|
||||
val a = new InnerActor
|
||||
}).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new FailingOuterActor(Actor.actorOf(new FailingInheritingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new OuterActor(Actor.actorOf(new FailingInheritingInnerActor).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
intercept[akka.actor.ActorInitializationException] {
|
||||
Actor.actorOf(new OuterActor(Actor.actorOf({ new InnerActor; new InnerActor }).start)).start()
|
||||
}
|
||||
|
||||
refStackMustBeEmpty
|
||||
|
||||
(intercept[java.lang.IllegalStateException] {
|
||||
Actor.actorOf(new OuterActor(Actor.actorOf({ throw new IllegalStateException("Ur state be b0rked"); new InnerActor }).start)).start()
|
||||
}).getMessage must be === "Ur state be b0rked"
|
||||
|
||||
refStackMustBeEmpty
|
||||
}
|
||||
|
||||
"be serializable using Java Serialization on local node" in {
|
||||
val a = Actor.actorOf[InnerActor].start
|
||||
|
||||
import java.io._
|
||||
|
||||
val baos = new ByteArrayOutputStream(8192 * 32)
|
||||
val out = new ObjectOutputStream(baos)
|
||||
|
||||
out.writeObject(a)
|
||||
|
||||
out.flush
|
||||
out.close
|
||||
|
||||
val in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray))
|
||||
val readA = in.readObject
|
||||
|
||||
a.isInstanceOf[LocalActorRef] must be === true
|
||||
readA.isInstanceOf[LocalActorRef] must be === true
|
||||
(readA eq a) must be === true
|
||||
}
|
||||
|
||||
"must throw exception on deserialize if not present in local registry and remoting is not enabled" in {
|
||||
ReflectiveAccess.RemoteModule.isEnabled must be === false
|
||||
|
||||
val a = Actor.actorOf[InnerActor].start
|
||||
|
||||
val inetAddress = ReflectiveAccess.RemoteModule.configDefaultAddress
|
||||
|
||||
val expectedSerializedRepresentation = SerializedActorRef(
|
||||
a.uuid,
|
||||
a.address,
|
||||
inetAddress.getAddress.getHostAddress,
|
||||
inetAddress.getPort,
|
||||
a.timeout)
|
||||
|
||||
Actor.registry.unregister(a)
|
||||
|
||||
import java.io._
|
||||
|
||||
val baos = new ByteArrayOutputStream(8192 * 32)
|
||||
val out = new ObjectOutputStream(baos)
|
||||
|
||||
out.writeObject(a)
|
||||
|
||||
out.flush
|
||||
out.close
|
||||
|
||||
val in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray))
|
||||
(intercept[java.lang.IllegalStateException] {
|
||||
in.readObject
|
||||
}).getMessage must be === "Trying to deserialize ActorRef (" + expectedSerializedRepresentation + ") but it's not found in the local registry and remoting is not enabled!"
|
||||
}
|
||||
|
||||
"support nested actorOfs" in {
|
||||
|
|
@ -96,12 +260,23 @@ class ActorRefSpec extends WordSpec with MustMatchers {
|
|||
def receive = { case _ ⇒ self reply nested }
|
||||
}).start()
|
||||
|
||||
val nested = (a !! "any").get.asInstanceOf[ActorRef]
|
||||
val nested = (a ? "any").as[ActorRef].get
|
||||
a must not be null
|
||||
nested must not be null
|
||||
(a ne nested) must be === true
|
||||
}
|
||||
|
||||
"support advanced nested actorOfs" in {
|
||||
val a = Actor.actorOf(new OuterActor(Actor.actorOf(new InnerActor).start)).start
|
||||
val inner = (a ? "innerself").as[Any].get
|
||||
|
||||
(a ? a).as[ActorRef].get must be(a)
|
||||
(a ? "self").as[ActorRef].get must be(a)
|
||||
inner must not be a
|
||||
|
||||
(a ? "msg").as[String] must be === Some("msg")
|
||||
}
|
||||
|
||||
"support reply via channel" in {
|
||||
val serverRef = Actor.actorOf[ReplyActor].start()
|
||||
val clientRef = Actor.actorOf(new SenderActor(serverRef)).start()
|
||||
|
|
@ -135,11 +310,11 @@ class ActorRefSpec extends WordSpec with MustMatchers {
|
|||
}
|
||||
}).start()
|
||||
|
||||
val ffive: Future[String] = ref !!! 5
|
||||
val fnull: Future[String] = ref !!! null
|
||||
val ffive = (ref ? 5).mapTo[String]
|
||||
val fnull = (ref ? null).mapTo[String]
|
||||
|
||||
intercept[ActorKilledException] {
|
||||
ref !! PoisonPill
|
||||
(ref ? PoisonPill).get
|
||||
fail("shouldn't get here")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package akka.actor
|
||||
|
||||
import org.scalatest.{ WordSpec, BeforeAndAfterAll }
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.testkit.TestKit
|
||||
import akka.dispatch.FutureTimeoutException
|
||||
import akka.util.duration._
|
||||
|
||||
class ActorTimeoutSpec
|
||||
extends WordSpec
|
||||
with BeforeAndAfterAll
|
||||
with MustMatchers
|
||||
with TestKit {
|
||||
|
||||
val echo = Actor.actorOf(new Actor {
|
||||
def receive = {
|
||||
case x ⇒
|
||||
}
|
||||
}).start()
|
||||
|
||||
val testTimeout = if (Actor.defaultTimeout.duration < 400.millis) 500 millis else 100 millis
|
||||
|
||||
override def afterAll { echo.stop() }
|
||||
|
||||
"An Actor-based Future" must {
|
||||
|
||||
"use the global default timeout if no implicit in scope" in {
|
||||
echo.timeout = 12
|
||||
within((Actor.TIMEOUT - 100).millis, (Actor.TIMEOUT + 300).millis) {
|
||||
val f = echo ? "hallo"
|
||||
intercept[FutureTimeoutException] { f.await }
|
||||
}
|
||||
}
|
||||
|
||||
"use implicitly supplied timeout" in {
|
||||
implicit val timeout = Actor.Timeout(testTimeout)
|
||||
within(testTimeout - 100.millis, testTimeout + 300.millis) {
|
||||
val f = (echo ? "hallo").mapTo[String]
|
||||
intercept[FutureTimeoutException] { f.await }
|
||||
f.value must be(None)
|
||||
}
|
||||
}
|
||||
|
||||
"use explicitly supplied timeout" in {
|
||||
within(testTimeout - 100.millis, testTimeout + 300.millis) {
|
||||
(echo.?("hallo", testTimeout)).as[String] must be(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ object Chameneos {
|
|||
}
|
||||
} else {
|
||||
waitingChameneo.foreach(_ ! Exit)
|
||||
self.sender.get ! Exit
|
||||
self.channel ! Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ class DeployerSpec extends WordSpec with MustMatchers {
|
|||
Clustered(
|
||||
Node("node1"),
|
||||
Replicate(3),
|
||||
Stateless))))
|
||||
Replication(
|
||||
TransactionLog,
|
||||
WriteThrough)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,7 @@ object ForwardActorSpec {
|
|||
val latch = TestLatch()
|
||||
val forwardActor = actorOf[ForwardActor]
|
||||
forwardActor.start()
|
||||
(forwardActor !! "SendBangBang") match {
|
||||
case Some(_) ⇒ latch.countDown()
|
||||
case None ⇒ {}
|
||||
}
|
||||
forwardActor ? "SendBangBang" onComplete { _ ⇒ latch.countDown() }
|
||||
def receive = {
|
||||
case _ ⇒ {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ object SupervisorSpec {
|
|||
}
|
||||
|
||||
override def receive = {
|
||||
case Die ⇒ temp !! (Die, TimeoutMillis)
|
||||
case Die ⇒ (temp.?(Die, TimeoutMillis)).get
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,13 +200,13 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach
|
|||
}
|
||||
|
||||
def ping(pingPongActor: ActorRef) = {
|
||||
(pingPongActor !! (Ping, TimeoutMillis)).getOrElse("nil") must be(PongMessage)
|
||||
messageLogPoll must be(PingMessage)
|
||||
(pingPongActor.?(Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage
|
||||
messageLogPoll must be === PingMessage
|
||||
}
|
||||
|
||||
def kill(pingPongActor: ActorRef) = {
|
||||
intercept[RuntimeException] { pingPongActor !! (Die, TimeoutMillis) }
|
||||
messageLogPoll must be(ExceptionMessage)
|
||||
messageLogPoll must be === ExceptionMessage
|
||||
}
|
||||
|
||||
"A supervisor" must {
|
||||
|
|
@ -215,7 +215,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach
|
|||
val master = actorOf[Master].start()
|
||||
|
||||
intercept[RuntimeException] {
|
||||
master !! (Die, TimeoutMillis)
|
||||
(master.?(Die, TimeoutMillis)).get
|
||||
}
|
||||
|
||||
sleepFor(1 second)
|
||||
|
|
@ -226,7 +226,7 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach
|
|||
val (temporaryActor, supervisor) = temporaryActorAllForOne
|
||||
|
||||
intercept[RuntimeException] {
|
||||
temporaryActor !! (Die, TimeoutMillis)
|
||||
(temporaryActor.?(Die, TimeoutMillis)).get
|
||||
}
|
||||
|
||||
sleepFor(1 second)
|
||||
|
|
@ -374,13 +374,13 @@ class SupervisorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach
|
|||
Supervise(dyingActor, Permanent) :: Nil))
|
||||
|
||||
intercept[Exception] {
|
||||
dyingActor !! (Die, TimeoutMillis)
|
||||
(dyingActor.?(Die, TimeoutMillis)).get
|
||||
}
|
||||
|
||||
// give time for restart
|
||||
sleepFor(3 seconds)
|
||||
|
||||
(dyingActor !! (Ping, TimeoutMillis)).getOrElse("nil") must be(PongMessage)
|
||||
(dyingActor.?(Ping, TimeoutMillis)).as[String].getOrElse("nil") must be === PongMessage
|
||||
|
||||
inits.get must be(3)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,8 @@ class ConfigSpec extends WordSpec with MustMatchers {
|
|||
getInt("akka.actor.throughput") must equal(Some(5))
|
||||
getInt("akka.actor.throughput-deadline-time") must equal(Some(-1))
|
||||
|
||||
getString("akka.remote.layer") must equal(Some("akka.remote.netty.NettyRemoteSupport"))
|
||||
getString("akka.remote.server.hostname") must equal(Some("localhost"))
|
||||
getInt("akka.remote.server.port") must equal(Some(2552))
|
||||
getString("akka.cluster.layer") must equal(Some("akka.remote.netty.NettyRemoteSupport"))
|
||||
getInt("akka.cluster.server.port") must equal(Some(2552))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import org.junit.Test
|
|||
import org.scalatest.Assertions._
|
||||
import akka.testing._
|
||||
import akka.dispatch._
|
||||
import akka.actor.{ ActorRef, Actor }
|
||||
import akka.actor.Actor._
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.{ ConcurrentHashMap, CountDownLatch, TimeUnit }
|
||||
import akka.actor.dispatch.ActorModelSpec.MessageDispatcherInterceptor
|
||||
import akka.util.{ Duration, Switch }
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import akka.actor.{ ActorKilledException, PoisonPill, ActorRef, Actor }
|
||||
|
||||
object ActorModelSpec {
|
||||
|
||||
|
|
@ -342,6 +342,22 @@ abstract class ActorModelSpec extends JUnitSuite {
|
|||
assertDispatcher(dispatcher)(starts = run, stops = run)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def dispatcherShouldCompleteAllUncompletedSenderFuturesOnDeregister {
|
||||
implicit val dispatcher = newInterceptedDispatcher
|
||||
val a = newTestActor.start()
|
||||
dispatcher.suspend(a)
|
||||
val f1: Future[String] = a ? Reply("foo") mapTo manifest[String]
|
||||
val stopped = a ? PoisonPill
|
||||
val shouldBeCompleted = for (i ← 1 to 10) yield a ? Reply(i)
|
||||
dispatcher.resume(a)
|
||||
assert(f1.get === "foo")
|
||||
stopped.await
|
||||
for (each ← shouldBeCompleted)
|
||||
assert(each.exception.get.isInstanceOf[ActorKilledException])
|
||||
a.stop()
|
||||
}
|
||||
}
|
||||
|
||||
class DispatcherModelTest extends ActorModelSpec {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class DispatcherActorSpec extends JUnitSuite {
|
|||
@Test
|
||||
def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
val result = (actor !! ("Hello", 10000)).as[String]
|
||||
val result = (actor.?("Hello", 10000)).as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop()
|
||||
}
|
||||
|
|
@ -53,8 +53,8 @@ class DispatcherActorSpec extends JUnitSuite {
|
|||
@Test
|
||||
def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
val result = (actor ? "Hello").as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ class DispatcherActorSpec extends JUnitSuite {
|
|||
def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
try {
|
||||
actor !! "Failure"
|
||||
(actor ? "Failure").get
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e ⇒
|
||||
|
|
@ -40,7 +40,7 @@ class FutureSpec extends JUnitSuite {
|
|||
def shouldActorReplyResultThroughExplicitFuture {
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start()
|
||||
val future = actor !!! "Hello"
|
||||
val future = actor ? "Hello"
|
||||
future.await
|
||||
assert(future.result.isDefined)
|
||||
assert("World" === future.result.get)
|
||||
|
|
@ -51,7 +51,7 @@ class FutureSpec extends JUnitSuite {
|
|||
def shouldActorReplyExceptionThroughExplicitFuture {
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start()
|
||||
val future = actor !!! "Failure"
|
||||
val future = actor ? "Failure"
|
||||
future.await
|
||||
assert(future.exception.isDefined)
|
||||
assert("Expected exception; to test fault-tolerance" === future.exception.get.getMessage)
|
||||
|
|
@ -62,22 +62,8 @@ class FutureSpec extends JUnitSuite {
|
|||
def shouldFutureCompose {
|
||||
val actor1 = actorOf[TestActor].start()
|
||||
val actor2 = actorOf(new Actor { def receive = { case s: String ⇒ self reply s.toUpperCase } }).start()
|
||||
val future1 = actor1 !!! "Hello" flatMap ((s: String) ⇒ actor2 !!! s)
|
||||
val future2 = actor1 !!! "Hello" flatMap (actor2 !!! (_: String))
|
||||
val future3 = actor1 !!! "Hello" flatMap (actor2 !!! (_: Int))
|
||||
assert((future1.get: Any) === "WORLD")
|
||||
assert((future2.get: Any) === "WORLD")
|
||||
intercept[ClassCastException] { future3.get }
|
||||
actor1.stop()
|
||||
actor2.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldFutureComposePatternMatch {
|
||||
val actor1 = actorOf[TestActor].start()
|
||||
val actor2 = actorOf(new Actor { def receive = { case s: String ⇒ self reply s.toUpperCase } }).start()
|
||||
val future1 = actor1 !!! "Hello" collect { case (s: String) ⇒ s } flatMap (actor2 !!! _)
|
||||
val future2 = actor1 !!! "Hello" collect { case (n: Int) ⇒ n } flatMap (actor2 !!! _)
|
||||
val future1 = actor1 ? "Hello" flatMap { _ match { case s: String ⇒ actor2 ? s } }
|
||||
val future2 = actor1 ? "Hello" flatMap { _ match { case i: Int ⇒ actor2 ? i } }
|
||||
assert((future1.get: Any) === "WORLD")
|
||||
intercept[MatchError] { future2.get }
|
||||
actor1.stop()
|
||||
|
|
@ -93,18 +79,18 @@ class FutureSpec extends JUnitSuite {
|
|||
}
|
||||
}).start()
|
||||
|
||||
val future0 = actor !!! "Hello"
|
||||
val future0 = actor ? "Hello"
|
||||
|
||||
val future1 = for {
|
||||
a: Int ← future0 // returns 5
|
||||
b: String ← actor !!! a // returns "10"
|
||||
c: String ← actor !!! 7 // returns "14"
|
||||
a: Int ← future0.mapTo[Int] // returns 5
|
||||
b: String ← (actor ? a).mapTo[String] // returns "10"
|
||||
c: String ← (actor ? 7).mapTo[String] // returns "14"
|
||||
} yield b + "-" + c
|
||||
|
||||
val future2 = for {
|
||||
a: Int ← future0
|
||||
b: Int ← actor !!! a
|
||||
c: String ← actor !!! 7
|
||||
a: Int ← future0.mapTo[Int]
|
||||
b: Int ← (actor ? a).mapTo[Int]
|
||||
c: String ← (actor ? 7).mapTo[String]
|
||||
} yield b + "-" + c
|
||||
|
||||
assert(future1.get === "10-14")
|
||||
|
|
@ -124,15 +110,15 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
|
||||
val future1 = for {
|
||||
Res(a: Int) ← actor !!! Req("Hello")
|
||||
Res(b: String) ← actor !!! Req(a)
|
||||
Res(c: String) ← actor !!! Req(7)
|
||||
Res(a: Int) ← actor ? Req("Hello")
|
||||
Res(b: String) ← actor ? Req(a)
|
||||
Res(c: String) ← actor ? Req(7)
|
||||
} yield b + "-" + c
|
||||
|
||||
val future2 = for {
|
||||
Res(a: Int) ← actor !!! Req("Hello")
|
||||
Res(b: Int) ← actor !!! Req(a)
|
||||
Res(c: Int) ← actor !!! Req(7)
|
||||
Res(a: Int) ← actor ? Req("Hello")
|
||||
Res(b: Int) ← actor ? Req(a)
|
||||
Res(c: Int) ← actor ? Req(7)
|
||||
} yield b + "-" + c
|
||||
|
||||
assert(future1.get === "10-14")
|
||||
|
|
@ -146,30 +132,30 @@ class FutureSpec extends JUnitSuite {
|
|||
val future2 = future1 map (_ / 0)
|
||||
val future3 = future2 map (_.toString)
|
||||
|
||||
val future4 = future1 failure {
|
||||
val future4 = future1 recover {
|
||||
case e: ArithmeticException ⇒ 0
|
||||
} map (_.toString)
|
||||
|
||||
val future5 = future2 failure {
|
||||
val future5 = future2 recover {
|
||||
case e: ArithmeticException ⇒ 0
|
||||
} map (_.toString)
|
||||
|
||||
val future6 = future2 failure {
|
||||
val future6 = future2 recover {
|
||||
case e: MatchError ⇒ 0
|
||||
} map (_.toString)
|
||||
|
||||
val future7 = future3 failure { case e: ArithmeticException ⇒ "You got ERROR" }
|
||||
val future7 = future3 recover { case e: ArithmeticException ⇒ "You got ERROR" }
|
||||
|
||||
val actor = actorOf[TestActor].start()
|
||||
|
||||
val future8 = actor !!! "Failure"
|
||||
val future9 = actor !!! "Failure" failure {
|
||||
val future8 = actor ? "Failure"
|
||||
val future9 = actor ? "Failure" recover {
|
||||
case e: RuntimeException ⇒ "FAIL!"
|
||||
}
|
||||
val future10 = actor !!! "Hello" failure {
|
||||
val future10 = actor ? "Hello" recover {
|
||||
case e: RuntimeException ⇒ "FAIL!"
|
||||
}
|
||||
val future11 = actor !!! "Failure" failure { case _ ⇒ "Oops!" }
|
||||
val future11 = actor ? "Failure" recover { case _ ⇒ "Oops!" }
|
||||
|
||||
assert(future1.get === 5)
|
||||
intercept[ArithmeticException] { future2.get }
|
||||
|
|
@ -194,7 +180,7 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!, timeout) }
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] }
|
||||
assert(Futures.fold(0, timeout)(futures)(_ + _).await.result.get === 45)
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +191,7 @@ class FutureSpec extends JUnitSuite {
|
|||
def receive = { case (add: Int, wait: Int) ⇒ Thread.sleep(wait); self reply_? add }
|
||||
}).start()
|
||||
}
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!, 10000) }
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), 10000).mapTo[Int] }
|
||||
assert(futures.foldLeft(Future(0))((fr, fa) ⇒ for (r ← fr; a ← fa) yield (r + a)).get === 45)
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +208,7 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!, timeout) }
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] }
|
||||
assert(Futures.fold(0, timeout)(futures)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +225,7 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!, timeout) }
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 200), timeout).mapTo[Int] }
|
||||
assert(Futures.reduce(futures, timeout)(_ + _).get === 45)
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +242,7 @@ class FutureSpec extends JUnitSuite {
|
|||
}).start()
|
||||
}
|
||||
val timeout = 10000
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.!!, timeout) }
|
||||
def futures = actors.zipWithIndex map { case (actor: ActorRef, idx: Int) ⇒ actor.?((idx, idx * 100), timeout).mapTo[Int] }
|
||||
assert(Futures.reduce(futures, timeout)(_ + _).await.exception.get.getMessage === "shouldFoldResultsWithException: expected")
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +255,7 @@ class FutureSpec extends JUnitSuite {
|
|||
def receiveShouldExecuteOnComplete {
|
||||
val latch = new StandardLatch
|
||||
val actor = actorOf[TestActor].start()
|
||||
actor !!! "Hello" receive { case "World" ⇒ latch.open }
|
||||
actor ? "Hello" onResult { case "World" ⇒ latch.open }
|
||||
assert(latch.tryAwait(5, TimeUnit.SECONDS))
|
||||
actor.stop()
|
||||
}
|
||||
|
|
@ -285,7 +271,7 @@ class FutureSpec extends JUnitSuite {
|
|||
}
|
||||
}).start()
|
||||
|
||||
val oddFutures: List[Future[Int]] = List.fill(100)(oddActor !!! 'GetNext)
|
||||
val oddFutures = List.fill(100)(oddActor ? 'GetNext mapTo manifest[Int])
|
||||
assert(Future.sequence(oddFutures).get.sum === 10000)
|
||||
oddActor.stop()
|
||||
|
||||
|
|
@ -304,13 +290,13 @@ class FutureSpec extends JUnitSuite {
|
|||
val latch = new StandardLatch
|
||||
val f2 = Future { latch.tryAwait(5, TimeUnit.SECONDS); "success" }
|
||||
f2 foreach (_ ⇒ throw new ThrowableTest("dispatcher foreach"))
|
||||
f2 receive { case _ ⇒ throw new ThrowableTest("dispatcher receive") }
|
||||
f2 onResult { case _ ⇒ throw new ThrowableTest("dispatcher receive") }
|
||||
val f3 = f2 map (s ⇒ s.toUpperCase)
|
||||
latch.open
|
||||
f2.await
|
||||
assert(f2.resultOrException === Some("success"))
|
||||
f2 foreach (_ ⇒ throw new ThrowableTest("current thread foreach"))
|
||||
f2 receive { case _ ⇒ throw new ThrowableTest("current thread receive") }
|
||||
f2 onResult { case _ ⇒ throw new ThrowableTest("current thread receive") }
|
||||
f3.await
|
||||
assert(f3.resultOrException === Some("SUCCESS"))
|
||||
|
||||
|
|
@ -342,9 +328,9 @@ class FutureSpec extends JUnitSuite {
|
|||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x flatMap (actor !!! _)
|
||||
val y = x flatMap (actor ? _) mapTo manifest[String]
|
||||
|
||||
val r = flow(x() + " " + y[String]() + "!")
|
||||
val r = flow(x() + " " + y() + "!")
|
||||
|
||||
assert(r.get === "Hello World!")
|
||||
|
||||
|
|
@ -370,9 +356,9 @@ class FutureSpec extends JUnitSuite {
|
|||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future(3)
|
||||
val y = actor !!! "Hello"
|
||||
val y = (actor ? "Hello").mapTo[Int]
|
||||
|
||||
val r = flow(x() + y[Int](), 100)
|
||||
val r = flow(x() + y(), 100)
|
||||
|
||||
intercept[ClassCastException](r.get)
|
||||
}
|
||||
|
|
@ -384,7 +370,7 @@ class FutureSpec extends JUnitSuite {
|
|||
val actor = actorOf[TestActor].start
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = actor !!! "Hello"
|
||||
val y = actor ? "Hello" mapTo manifest[Nothing]
|
||||
|
||||
val r = flow(x() + y())
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.junit.runner.RunWith
|
||||
import akka.actor.{ Actor, ActorRegistry }
|
||||
import akka.actor.{ Actor, ActorRegistry, NullChannel }
|
||||
import akka.actor.Actor.{ actorOf }
|
||||
import java.util.concurrent.{ TimeUnit, CountDownLatch, BlockingQueue }
|
||||
import java.util.{ Queue }
|
||||
|
|
@ -84,7 +84,7 @@ abstract class MailboxSpec extends WordSpec with MustMatchers with BeforeAndAfte
|
|||
new MessageInvocation(
|
||||
actorOf(new Actor { //Dummy actor
|
||||
def receive = { case _ ⇒ }
|
||||
}), msg, None, None)
|
||||
}), msg, NullChannel)
|
||||
}
|
||||
|
||||
def ensureInitialMailboxState(config: MailboxType, q: MessageQueue) {
|
||||
|
|
@ -158,4 +158,4 @@ class PriorityMailboxSpec extends MailboxSpec {
|
|||
case UnboundedMailbox() ⇒ new UnboundedPriorityMessageQueue(comparator)
|
||||
case BoundedMailbox(capacity, pushTimeOut) ⇒ new BoundedPriorityMessageQueue(capacity, pushTimeOut, comparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import akka.dispatch.Dispatchers
|
|||
import akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object ThreadBasedActorSpec {
|
||||
object PinnedActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newPinnedDispatcher(self)
|
||||
|
||||
|
|
@ -21,8 +21,8 @@ object ThreadBasedActorSpec {
|
|||
}
|
||||
}
|
||||
|
||||
class ThreadBasedActorSpec extends JUnitSuite {
|
||||
import ThreadBasedActorSpec._
|
||||
class PinnedActorSpec extends JUnitSuite {
|
||||
import PinnedActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ class ThreadBasedActorSpec extends JUnitSuite {
|
|||
@Test
|
||||
def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
val result = (actor !! ("Hello", 10000)).as[String]
|
||||
val result = (actor.?("Hello", 10000)).as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop()
|
||||
}
|
||||
|
|
@ -51,8 +51,8 @@ class ThreadBasedActorSpec extends JUnitSuite {
|
|||
@Test
|
||||
def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
val result = (actor ? "Hello").as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop()
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class ThreadBasedActorSpec extends JUnitSuite {
|
|||
def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start()
|
||||
try {
|
||||
actor !! "Failure"
|
||||
(actor ? "Failure").get
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e ⇒
|
||||
|
|
@ -44,7 +44,7 @@ class PriorityDispatcherSpec extends WordSpec with MustMatchers {
|
|||
|
||||
dispatcher.resume(actor) //Signal the actor to start treating it's message backlog
|
||||
|
||||
actor.!!![List[Int]]('Result).await.result.get must be === (msgs.reverse)
|
||||
actor.?('Result).as[List[Int]].get must be === (msgs.reverse)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class ActorRegistrySpec extends JUnitSuite {
|
|||
val actor2 = actorOf[TestActor]("test-actor-2").start
|
||||
val results = new ConcurrentLinkedQueue[Future[String]]
|
||||
|
||||
Actor.registry.local.foreach(actor ⇒ results.add(actor.!!))
|
||||
Actor.registry.local.foreach(actor ⇒ results.add(actor.?("ping").mapTo[String]))
|
||||
|
||||
assert(results.size === 2)
|
||||
val i = results.iterator
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ import akka.testing._
|
|||
import akka.testing.Testing.{ sleepFor, testMillis }
|
||||
import akka.util.duration._
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.routing._
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.dispatch.{ KeptPromise, Future }
|
||||
import akka.actor.{ TypedActor, Actor }
|
||||
|
||||
object RoutingSpec {
|
||||
trait Foo {
|
||||
|
|
@ -55,10 +55,11 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
case Test3 ⇒ t2
|
||||
}.start()
|
||||
|
||||
implicit val timeout = Actor.Timeout(testMillis(5 seconds))
|
||||
val result = for {
|
||||
a ← (d !! (Test1, testMillis(5 seconds))).as[Int]
|
||||
b ← (d !! (Test2, testMillis(5 seconds))).as[Int]
|
||||
c ← (d !! (Test3, testMillis(5 seconds))).as[Int]
|
||||
a ← (d ? (Test1)).as[Int]
|
||||
b ← (d ? (Test2)).as[Int]
|
||||
c ← (d ? (Test3)).as[Int]
|
||||
} yield a + b + c
|
||||
|
||||
result.isDefined must be(true)
|
||||
|
|
@ -166,38 +167,6 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
|
||||
for (a ← List(broadcast, a1, a2, a3)) a.stop()
|
||||
}
|
||||
|
||||
"be defined at" in {
|
||||
import akka.actor.ActorRef
|
||||
|
||||
val Yes = "yes"
|
||||
val No = "no"
|
||||
|
||||
def testActor() = actorOf(new Actor() {
|
||||
def receive = {
|
||||
case Yes ⇒ "yes"
|
||||
}
|
||||
}).start()
|
||||
|
||||
val t1 = testActor()
|
||||
val t2 = testActor()
|
||||
val t3 = testActor()
|
||||
val t4 = testActor()
|
||||
|
||||
val d1 = loadBalancerActor(new SmallestMailboxFirstIterator(t1 :: t2 :: Nil))
|
||||
val d2 = loadBalancerActor(new CyclicIterator[ActorRef](t3 :: t4 :: Nil))
|
||||
|
||||
t1.isDefinedAt(Yes) must be(true)
|
||||
t1.isDefinedAt(No) must be(false)
|
||||
t2.isDefinedAt(Yes) must be(true)
|
||||
t2.isDefinedAt(No) must be(false)
|
||||
d1.isDefinedAt(Yes) must be(true)
|
||||
d1.isDefinedAt(No) must be(false)
|
||||
d2.isDefinedAt(Yes) must be(true)
|
||||
d2.isDefinedAt(No) must be(false)
|
||||
|
||||
for (a ← List(t1, t2, d1, d2)) a.stop()
|
||||
}
|
||||
}
|
||||
|
||||
"Actor Pool" must {
|
||||
|
|
@ -225,11 +194,11 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
}).start()
|
||||
|
||||
val successes = TestLatch(2)
|
||||
val successCounter = Some(actorOf(new Actor {
|
||||
val successCounter = actorOf(new Actor {
|
||||
def receive = {
|
||||
case "success" ⇒ successes.countDown()
|
||||
}
|
||||
}).start())
|
||||
}).start()
|
||||
|
||||
implicit val replyTo = successCounter
|
||||
pool ! "a"
|
||||
|
|
@ -240,7 +209,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
|
||||
count.get must be(2)
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
||||
pool.stop()
|
||||
}
|
||||
|
|
@ -269,7 +238,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
}).start()
|
||||
|
||||
try {
|
||||
(for (count ← 1 to 500) yield pool.!!) foreach {
|
||||
(for (count ← 1 to 500) yield pool.?("Test", 20000)) foreach {
|
||||
_.await.resultOrException.get must be("Response")
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -308,14 +277,14 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
|
||||
pool ! 1
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
||||
var loops = 0
|
||||
def loop(t: Int) = {
|
||||
latch = TestLatch(loops)
|
||||
count.set(0)
|
||||
for (m ← 0 until loops) {
|
||||
pool !!! t
|
||||
pool ? t
|
||||
sleepFor(50 millis)
|
||||
}
|
||||
}
|
||||
|
|
@ -328,7 +297,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
latch.await
|
||||
count.get must be(loops)
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
||||
// a whole bunch should max it out
|
||||
|
||||
|
|
@ -337,7 +306,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
latch.await
|
||||
count.get must be(loops)
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(4)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(4)
|
||||
|
||||
pool.stop()
|
||||
}
|
||||
|
|
@ -385,7 +354,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
latch.await
|
||||
count.get must be(loops)
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be(2)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be(2)
|
||||
|
||||
// send a bunch over the theshold and observe an increment
|
||||
loops = 15
|
||||
|
|
@ -394,7 +363,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
latch.await(10 seconds)
|
||||
count.get must be(loops)
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be >= (3)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be >= (3)
|
||||
|
||||
pool.stop()
|
||||
}
|
||||
|
|
@ -490,7 +459,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
|
||||
sleepFor(5 millis)
|
||||
|
||||
val z = (pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size
|
||||
val z = (pool ? ActorPool.Stat).as[ActorPool.Stats].get.size
|
||||
|
||||
z must be >= (2)
|
||||
|
||||
|
|
@ -501,7 +470,7 @@ class RoutingSpec extends WordSpec with MustMatchers {
|
|||
sleepFor(500 millis)
|
||||
}
|
||||
|
||||
(pool !! ActorPool.Stat).asInstanceOf[Option[ActorPool.Stats]].get.size must be <= (z)
|
||||
(pool ? ActorPool.Stat).as[ActorPool.Stats].get.size must be <= (z)
|
||||
|
||||
pool.stop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.serialization
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import akka.serialization.Serialization._
|
||||
import scala.reflect._
|
||||
|
||||
object SerializeSpec {
|
||||
@BeanInfo
|
||||
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
||||
@BeanInfo
|
||||
case class Person(name: String, age: Int, address: Address) { def this() = this("", 0, null) }
|
||||
|
||||
case class Record(id: Int, person: Person)
|
||||
}
|
||||
|
||||
class SerializeSpec extends JUnitSuite {
|
||||
import SerializeSpec._
|
||||
|
||||
@Test
|
||||
def shouldSerializeAddress {
|
||||
val addr = Address("120", "Monroe Street", "Santa Clara", "95050")
|
||||
val b = serialize(addr) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(bytes) ⇒ bytes
|
||||
}
|
||||
deserialize(b.asInstanceOf[Array[Byte]], classOf[Address], None) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(add) ⇒ assert(add === addr)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldSerializePerson {
|
||||
val person = Person("debasish ghosh", 25, Address("120", "Monroe Street", "Santa Clara", "95050"))
|
||||
val b = serialize(person) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(bytes) ⇒ bytes
|
||||
}
|
||||
deserialize(b.asInstanceOf[Array[Byte]], classOf[Person], None) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(p) ⇒ assert(p === person)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldSerializeRecordWithDefaultSerializer {
|
||||
val person = Person("debasish ghosh", 25, Address("120", "Monroe Street", "Santa Clara", "95050"))
|
||||
val r = Record(100, person)
|
||||
val b = serialize(r) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(bytes) ⇒ bytes
|
||||
}
|
||||
deserialize(b.asInstanceOf[Array[Byte]], classOf[Record], None) match {
|
||||
case Left(exception) ⇒ fail(exception)
|
||||
case Right(p) ⇒ assert(p === r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
akka {
|
||||
actor {
|
||||
serializers {
|
||||
java = "akka.serialization.JavaSerializer"
|
||||
proto = "akka.testing.ProtobufSerializer"
|
||||
sjson = "akka.testing.SJSONSerializer"
|
||||
default = "akka.serialization.JavaSerializer"
|
||||
}
|
||||
|
||||
bindings {
|
||||
java = ["akka.serialization.SerializeSpec$Address", "akka.serialization.MyJavaSerializableActor", "akka.serialization.MyStatelessActorWithMessagesInMailbox", "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
||||
sjson = ["akka.serialization.SerializeSpec$Person"]
|
||||
proto = ["com.google.protobuf.Message", "akka.actor.ProtobufProtocol$MyMessage"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,10 @@ class CallingThreadDispatcherModelSpec extends ActorModelSpec {
|
|||
}
|
||||
}
|
||||
|
||||
override def dispatcherShouldCompleteAllUncompletedSenderFuturesOnDeregister {
|
||||
//Can't handle this...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: set ts=2 sw=2 et:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
|
||||
class Ticket703Spec extends WordSpec with MustMatchers {
|
||||
|
||||
"A !!! call to an actor pool" should {
|
||||
"A ? call to an actor pool" should {
|
||||
"reuse the proper timeout" in {
|
||||
val actorPool = actorOf(
|
||||
new Actor with DefaultActorPool with BoundedCapacityStrategy with MailboxPressureCapacitor with SmallestMailboxSelector with BasicNoBackoffFilter {
|
||||
|
|
@ -28,7 +28,7 @@ class Ticket703Spec extends WordSpec with MustMatchers {
|
|||
}
|
||||
})
|
||||
}).start()
|
||||
(actorPool.!!).await.result must be === Some("Response")
|
||||
(actorPool.?("Ping", 7000)).await.result must be === Some("Response")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,20 @@ import akka.config._
|
|||
import Config._
|
||||
import akka.util.{ ListenerManagement, ReflectiveAccess, Duration, Helpers }
|
||||
import ReflectiveAccess._
|
||||
import Helpers.{ narrow, narrowSilently }
|
||||
import akka.remoteinterface.RemoteSupport
|
||||
import akka.japi.{ Creator, Procedure }
|
||||
import akka.AkkaException
|
||||
import akka.serialization.{ Format, Serializer }
|
||||
import akka.cluster.ClusterNode
|
||||
import akka.event.EventHandler
|
||||
import scala.collection.immutable.Stack
|
||||
|
||||
import scala.reflect.BeanProperty
|
||||
|
||||
import com.eaio.uuid.UUID
|
||||
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Life-cycle messages for the Actors
|
||||
|
|
@ -72,10 +73,10 @@ case object Kill extends AutoReceivedMessage with LifeCycleMessage
|
|||
case object ReceiveTimeout extends LifeCycleMessage
|
||||
|
||||
case class MaximumNumberOfRestartsWithinTimeRangeReached(
|
||||
@BeanProperty val victim: ActorRef,
|
||||
@BeanProperty val maxNrOfRetries: Option[Int],
|
||||
@BeanProperty val withinTimeRange: Option[Int],
|
||||
@BeanProperty val lastExceptionCausingRestart: Throwable) extends LifeCycleMessage
|
||||
@BeanProperty victim: ActorRef,
|
||||
@BeanProperty maxNrOfRetries: Option[Int],
|
||||
@BeanProperty withinTimeRange: Option[Int],
|
||||
@BeanProperty lastExceptionCausingRestart: Throwable) extends LifeCycleMessage
|
||||
|
||||
// Exceptions for Actors
|
||||
class ActorStartException private[akka] (message: String, cause: Throwable = null) extends AkkaException(message, cause)
|
||||
|
|
@ -88,8 +89,11 @@ class InvalidMessageException private[akka] (message: String, cause: Throwable =
|
|||
/**
|
||||
* This message is thrown by default when an Actors behavior doesn't match a message
|
||||
*/
|
||||
case class UnhandledMessageException(msg: Any, ref: ActorRef) extends Exception {
|
||||
override def getMessage = "Actor %s does not handle [%s]".format(ref, msg)
|
||||
case class UnhandledMessageException(msg: Any, ref: ActorRef = null) extends Exception {
|
||||
// constructor with 'null' ActorRef needed to work with client instantiation of remote exception
|
||||
override def getMessage =
|
||||
if (ref ne null) "Actor %s does not handle [%s]".format(ref, msg)
|
||||
else "Actor does not handle [%s]".format(msg)
|
||||
override def fillInStackTrace() = this //Don't waste cycles generating stack trace
|
||||
}
|
||||
|
||||
|
|
@ -109,9 +113,6 @@ object Status {
|
|||
*/
|
||||
object Actor extends ListenerManagement {
|
||||
|
||||
private[akka] val TIMEOUT = Duration(config.getInt("akka.actor.timeout", 5), TIME_UNIT).toMillis
|
||||
private[akka] val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false)
|
||||
|
||||
/**
|
||||
* A Receive is a convenience type that defines actor message behavior currently modeled as
|
||||
* a PartialFunction[Any, Unit].
|
||||
|
|
@ -131,14 +132,31 @@ object Actor extends ListenerManagement {
|
|||
subclassAudits synchronized { subclassAudits.clear() }
|
||||
}
|
||||
}
|
||||
Runtime.getRuntime.addShutdownHook(new Thread(hook))
|
||||
Runtime.getRuntime.addShutdownHook(new Thread(hook, "akka-shutdown-hook"))
|
||||
hook
|
||||
}
|
||||
|
||||
private[actor] val actorRefInCreation = new ThreadLocal[Option[ActorRef]] {
|
||||
override def initialValue = None
|
||||
private[actor] val actorRefInCreation = new ThreadLocal[Stack[ActorRef]] {
|
||||
override def initialValue = Stack[ActorRef]()
|
||||
}
|
||||
|
||||
case class Timeout(duration: Duration) {
|
||||
def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
|
||||
def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
|
||||
}
|
||||
object Timeout {
|
||||
def apply(timeout: Long) = new Timeout(timeout)
|
||||
def apply(length: Long, unit: TimeUnit) = new Timeout(length, unit)
|
||||
implicit def durationToTimeout(duration: Duration) = new Timeout(duration)
|
||||
implicit def intToTimeout(timeout: Int) = new Timeout(timeout)
|
||||
implicit def longToTimeout(timeout: Long) = new Timeout(timeout)
|
||||
}
|
||||
|
||||
private[akka] val TIMEOUT = Duration(config.getInt("akka.actor.timeout", 5), TIME_UNIT).toMillis
|
||||
val defaultTimeout = Timeout(TIMEOUT)
|
||||
val noTimeoutGiven = Timeout(-123456789)
|
||||
private[akka] val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false)
|
||||
|
||||
/**
|
||||
* Handle to the ActorRegistry.
|
||||
*/
|
||||
|
|
@ -159,9 +177,6 @@ object Actor extends ListenerManagement {
|
|||
*/
|
||||
private[akka] lazy val remote: RemoteSupport = cluster.remoteService
|
||||
|
||||
// start up a cluster node to join the ZooKeeper cluster
|
||||
//if (ClusterModule.isEnabled) cluster.start()
|
||||
|
||||
/**
|
||||
* Creates an ActorRef out of the Actor with type T.
|
||||
* <pre>
|
||||
|
|
@ -272,7 +287,7 @@ object Actor extends ListenerManagement {
|
|||
* </pre>
|
||||
*/
|
||||
def actorOf[T <: Actor](creator: ⇒ T, address: String): ActorRef = {
|
||||
createActor(address, () ⇒ new LocalActorRef(() ⇒ creator, address))
|
||||
createActor(address, () ⇒ new LocalActorRef(() ⇒ creator, address, Transient))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -295,7 +310,7 @@ object Actor extends ListenerManagement {
|
|||
* JAVA API
|
||||
*/
|
||||
def actorOf[T <: Actor](creator: Creator[T], address: String): ActorRef = {
|
||||
createActor(address, () ⇒ new LocalActorRef(() ⇒ creator.create, address))
|
||||
createActor(address, () ⇒ new LocalActorRef(() ⇒ creator.create, address, Transient))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -323,25 +338,6 @@ object Actor extends ListenerManagement {
|
|||
}).start() ! Spawn
|
||||
}
|
||||
|
||||
/**
|
||||
* Implicitly converts the given Option[Any] to a AnyOptionAsTypedOption which offers the method <code>as[T]</code>
|
||||
* to convert an Option[Any] to an Option[T].
|
||||
*/
|
||||
implicit def toAnyOptionAsTypedOption(anyOption: Option[Any]) = new AnyOptionAsTypedOption(anyOption)
|
||||
|
||||
/**
|
||||
* Implicitly converts the given Future[_] to a AnyOptionAsTypedOption which offers the method <code>as[T]</code>
|
||||
* to convert an Option[Any] to an Option[T].
|
||||
* This means that the following code is equivalent:
|
||||
* (actor !! "foo").as[Int] (Deprecated)
|
||||
* and
|
||||
* (actor !!! "foo").as[Int] (Recommended)
|
||||
*/
|
||||
implicit def futureToAnyOptionAsTypedOption(anyFuture: Future[_]) = new AnyOptionAsTypedOption({
|
||||
try { anyFuture.await } catch { case t: FutureTimeoutException ⇒ }
|
||||
anyFuture.resultOrException
|
||||
})
|
||||
|
||||
private[akka] def createActor(address: String, actorFactory: () ⇒ ActorRef): ActorRef = {
|
||||
Address.validate(address)
|
||||
registry.actorFor(address) match { // check if the actor for the address is already in the registry
|
||||
|
|
@ -377,18 +373,27 @@ object Actor extends ListenerManagement {
|
|||
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
||||
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", cause)
|
||||
}
|
||||
}, address)
|
||||
}, address, Transient)
|
||||
}
|
||||
|
||||
private def newClusterActorRef(factory: () ⇒ ActorRef, address: String, deploy: Deploy): ActorRef = {
|
||||
deploy match {
|
||||
case Deploy(_, router, serializerClassName, Clustered(home, replication: Replication, state: State)) ⇒
|
||||
case Deploy(
|
||||
configAdress, router, serializerClassName,
|
||||
Clustered(
|
||||
home,
|
||||
replicas,
|
||||
replication)) ⇒
|
||||
|
||||
ClusterModule.ensureEnabled()
|
||||
if (!Actor.remote.isRunning) throw new IllegalStateException("Remote server is not running")
|
||||
|
||||
if (configAdress != address) throw new IllegalStateException(
|
||||
"Deployment config for [" + address + "] is wrong [" + deploy + "]")
|
||||
if (!Actor.remote.isRunning) throw new IllegalStateException(
|
||||
"Remote server is not running")
|
||||
|
||||
val isHomeNode = DeploymentConfig.isHomeNode(home)
|
||||
val replicas = DeploymentConfig.replicaValueFor(replication)
|
||||
val nrOfReplicas = DeploymentConfig.replicaValueFor(replicas)
|
||||
|
||||
def serializerErrorDueTo(reason: String) =
|
||||
throw new akka.config.ConfigurationException(
|
||||
|
|
@ -396,42 +401,34 @@ object Actor extends ListenerManagement {
|
|||
"] for serialization of actor [" + address +
|
||||
"] since " + reason)
|
||||
|
||||
val serializer: Serializer = {
|
||||
if ((serializerClassName eq null) ||
|
||||
(serializerClassName == "") ||
|
||||
(serializerClassName == Format.defaultSerializerName)) {
|
||||
Format.Default
|
||||
} else {
|
||||
val clazz: Class[_] = ReflectiveAccess.getClassFor(serializerClassName) match {
|
||||
case Right(clazz) ⇒ clazz
|
||||
case Left(exception) ⇒
|
||||
val cause = exception match {
|
||||
case i: InvocationTargetException ⇒ i.getTargetException
|
||||
case _ ⇒ exception
|
||||
}
|
||||
serializerErrorDueTo(cause.toString)
|
||||
}
|
||||
val f = clazz.newInstance.asInstanceOf[AnyRef]
|
||||
if (f.isInstanceOf[Serializer]) f.asInstanceOf[Serializer]
|
||||
else serializerErrorDueTo("class must be of type [akka.serialization.Serializer]")
|
||||
}
|
||||
}
|
||||
val serializer: Serializer =
|
||||
akka.serialization.Serialization.getSerializer(this.getClass).fold(x ⇒ serializerErrorDueTo(x.toString), s ⇒ s)
|
||||
|
||||
if (isHomeNode) { // home node for clustered actor
|
||||
cluster
|
||||
.use(address, serializer)
|
||||
.getOrElse(throw new ConfigurationException(
|
||||
"Could not check out actor [" + address + "] from cluster registry as a \"local\" actor"))
|
||||
|
||||
} else {
|
||||
if (!cluster.isClustered(address)) {
|
||||
cluster.store(factory().start(), replicas, false, serializer) // add actor to cluster registry (if not already added)
|
||||
}
|
||||
def storeActorAndGetClusterRef(replicationScheme: ReplicationScheme, serializer: Serializer): ActorRef = {
|
||||
// add actor to cluster registry (if not already added)
|
||||
if (!cluster.isClustered(address))
|
||||
cluster.store(factory().start(), nrOfReplicas, replicationScheme, false, serializer)
|
||||
|
||||
// remote node (not home node), check out as ClusterActorRef
|
||||
cluster.ref(address, DeploymentConfig.routerTypeFor(router))
|
||||
}
|
||||
|
||||
replication match {
|
||||
case _: Transient | Transient ⇒
|
||||
storeActorAndGetClusterRef(Transient, serializer)
|
||||
|
||||
case replication: Replication ⇒
|
||||
if (isHomeNode) { // stateful actor's home node
|
||||
cluster
|
||||
.use(address, serializer)
|
||||
.getOrElse(throw new ConfigurationException(
|
||||
"Could not check out actor [" + address + "] from cluster registry as a \"local\" actor"))
|
||||
} else {
|
||||
// FIXME later manage different 'storage' (data grid) as well
|
||||
storeActorAndGetClusterRef(replication, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
case invalid ⇒ throw new IllegalActorStateException(
|
||||
"Could not create actor with address [" + address +
|
||||
"], not bound to a valid deployment scheme [" + invalid + "]")
|
||||
|
|
@ -455,7 +452,7 @@ object Actor extends ListenerManagement {
|
|||
*
|
||||
* <p/>
|
||||
* Here you find functions like:
|
||||
* - !, !!, !!! and forward
|
||||
* - !, ? and forward
|
||||
* - link, unlink, startLink etc
|
||||
* - start, stop
|
||||
* - etc.
|
||||
|
|
@ -492,33 +489,40 @@ trait Actor {
|
|||
*/
|
||||
type Receive = Actor.Receive
|
||||
|
||||
/*
|
||||
/**
|
||||
* Some[ActorRef] representation of the 'self' ActorRef reference.
|
||||
* <p/>
|
||||
* Mainly for internal use, functions as the implicit sender references when invoking
|
||||
* the 'forward' function.
|
||||
*/
|
||||
@transient
|
||||
implicit val someSelf: Some[ActorRef] = {
|
||||
val optRef = Actor.actorRefInCreation.get
|
||||
if (optRef.isEmpty) throw new ActorInitializationException(
|
||||
"ActorRef for instance of actor [" + getClass.getName + "] is not in scope." +
|
||||
"\n\tYou can not create an instance of an actor explicitly using 'new MyActor'." +
|
||||
val someSelf: Some[ActorRef] = {
|
||||
val refStack = Actor.actorRefInCreation.get
|
||||
if (refStack.isEmpty) throw new ActorInitializationException(
|
||||
"\n\tYou can not create an instance of an " + getClass.getName + " explicitly using 'new MyActor'." +
|
||||
"\n\tYou have to use one of the factory methods in the 'Actor' object to create a new actor." +
|
||||
"\n\tEither use:" +
|
||||
"\n\t\t'val actor = Actor.actorOf[MyActor]', or" +
|
||||
"\n\t\t'val actor = Actor.actorOf(new MyActor(..))'")
|
||||
Actor.actorRefInCreation.set(None)
|
||||
optRef.asInstanceOf[Some[ActorRef]]
|
||||
|
||||
val ref = refStack.head
|
||||
|
||||
if (ref eq null)
|
||||
throw new ActorInitializationException("Trying to create an instance of " + getClass.getName + " outside of a wrapping 'actorOf'")
|
||||
else {
|
||||
// Push a null marker so any subsequent calls to new Actor doesn't reuse this actor ref
|
||||
Actor.actorRefInCreation.set(refStack.push(null))
|
||||
Some(ref)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Option[ActorRef] representation of the 'self' ActorRef reference.
|
||||
* <p/>
|
||||
* Mainly for internal use, functions as the implicit sender references when invoking
|
||||
* one of the message send functions ('!', '!!' and '!!!').
|
||||
* one of the message send functions ('!' and '?').
|
||||
*/
|
||||
implicit def optionSelf: Option[ActorRef] = someSelf
|
||||
def optionSelf: Option[ActorRef] = someSelf
|
||||
|
||||
/**
|
||||
* The 'self' field holds the ActorRef for this actor.
|
||||
|
|
@ -548,7 +552,7 @@ trait Actor {
|
|||
* </pre>
|
||||
*/
|
||||
@transient
|
||||
val self: ScalaActorRef = someSelf.get
|
||||
implicit val self: ScalaActorRef = someSelf.get
|
||||
|
||||
/**
|
||||
* User overridable callback/setting.
|
||||
|
|
@ -611,21 +615,6 @@ trait Actor {
|
|||
throw new UnhandledMessageException(msg, self)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the actor able to handle the message passed in as arguments?
|
||||
*/
|
||||
def isDefinedAt(message: Any): Boolean = {
|
||||
val behaviorStack = self.hotswap
|
||||
message match { //Same logic as apply(msg) but without the unhandled catch-all
|
||||
case l: AutoReceivedMessage ⇒ true
|
||||
case msg if behaviorStack.nonEmpty &&
|
||||
behaviorStack.head.isDefinedAt(msg) ⇒ true
|
||||
case msg if behaviorStack.isEmpty &&
|
||||
processingBehavior.isDefinedAt(msg) ⇒ true
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
|
||||
* Puts the behavior on top of the hotswap stack.
|
||||
|
|
@ -650,14 +639,13 @@ trait Actor {
|
|||
|
||||
private[akka] final def apply(msg: Any) = {
|
||||
if (msg.isInstanceOf[AnyRef] && (msg.asInstanceOf[AnyRef] eq null))
|
||||
throw new InvalidMessageException("Message from [" + self.sender + "] to [" + self.toString + "] is null")
|
||||
throw new InvalidMessageException("Message from [" + self.channel + "] to [" + self.toString + "] is null")
|
||||
val behaviorStack = self.hotswap
|
||||
|
||||
msg match {
|
||||
case l: AutoReceivedMessage ⇒ autoReceiveMessage(l)
|
||||
case msg if behaviorStack.nonEmpty &&
|
||||
behaviorStack.head.isDefinedAt(msg) ⇒ behaviorStack.head.apply(msg)
|
||||
case msg if behaviorStack.isEmpty &&
|
||||
processingBehavior.isDefinedAt(msg) ⇒ processingBehavior.apply(msg)
|
||||
case msg if behaviorStack.nonEmpty && behaviorStack.head.isDefinedAt(msg) ⇒ behaviorStack.head.apply(msg)
|
||||
case msg if behaviorStack.isEmpty && processingBehavior.isDefinedAt(msg) ⇒ processingBehavior.apply(msg)
|
||||
case unknown ⇒ unhandled(unknown) //This is the only line that differs from processingbehavior
|
||||
}
|
||||
}
|
||||
|
|
@ -673,35 +661,11 @@ trait Actor {
|
|||
case Restart(reason) ⇒ throw reason
|
||||
case Kill ⇒ throw new ActorKilledException("Kill")
|
||||
case PoisonPill ⇒
|
||||
val f = self.senderFuture()
|
||||
val ch = self.channel
|
||||
self.stop()
|
||||
if (f.isDefined) f.get.completeWithException(new ActorKilledException("PoisonPill"))
|
||||
ch.sendException(new ActorKilledException("PoisonPill"))
|
||||
}
|
||||
}
|
||||
|
||||
private lazy val processingBehavior = receive //ProcessingBehavior is the original behavior
|
||||
}
|
||||
|
||||
private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) {
|
||||
|
||||
/**
|
||||
* Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException
|
||||
* if the actual type is not assignable from the given one.
|
||||
*/
|
||||
def as[T]: Option[T] = narrow[T](anyOption)
|
||||
|
||||
/**
|
||||
* Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible
|
||||
* ClassCastException and return None in that case.
|
||||
*/
|
||||
def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption)
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker interface for proxyable actors (such as typed actor).
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait Proxyable {
|
||||
private[actor] def swapProxiedActor(newInstance: Actor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@ package akka.actor
|
|||
|
||||
import akka.event.EventHandler
|
||||
import akka.dispatch._
|
||||
import akka.config.Config
|
||||
import akka.config._
|
||||
import akka.config.Supervision._
|
||||
import akka.util._
|
||||
import akka.serialization.{ Format, Serializer }
|
||||
import ReflectiveAccess._
|
||||
import ClusterModule._
|
||||
import DeploymentConfig.{ ReplicationScheme, Replication, Transient, WriteThrough, WriteBehind }
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
|
@ -19,6 +22,7 @@ import java.util.{ Map ⇒ JMap }
|
|||
import scala.reflect.BeanProperty
|
||||
import scala.collection.immutable.Stack
|
||||
import scala.annotation.tailrec
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
private[akka] object ActorRefInternals {
|
||||
|
||||
|
|
@ -32,27 +36,6 @@ private[akka] object ActorRefInternals {
|
|||
object SHUTDOWN extends StatusType
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction for unification of sender and senderFuture for later reply.
|
||||
* Can be stored away and used at a later point in time.
|
||||
*/
|
||||
abstract class Channel[T] {
|
||||
|
||||
/**
|
||||
* Scala API. <p/>
|
||||
* Sends the specified message to the channel.
|
||||
*/
|
||||
def !(msg: T)
|
||||
|
||||
/**
|
||||
* Java API. <p/>
|
||||
* Sends the specified message to the channel.
|
||||
*/
|
||||
def sendOneWay(msg: T) {
|
||||
this.!(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ActorRef is an immutable and serializable handle to an Actor.
|
||||
* <p/>
|
||||
|
|
@ -85,20 +68,20 @@ abstract class Channel[T] {
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scalaRef: ScalaActorRef ⇒
|
||||
trait ActorRef extends ActorRefShared with ForwardableChannel with java.lang.Comparable[ActorRef] with Serializable { scalaRef: ScalaActorRef ⇒
|
||||
// Only mutable for RemoteServer in order to maintain identity across nodes
|
||||
@volatile
|
||||
protected[akka] var _uuid = newUuid
|
||||
@volatile
|
||||
protected[this] var _status: ActorRefInternals.StatusType = ActorRefInternals.UNSTARTED
|
||||
|
||||
val address: String
|
||||
def address: String
|
||||
|
||||
/**
|
||||
* User overridable callback/setting.
|
||||
* <p/>
|
||||
* Defines the default timeout for '!!' and '!!!' invocations,
|
||||
* e.g. the timeout for the future returned by the call to '!!' and '!!!'.
|
||||
* Defines the default timeout for '?'/'ask' invocations,
|
||||
* e.g. the timeout for the future returned by the call to '?'/'ask'.
|
||||
*/
|
||||
@deprecated("Will be replaced by implicit-scoped timeout on all methods that needs it, will default to timeout specified in config", "1.1")
|
||||
@BeanProperty
|
||||
|
|
@ -206,7 +189,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
/**
|
||||
* Akka Java API. <p/>
|
||||
* The reference sender future of the last received message.
|
||||
* Is defined if the message was sent with sent with '!!' or '!!!', else None.
|
||||
* Is defined if the message was sent with sent with '?'/'ask', else None.
|
||||
*/
|
||||
def getSenderFuture: Option[Promise[Any]] = senderFuture
|
||||
|
||||
|
|
@ -233,12 +216,6 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
*/
|
||||
def isUnstarted: Boolean = _status == ActorRefInternals.UNSTARTED
|
||||
|
||||
/**
|
||||
* Is the actor able to handle the message passed in as arguments?
|
||||
*/
|
||||
@deprecated("Will be removed without replacement, it's just not reliable in the face of `become` and `unbecome`", "1.1")
|
||||
def isDefinedAt(message: Any): Boolean = actor.isDefinedAt(message)
|
||||
|
||||
/**
|
||||
* Only for internal use. UUID is effectively final.
|
||||
*/
|
||||
|
|
@ -248,82 +225,17 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
|
||||
* <p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message);
|
||||
* </pre>
|
||||
* <p/>
|
||||
*/
|
||||
def sendOneWay(message: AnyRef): Unit = {
|
||||
sendOneWay(message, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* Sends a one-way asynchronous message. E.g. fire-and-forget semantics.
|
||||
* <p/>
|
||||
* Allows you to pass along the sender of the message.
|
||||
* <p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message, context);
|
||||
* </pre>
|
||||
* <p/>
|
||||
*/
|
||||
def sendOneWay(message: AnyRef, sender: ActorRef) {
|
||||
this.!(message)(Option(sender))
|
||||
}
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef)
|
||||
* Uses the default timeout of the Actor (setTimeout()) and omits the sender reference
|
||||
*/
|
||||
def sendRequestReply(message: AnyRef): AnyRef = sendRequestReply(message, timeout, null)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* @see sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef)
|
||||
* Uses the default timeout of the Actor (setTimeout())
|
||||
*/
|
||||
def sendRequestReply(message: AnyRef, sender: ActorRef): AnyRef = sendRequestReply(message, timeout, sender)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* Sends a message asynchronously and waits on a future for a reply message under the hood.
|
||||
* <p/>
|
||||
* It waits on the reply either until it receives it or until the timeout expires
|
||||
* (which will throw an ActorTimeoutException). E.g. send-and-receive-eventually semantics.
|
||||
* <p/>
|
||||
* <b>NOTE:</b>
|
||||
* Use this method with care. In most cases it is better to use 'sendOneWay' together with 'getContext().getSender()' to
|
||||
* implement request/response message exchanges.
|
||||
* <p/>
|
||||
* If you are sending messages using <code>sendRequestReply</code> then you <b>have to</b> use <code>getContext().reply(..)</code>
|
||||
* to send a reply message to the original sender. If not then the sender will block until the timeout expires.
|
||||
*/
|
||||
def sendRequestReply(message: AnyRef, timeout: Long, sender: ActorRef): AnyRef = {
|
||||
!!(message, timeout)(Option(sender)).getOrElse(throw new ActorTimeoutException(
|
||||
"Message [" + message +
|
||||
"]\n\tfrom [" + (if (sender ne null) sender.address else "nowhere") +
|
||||
"]\n\twith timeout [" + timeout +
|
||||
"]\n\ttimed out."))
|
||||
.asInstanceOf[AnyRef]
|
||||
}
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_]
|
||||
* @see ask(message: AnyRef, sender: ActorRef): Future[_]
|
||||
* Uses the Actors default timeout (setTimeout()) and omits the sender
|
||||
*/
|
||||
def sendRequestReplyFuture[T <: AnyRef](message: AnyRef): Future[T] = sendRequestReplyFuture(message, timeout, null).asInstanceOf[Future[T]]
|
||||
def ask(message: AnyRef): Future[AnyRef] = ask(message, timeout, null)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* @see sendRequestReplyFuture(message: AnyRef, sender: ActorRef): Future[_]
|
||||
* @see ask(message: AnyRef, sender: ActorRef): Future[_]
|
||||
* Uses the Actors default timeout (setTimeout())
|
||||
*/
|
||||
def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, sender: ActorRef): Future[T] = sendRequestReplyFuture(message, timeout, sender).asInstanceOf[Future[T]]
|
||||
def ask(message: AnyRef, sender: ActorRef): Future[AnyRef] = ask(message, timeout, sender)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
|
|
@ -333,10 +245,11 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
* Use this method with care. In most cases it is better to use 'sendOneWay' together with the 'getContext().getSender()' to
|
||||
* implement request/response message exchanges.
|
||||
* <p/>
|
||||
* If you are sending messages using <code>sendRequestReplyFuture</code> then you <b>have to</b> use <code>getContext().reply(..)</code>
|
||||
* If you are sending messages using <code>ask</code> then you <b>have to</b> use <code>getContext().reply(..)</code>
|
||||
* to send a reply message to the original sender. If not then the sender will block until the timeout expires.
|
||||
*/
|
||||
def sendRequestReplyFuture[T <: AnyRef](message: AnyRef, timeout: Long, sender: ActorRef): Future[T] = !!!(message, timeout)(Option(sender)).asInstanceOf[Future[T]]
|
||||
def ask(message: AnyRef, timeout: Long, sender: ActorRef): Future[AnyRef] =
|
||||
?(message, Actor.Timeout(timeout))(sender).asInstanceOf[Future[AnyRef]]
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
|
|
@ -344,7 +257,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
*/
|
||||
def forward(message: AnyRef, sender: ActorRef) {
|
||||
if (sender eq null) throw new IllegalArgumentException("The 'sender' argument to 'forward' can't be null")
|
||||
else forward(message)(Some(sender))
|
||||
else forward(message)(sender)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -415,17 +328,6 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
*/
|
||||
def startLink(actorRef: ActorRef): ActorRef
|
||||
|
||||
/**
|
||||
* Returns the mailbox size.
|
||||
*/
|
||||
def mailboxSize = dispatcher.mailboxSize(this)
|
||||
|
||||
/**
|
||||
* Akka Java API. <p/>
|
||||
* Returns the mailbox size.
|
||||
*/
|
||||
def getMailboxSize: Int = mailboxSize
|
||||
|
||||
/**
|
||||
* Returns the supervisor, if there is one.
|
||||
*/
|
||||
|
|
@ -453,36 +355,36 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
/**
|
||||
* Abstraction for unification of sender and senderFuture for later reply
|
||||
*/
|
||||
def channel: Channel[Any] = {
|
||||
if (senderFuture.isDefined) {
|
||||
new Channel[Any] {
|
||||
val future = senderFuture.get
|
||||
def !(msg: Any) = future completeWithResult msg
|
||||
}
|
||||
} else if (sender.isDefined) {
|
||||
val someSelf = Some(this)
|
||||
new Channel[Any] {
|
||||
val client = sender.get
|
||||
def !(msg: Any) = client.!(msg)(someSelf)
|
||||
}
|
||||
} else throw new IllegalActorStateException("No channel available")
|
||||
def channel: UntypedChannel = {
|
||||
val msg = currentMessage
|
||||
if (msg ne null) msg.channel
|
||||
else NullChannel
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of ForwardableChannel
|
||||
*/
|
||||
|
||||
def sendException(ex: Throwable) {}
|
||||
def isUsableOnlyOnce = false
|
||||
def isUsable = true
|
||||
def isReplyable = true
|
||||
def canSendException = false
|
||||
|
||||
/**
|
||||
* Java API. <p/>
|
||||
* Abstraction for unification of sender and senderFuture for later reply
|
||||
*/
|
||||
def getChannel: Channel[Any] = channel
|
||||
def getChannel: UntypedChannel = channel
|
||||
|
||||
protected[akka] def invoke(messageHandle: MessageInvocation)
|
||||
|
||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef])
|
||||
protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit
|
||||
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout(
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[Promise[T]]): Promise[T]
|
||||
channel: UntypedChannel): Future[Any]
|
||||
|
||||
protected[akka] def actorInstance: AtomicReference[Actor]
|
||||
|
||||
|
|
@ -506,7 +408,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
that.asInstanceOf[ActorRef].uuid == uuid
|
||||
}
|
||||
|
||||
override def toString = "Actor[" + address + ":" + uuid + "]"
|
||||
override def toString = "Actor[%s:%s]".format(address, uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -514,27 +416,82 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor, val address: String)
|
||||
class LocalActorRef private[akka] (
|
||||
private[this] val actorFactory: () ⇒ Actor,
|
||||
val address: String,
|
||||
replicationScheme: ReplicationScheme)
|
||||
extends ActorRef with ScalaActorRef {
|
||||
|
||||
protected[akka] val guard = new ReentrantGuard
|
||||
|
||||
@volatile
|
||||
protected[akka] var _futureTimeout: Option[ScheduledFuture[AnyRef]] = None
|
||||
|
||||
@volatile
|
||||
private[akka] lazy val _linkedActors = new ConcurrentHashMap[Uuid, ActorRef]
|
||||
|
||||
@volatile
|
||||
private[akka] var _supervisor: Option[ActorRef] = None
|
||||
|
||||
@volatile
|
||||
private var maxNrOfRetriesCount: Int = 0
|
||||
|
||||
@volatile
|
||||
private var restartTimeWindowStartNanos: Long = 0L
|
||||
|
||||
@volatile
|
||||
private var _mailbox: AnyRef = _
|
||||
|
||||
@volatile
|
||||
private[akka] var _dispatcher: MessageDispatcher = Dispatchers.defaultGlobalDispatcher
|
||||
|
||||
protected[akka] val actorInstance = guard.withGuard { new AtomicReference[Actor](newActor) }
|
||||
|
||||
private val isReplicated: Boolean = replicationScheme match {
|
||||
case _: Transient | Transient ⇒ false
|
||||
case _ ⇒ true
|
||||
}
|
||||
|
||||
// FIXME how to get the matching serializerClassName? Now default is used. Needed for transaction log snapshot
|
||||
// private val serializer = Actor.serializerFor(address, Format.defaultSerializerName)
|
||||
|
||||
def serializerErrorDueTo(reason: String) =
|
||||
throw new akka.config.ConfigurationException(
|
||||
"Could not create Serializer object [" + this.getClass.getName +
|
||||
"]")
|
||||
|
||||
private val serializer: Serializer =
|
||||
akka.serialization.Serialization.getSerializer(this.getClass).fold(x ⇒ serializerErrorDueTo(x.toString), s ⇒ s)
|
||||
|
||||
private lazy val replicationStorage: Either[TransactionLog, AnyRef] = {
|
||||
replicationScheme match {
|
||||
case _: Transient | Transient ⇒
|
||||
throw new IllegalStateException("Can not replicate 'transient' actor [" + toString + "]")
|
||||
|
||||
case Replication(storage, strategy) ⇒
|
||||
val isWriteBehind = strategy match {
|
||||
case _: WriteBehind | WriteBehind ⇒ true
|
||||
case _: WriteThrough | WriteThrough ⇒ false
|
||||
}
|
||||
|
||||
storage match {
|
||||
case _: DeploymentConfig.TransactionLog | DeploymentConfig.TransactionLog ⇒
|
||||
EventHandler.debug(this,
|
||||
"Creating a transaction log for Actor [%s] with replication strategy [%s]"
|
||||
.format(address, replicationScheme))
|
||||
// Left(transactionLog.newLogFor(_uuid.toString, isWriteBehind, replicationScheme, serializer))
|
||||
// to fix null
|
||||
Left(transactionLog.newLogFor(_uuid.toString, isWriteBehind, replicationScheme, null))
|
||||
|
||||
case _: DeploymentConfig.DataGrid | DeploymentConfig.DataGrid ⇒
|
||||
throw new ConfigurationException("Replication storage type \"data-grid\" is not yet supported")
|
||||
|
||||
case unknown ⇒
|
||||
throw new ConfigurationException("Unknown replication storage type [" + unknown + "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If it was started inside "newActor", initialize it
|
||||
if (isRunning) initializeActorInstance
|
||||
|
||||
|
|
@ -547,8 +504,11 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
__lifeCycle: LifeCycle,
|
||||
__supervisor: Option[ActorRef],
|
||||
__hotswap: Stack[PartialFunction[Any, Unit]],
|
||||
__factory: () ⇒ Actor) = {
|
||||
this(__factory, __address)
|
||||
__factory: () ⇒ Actor,
|
||||
__replicationStrategy: ReplicationScheme) = {
|
||||
|
||||
this(__factory, __address, __replicationStrategy)
|
||||
|
||||
_uuid = __uuid
|
||||
timeout = __timeout
|
||||
receiveTimeout = __receiveTimeout
|
||||
|
|
@ -620,6 +580,10 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
setActorSelfFields(actorInstance.get, null)
|
||||
}
|
||||
} //else if (isBeingRestarted) throw new ActorKilledException("Actor [" + toString + "] is being restarted.")
|
||||
|
||||
if (isReplicated) {
|
||||
if (replicationStorage.isLeft) replicationStorage.left.get.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -656,7 +620,6 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
guard.withGuard {
|
||||
if (_linkedActors.remove(actorRef.uuid) eq null)
|
||||
throw new IllegalActorStateException("Actor [" + actorRef + "] is not a linked actor, can't unlink")
|
||||
|
||||
actorRef.supervisor = None
|
||||
}
|
||||
}
|
||||
|
|
@ -685,24 +648,31 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
def supervisor: Option[ActorRef] = _supervisor
|
||||
|
||||
// ========= AKKA PROTECTED FUNCTIONS =========
|
||||
@throws(classOf[java.io.ObjectStreamException])
|
||||
private def writeReplace(): AnyRef = {
|
||||
val inetaddr =
|
||||
if (ReflectiveAccess.RemoteModule.isEnabled) Actor.remote.address
|
||||
else ReflectiveAccess.RemoteModule.configDefaultAddress
|
||||
SerializedActorRef(uuid, address, inetaddr.getAddress.getHostAddress, inetaddr.getPort, timeout)
|
||||
}
|
||||
|
||||
protected[akka] def supervisor_=(sup: Option[ActorRef]) {
|
||||
_supervisor = sup
|
||||
}
|
||||
|
||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) {
|
||||
dispatcher dispatchMessage new MessageInvocation(this, message, senderOption, None)
|
||||
}
|
||||
protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit =
|
||||
dispatcher dispatchMessage new MessageInvocation(this, message, channel)
|
||||
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout(
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[Promise[T]]): Promise[T] = {
|
||||
val future = if (senderFuture.isDefined) senderFuture else Some(new DefaultPromise[T](timeout))
|
||||
dispatcher dispatchMessage new MessageInvocation(
|
||||
this, message, senderOption, future.asInstanceOf[Some[Promise[Any]]])
|
||||
future.get
|
||||
channel: UntypedChannel): Future[Any] = {
|
||||
val future = channel match {
|
||||
case f: ActorPromise ⇒ f
|
||||
case _ ⇒ new ActorPromise(timeout)
|
||||
}
|
||||
dispatcher dispatchMessage new MessageInvocation(this, message, future)
|
||||
future
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -733,7 +703,12 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
throw e
|
||||
}
|
||||
}
|
||||
} finally { guard.lock.unlock() }
|
||||
} finally {
|
||||
guard.lock.unlock()
|
||||
if (isReplicated) {
|
||||
if (replicationStorage.isLeft) replicationStorage.left.get.recordEntry(messageHandle, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable) {
|
||||
|
|
@ -785,27 +760,17 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) {
|
||||
def performRestart() {
|
||||
val failedActor = actorInstance.get
|
||||
|
||||
failedActor match {
|
||||
case p: Proxyable ⇒
|
||||
failedActor.preRestart(reason)
|
||||
failedActor.postRestart(reason)
|
||||
case _ ⇒
|
||||
failedActor.preRestart(reason)
|
||||
val freshActor = newActor
|
||||
setActorSelfFields(failedActor, null) // Only null out the references if we could instantiate the new actor
|
||||
actorInstance.set(freshActor) // Assign it here so if preStart fails, we can null out the sef-refs next call
|
||||
freshActor.preStart()
|
||||
freshActor.postRestart(reason)
|
||||
}
|
||||
failedActor.preRestart(reason)
|
||||
val freshActor = newActor
|
||||
setActorSelfFields(failedActor, null) // Only null out the references if we could instantiate the new actor
|
||||
actorInstance.set(freshActor) // Assign it here so if preStart fails, we can null out the sef-refs next call
|
||||
freshActor.preStart()
|
||||
freshActor.postRestart(reason)
|
||||
}
|
||||
|
||||
def tooManyRestarts() {
|
||||
_supervisor.foreach { sup ⇒
|
||||
// can supervisor handle the notification?
|
||||
val notification = MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason)
|
||||
if (sup.isDefinedAt(notification)) notifySupervisorWithMessage(notification)
|
||||
}
|
||||
notifySupervisorWithMessage(
|
||||
MaximumNumberOfRestartsWithinTimeRangeReached(this, maxNrOfRetries, withinTimeRange, reason))
|
||||
stop()
|
||||
}
|
||||
|
||||
|
|
@ -869,14 +834,19 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
// ========= PRIVATE FUNCTIONS =========
|
||||
|
||||
private[this] def newActor: Actor = {
|
||||
import Actor.{ actorRefInCreation ⇒ refStack }
|
||||
val stackBefore = refStack.get
|
||||
refStack.set(stackBefore.push(this))
|
||||
try {
|
||||
Actor.actorRefInCreation.set(Some(this))
|
||||
val a = actorFactory()
|
||||
if (a eq null) throw new ActorInitializationException("Actor instance passed to ActorRef can not be 'null'")
|
||||
a
|
||||
actorFactory()
|
||||
} finally {
|
||||
Actor.actorRefInCreation.set(None)
|
||||
val stackAfter = refStack.get
|
||||
if (stackAfter.nonEmpty)
|
||||
refStack.set(if (stackAfter.head eq null) stackAfter.pop.pop else stackAfter.pop) //pop null marker plus self
|
||||
}
|
||||
} match {
|
||||
case null ⇒ throw new ActorInitializationException("Actor instance passed to ActorRef can not be 'null'")
|
||||
case valid ⇒ valid
|
||||
}
|
||||
|
||||
private def shutDownTemporaryActor(temporaryActor: ActorRef) {
|
||||
|
|
@ -893,7 +863,7 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
//Prevent any further messages to be processed until the actor has been restarted
|
||||
dispatcher.suspend(this)
|
||||
|
||||
senderFuture.foreach(_.completeWithException(reason))
|
||||
channel.sendException(reason)
|
||||
|
||||
if (supervisor.isDefined) notifySupervisorWithMessage(Exit(this, reason))
|
||||
else {
|
||||
|
|
@ -957,7 +927,7 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor,
|
|||
|
||||
protected[akka] def checkReceiveTimeout() {
|
||||
cancelReceiveTimeout()
|
||||
if (receiveTimeout.isDefined && dispatcher.mailboxSize(this) <= 0) { //Only reschedule if desired and there are currently no more messages to be processed
|
||||
if (receiveTimeout.isDefined && dispatcher.mailboxIsEmpty(this)) { //Only reschedule if desired and there are currently no more messages to be processed
|
||||
_futureTimeout = Some(Scheduler.scheduleOnce(this, ReceiveTimeout, receiveTimeout.get, TimeUnit.MILLISECONDS))
|
||||
}
|
||||
}
|
||||
|
|
@ -996,23 +966,30 @@ private[akka] case class RemoteActorRef private[akka] (
|
|||
|
||||
timeout = _timeout
|
||||
|
||||
// FIXME BAD, we should not have different ActorRefs
|
||||
|
||||
start()
|
||||
|
||||
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) {
|
||||
Actor.remote.send[Any](message, senderOption, None, remoteAddress, timeout, true, this, loader)
|
||||
def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit = {
|
||||
val chSender = channel match {
|
||||
case ref: ActorRef ⇒ Some(ref)
|
||||
case _ ⇒ None
|
||||
}
|
||||
Actor.remote.send[Any](message, chSender, None, remoteAddress, timeout, true, this, loader)
|
||||
}
|
||||
|
||||
def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
def postMessageToMailboxAndCreateFutureResultWithTimeout(
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[Promise[T]]): Promise[T] = {
|
||||
val future = Actor.remote.send[T](
|
||||
message, senderOption, senderFuture,
|
||||
remoteAddress, timeout, false, this, loader)
|
||||
if (future.isDefined) future.get
|
||||
channel: UntypedChannel): Future[Any] = {
|
||||
val chSender = channel match {
|
||||
case ref: ActorRef ⇒ Some(ref)
|
||||
case _ ⇒ None
|
||||
}
|
||||
val chFuture = channel match {
|
||||
case f: Promise[Any] ⇒ Some(f)
|
||||
case _ ⇒ None
|
||||
}
|
||||
val future = Actor.remote.send[Any](message, chSender, chFuture, remoteAddress, timeout, false, this, loader)
|
||||
if (future.isDefined) ActorPromise(future.get)
|
||||
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
|
||||
}
|
||||
|
||||
|
|
@ -1030,9 +1007,12 @@ private[akka] case class RemoteActorRef private[akka] (
|
|||
}
|
||||
}
|
||||
|
||||
@throws(classOf[java.io.ObjectStreamException])
|
||||
private def writeReplace(): AnyRef = {
|
||||
SerializedActorRef(uuid, address, remoteAddress.getAddress.getHostAddress, remoteAddress.getPort, timeout)
|
||||
}
|
||||
|
||||
// ==== NOT SUPPORTED ====
|
||||
@deprecated("Will be removed without replacement, doesn't make any sense to have in the face of `become` and `unbecome`", "1.1")
|
||||
def actorClass: Class[_ <: Actor] = unsupported
|
||||
def dispatcher_=(md: MessageDispatcher) {
|
||||
unsupported
|
||||
}
|
||||
|
|
@ -1095,7 +1075,7 @@ trait ActorRefShared {
|
|||
* There are implicit conversions in ../actor/Implicits.scala
|
||||
* from ActorRef -> ScalaActorRef and back
|
||||
*/
|
||||
trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
||||
trait ScalaActorRef extends ActorRefShared with ForwardableChannel { ref: ActorRef ⇒
|
||||
|
||||
/**
|
||||
* Address for actor, must be a unique one.
|
||||
|
|
@ -1133,20 +1113,28 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
* The reference sender Actor of the last received message.
|
||||
* Is defined if the message was sent from another Actor, else None.
|
||||
*/
|
||||
@deprecated("will be removed in 2.0, use channel instead", "1.2")
|
||||
def sender: Option[ActorRef] = {
|
||||
val msg = currentMessage
|
||||
if (msg eq null) None
|
||||
else msg.sender
|
||||
else msg.channel match {
|
||||
case ref: ActorRef ⇒ Some(ref)
|
||||
case _ ⇒ None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The reference sender future of the last received message.
|
||||
* Is defined if the message was sent with sent with '!!' or '!!!', else None.
|
||||
* Is defined if the message was sent with sent with '?'/'ask', else None.
|
||||
*/
|
||||
@deprecated("will be removed in 2.0, use channel instead", "1.2")
|
||||
def senderFuture(): Option[Promise[Any]] = {
|
||||
val msg = currentMessage
|
||||
if (msg eq null) None
|
||||
else msg.senderFuture
|
||||
else msg.channel match {
|
||||
case f: ActorPromise ⇒ Some(f)
|
||||
case _ ⇒ None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1163,8 +1151,8 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
* </pre>
|
||||
* <p/>
|
||||
*/
|
||||
def !(message: Any)(implicit sender: Option[ActorRef] = None) {
|
||||
if (isRunning) postMessageToMailbox(message, sender)
|
||||
def !(message: Any)(implicit channel: UntypedChannel = NullChannel): Unit = {
|
||||
if (isRunning) postMessageToMailbox(message, channel)
|
||||
else throw new ActorInitializationException(
|
||||
"Actor has not been started, you need to invoke 'actor.start()' before using it")
|
||||
}
|
||||
|
|
@ -1181,9 +1169,10 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
* If you are sending messages using <code>!!</code> then you <b>have to</b> use <code>self.reply(..)</code>
|
||||
* to send a reply message to the original sender. If not then the sender will block until the timeout expires.
|
||||
*/
|
||||
def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = {
|
||||
@deprecated("use `(actor ? msg).as[T]` instead", "1.2")
|
||||
def !!(message: Any, timeout: Long = this.timeout)(implicit channel: UntypedChannel = NullChannel): Option[Any] = {
|
||||
if (isRunning) {
|
||||
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
|
||||
val future = postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, channel)
|
||||
|
||||
try { future.await.resultOrException } catch { case e: FutureTimeoutException ⇒ None }
|
||||
} else throw new ActorInitializationException(
|
||||
|
|
@ -1191,16 +1180,11 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends a message asynchronously returns a future holding the eventual reply message.
|
||||
* <p/>
|
||||
* <b>NOTE:</b>
|
||||
* Use this method with care. In most cases it is better to use '!' together with the 'sender' member field to
|
||||
* implement request/response message exchanges.
|
||||
* If you are sending messages using <code>!!!</code> then you <b>have to</b> use <code>self.reply(..)</code>
|
||||
* to send a reply message to the original sender. If not then the sender will block until the timeout expires.
|
||||
* Sends a message asynchronously, returning a future which may eventually hold the reply.
|
||||
*/
|
||||
def !!(implicit sender: Option[ActorRef] = None): Future[T] = {
|
||||
if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None)
|
||||
def ?(message: Any, timeout: Actor.Timeout = Actor.noTimeoutGiven)(implicit channel: UntypedChannel = NullChannel, implicitTimeout: Actor.Timeout = Actor.defaultTimeout): Future[Any] = {
|
||||
val realTimeout = if (timeout eq Actor.noTimeoutGiven) implicitTimeout else timeout
|
||||
if (isRunning) postMessageToMailboxAndCreateFutureResultWithTimeout(message, realTimeout.duration.toMillis, channel)
|
||||
else throw new ActorInitializationException(
|
||||
"Actor has not been started, you need to invoke 'actor.start()' before using it")
|
||||
}
|
||||
|
|
@ -1208,15 +1192,13 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
/**
|
||||
* Forwards the message and passes the original sender actor as the sender.
|
||||
* <p/>
|
||||
* Works with '!', '!!' and '!!!'.
|
||||
* Works with '!' and '?'/'ask'.
|
||||
*/
|
||||
def forward(message: Any)(implicit sender: Some[ActorRef]) = {
|
||||
def forward(message: Any)(implicit channel: ForwardableChannel) = {
|
||||
if (isRunning) {
|
||||
if (sender.get.senderFuture.isDefined)
|
||||
postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, sender.get.sender, sender.get.senderFuture)
|
||||
else
|
||||
postMessageToMailbox(message, sender.get.sender)
|
||||
} else throw new ActorInitializationException("Actor has not been started, you need to invoke 'actor.start()' before using it")
|
||||
postMessageToMailbox(message, channel.channel)
|
||||
} else throw new ActorInitializationException(
|
||||
"Actor has not been started, you need to invoke 'actor.start()' before using it")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1225,14 +1207,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
* <p/>
|
||||
* Throws an IllegalStateException if unable to determine what to reply to.
|
||||
*/
|
||||
def reply(message: Any) {
|
||||
if (!reply_?(message)) throw new IllegalActorStateException(
|
||||
"\n\tNo sender in scope, can't reply. " +
|
||||
"\n\tYou have probably: " +
|
||||
"\n\t\t1. Sent a message to an Actor from an instance that is NOT an Actor." +
|
||||
"\n\t\t2. Invoked a method on an TypedActor from an instance NOT an TypedActor." +
|
||||
"\n\tElse you might want to use 'reply_?' which returns Boolean(true) if succes and Boolean(false) if no sender in scope")
|
||||
}
|
||||
def reply(message: Any) = channel.!(message)(this)
|
||||
|
||||
/**
|
||||
* Use <code>reply_?(..)</code> to reply with a message to the original sender of the message currently
|
||||
|
|
@ -1240,14 +1215,23 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒
|
|||
* <p/>
|
||||
* Returns true if reply was sent, and false if unable to determine what to reply to.
|
||||
*/
|
||||
def reply_?(message: Any): Boolean = {
|
||||
if (senderFuture.isDefined) {
|
||||
senderFuture.get completeWithResult message
|
||||
true
|
||||
} else if (sender.isDefined) {
|
||||
//TODO: optimize away this allocation, perhaps by having implicit self: Option[ActorRef] in signature
|
||||
sender.get.!(message)(Some(this))
|
||||
true
|
||||
} else false
|
||||
def reply_?(message: Any): Boolean = channel.safe_!(message)(this)
|
||||
}
|
||||
|
||||
case class SerializedActorRef(val uuid: Uuid,
|
||||
val address: String,
|
||||
val hostname: String,
|
||||
val port: Int,
|
||||
val timeout: Long) {
|
||||
@throws(classOf[java.io.ObjectStreamException])
|
||||
def readResolve(): AnyRef = Actor.registry.local.actorFor(uuid) match {
|
||||
case Some(actor) ⇒ actor
|
||||
case None ⇒
|
||||
if (ReflectiveAccess.RemoteModule.isEnabled)
|
||||
RemoteActorRef(new InetSocketAddress(hostname, port), address, timeout, None)
|
||||
else
|
||||
throw new IllegalStateException(
|
||||
"Trying to deserialize ActorRef (" + this +
|
||||
") but it's not found in the local registry and remoting is not enabled!")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.{ ConcurrentSkipListSet, ConcurrentHashMap }
|
|||
import java.util.{ Set ⇒ JSet }
|
||||
|
||||
import akka.util.ReflectiveAccess._
|
||||
import akka.util.{ ReflectiveAccess, ReadWriteGuard, ListenerManagement }
|
||||
import akka.util.ListenerManagement
|
||||
import akka.serialization._
|
||||
|
||||
/**
|
||||
|
|
@ -21,8 +21,8 @@ import akka.serialization._
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
sealed trait ActorRegistryEvent
|
||||
case class ActorRegistered(address: String, actor: ActorRef) extends ActorRegistryEvent
|
||||
case class ActorUnregistered(address: String, actor: ActorRef) extends ActorRegistryEvent
|
||||
case class ActorRegistered(address: String, actor: ActorRef, typedActor: Option[AnyRef]) extends ActorRegistryEvent
|
||||
case class ActorUnregistered(address: String, actor: ActorRef, typedActor: Option[AnyRef]) extends ActorRegistryEvent
|
||||
|
||||
/**
|
||||
* Registry holding all Actor instances in the whole system.
|
||||
|
|
@ -36,7 +36,6 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
private val actorsByAddress = new ConcurrentHashMap[String, ActorRef]
|
||||
private val actorsByUuid = new ConcurrentHashMap[Uuid, ActorRef]
|
||||
private val typedActorsByUuid = new ConcurrentHashMap[Uuid, AnyRef]
|
||||
private val guard = new ReadWriteGuard
|
||||
|
||||
val local = new LocalActorRegistry(actorsByAddress, actorsByUuid, typedActorsByUuid)
|
||||
|
||||
|
|
@ -45,7 +44,6 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
*/
|
||||
def actorFor(address: String): Option[ActorRef] = {
|
||||
if (actorsByAddress.containsKey(address)) Some(actorsByAddress.get(address))
|
||||
// else if (isClusterEnabled) ClusterModule.node.use(address) // FIXME uncomment and fix
|
||||
else None
|
||||
}
|
||||
|
||||
|
|
@ -67,11 +65,12 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
|
||||
actorsByAddress.put(address, actor)
|
||||
actorsByUuid.put(actor.uuid, actor)
|
||||
notifyListeners(ActorRegistered(address, actor))
|
||||
notifyListeners(ActorRegistered(address, actor, Option(typedActorsByUuid get actor.uuid)))
|
||||
}
|
||||
|
||||
private[akka] def registerTypedActor(actorRef: ActorRef, interface: AnyRef) {
|
||||
typedActorsByUuid.put(actorRef.uuid, interface)
|
||||
actorRef.start // register actorRef
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +79,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
private[akka] def unregister(address: String) {
|
||||
val actor = actorsByAddress remove address
|
||||
actorsByUuid remove actor.uuid
|
||||
notifyListeners(ActorUnregistered(address, actor))
|
||||
notifyListeners(ActorUnregistered(address, actor, None))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -90,8 +89,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
val address = actor.address
|
||||
actorsByAddress remove address
|
||||
actorsByUuid remove actor.uuid
|
||||
typedActorsByUuid remove actor.uuid
|
||||
notifyListeners(ActorUnregistered(address, actor))
|
||||
notifyListeners(ActorUnregistered(address, actor, Option(typedActorsByUuid remove actor.uuid)))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,7 +115,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag
|
|||
}
|
||||
|
||||
/**
|
||||
* View over the local actor registry.
|
||||
* Projection over the local actor registry.
|
||||
*/
|
||||
class LocalActorRegistry(
|
||||
private val actorsByAddress: ConcurrentHashMap[String, ActorRef],
|
||||
|
|
@ -135,9 +133,9 @@ class LocalActorRegistry(
|
|||
def shutdownAll() {
|
||||
foreach(_.stop)
|
||||
if (ClusterModule.isEnabled) Actor.remote.clear //FIXME: Should this be here?
|
||||
actorsByAddress.clear
|
||||
actorsByUuid.clear
|
||||
typedActorsByUuid.clear
|
||||
actorsByAddress.clear()
|
||||
actorsByUuid.clear()
|
||||
typedActorsByUuid.clear()
|
||||
}
|
||||
|
||||
//============== ACTORS ==============
|
||||
|
|
@ -341,9 +339,7 @@ class Index[K <: AnyRef, V <: AnyRef: Manifest] {
|
|||
*/
|
||||
def foreach(fun: (K, V) ⇒ Unit) {
|
||||
import scala.collection.JavaConversions._
|
||||
container.entrySet foreach { (e) ⇒
|
||||
e.getValue.foreach(fun(e.getKey, _))
|
||||
}
|
||||
container.entrySet foreach { e ⇒ e.getValue.foreach(fun(e.getKey, _)) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
173
akka-actor/src/main/scala/akka/actor/Channel.scala
Normal file
173
akka-actor/src/main/scala/akka/actor/Channel.scala
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.actor
|
||||
|
||||
/**
|
||||
* Abstraction for unification of sender and senderFuture for later reply.
|
||||
* Can be stored away and used at a later point in time.
|
||||
*
|
||||
* Channel cannot be contravariant because of Future providing its value in
|
||||
* covariant position.
|
||||
*
|
||||
* The possible reply channel which can be passed into ! and safe_! is always
|
||||
* untyped, as there is no way to utilize its real static type without
|
||||
* requiring runtime-costly manifests.
|
||||
*/
|
||||
trait Channel[T] {
|
||||
|
||||
/**
|
||||
* Scala API. <p/>
|
||||
* Sends the specified message to the channel.
|
||||
*/
|
||||
def !(msg: T)(implicit channel: UntypedChannel = NullChannel): Unit
|
||||
|
||||
/**
|
||||
* Try to send an exception. Not all channel types support this, one notable
|
||||
* positive example is Future. Failure to send is silent.
|
||||
*/
|
||||
def sendException(ex: Throwable): Unit
|
||||
|
||||
/**
|
||||
* Scala API.<p/>
|
||||
* Try to send message to the channel, return whether successful.
|
||||
*/
|
||||
def safe_!(msg: T)(implicit channel: UntypedChannel = NullChannel): Boolean
|
||||
|
||||
/**
|
||||
* Indicates whether this channel may be used only once, e.g. a Future.
|
||||
*/
|
||||
def isUsableOnlyOnce: Boolean
|
||||
|
||||
/**
|
||||
* Indicates whether this channel may still be used (only useful if
|
||||
* isUsableOnlyOnce returns true).
|
||||
*/
|
||||
def isUsable: Boolean
|
||||
|
||||
/**
|
||||
* Indicates whether this channel carries reply information, e.g. an
|
||||
* ActorRef.
|
||||
*/
|
||||
def isReplyable: Boolean
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is capable of sending exceptions to its
|
||||
* recipient.
|
||||
*/
|
||||
def canSendException: Boolean
|
||||
|
||||
/**
|
||||
* Java API.<p/>
|
||||
* Sends the specified message to the channel, i.e. fire-and-forget semantics.<p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message);
|
||||
* </pre>
|
||||
*/
|
||||
def sendOneWay(msg: T): Unit = this.!(msg)
|
||||
|
||||
/**
|
||||
* Java API. <p/>
|
||||
* Sends the specified message to the channel, i.e. fire-and-forget
|
||||
* semantics, including the sender reference if possible (not supported on
|
||||
* all channels).<p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message, context);
|
||||
* </pre>
|
||||
*/
|
||||
def sendOneWay(msg: T, sender: UntypedChannel): Unit = this.!(msg)(sender)
|
||||
|
||||
/**
|
||||
* Java API.<p/>
|
||||
* Try to send the specified message to the channel, i.e. fire-and-forget semantics.<p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message);
|
||||
* </pre>
|
||||
*/
|
||||
def sendOneWaySafe(msg: T): Boolean = this.safe_!(msg)
|
||||
|
||||
/**
|
||||
* Java API. <p/>
|
||||
* Try to send the specified message to the channel, i.e. fire-and-forget
|
||||
* semantics, including the sender reference if possible (not supported on
|
||||
* all channels).<p/>
|
||||
* <pre>
|
||||
* actor.sendOneWay(message, context);
|
||||
* </pre>
|
||||
*/
|
||||
def sendOneWaySafe(msg: T, sender: UntypedChannel): Boolean = this.safe_!(msg)(sender)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This trait represents a channel that a priori does have sending capability,
|
||||
* i.e. ! is not guaranteed to fail (e.g. NullChannel would be a
|
||||
* counter-example).
|
||||
*/
|
||||
trait AvailableChannel[T] { self: Channel[T] ⇒
|
||||
def safe_!(msg: T)(implicit channel: UntypedChannel = NullChannel): Boolean = {
|
||||
if (isUsable) {
|
||||
try {
|
||||
this ! msg
|
||||
true
|
||||
} catch {
|
||||
case _ ⇒ false
|
||||
}
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All channels used in conjunction with MessageInvocation are untyped by
|
||||
* design, so make this explicit.
|
||||
*/
|
||||
trait UntypedChannel extends Channel[Any]
|
||||
|
||||
object UntypedChannel {
|
||||
implicit def senderOption2Channel(sender: Option[ActorRef]): UntypedChannel =
|
||||
sender match {
|
||||
case Some(actor) ⇒ actor
|
||||
case None ⇒ NullChannel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default channel when none available.
|
||||
*/
|
||||
case object NullChannel extends UntypedChannel {
|
||||
def !(msg: Any)(implicit channel: UntypedChannel = NullChannel) {
|
||||
throw new IllegalActorStateException("""
|
||||
No sender in scope, can't reply.
|
||||
You have probably:
|
||||
1. Sent a message to an Actor from an instance that is NOT an Actor.
|
||||
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
|
||||
You may want to have a look at safe_! for a variant returning a Boolean""")
|
||||
}
|
||||
def safe_!(msg: Any)(implicit channel: UntypedChannel = NullChannel): Boolean = false
|
||||
def sendException(ex: Throwable) {}
|
||||
def isUsableOnlyOnce = false
|
||||
def isUsable = false
|
||||
def isReplyable = false
|
||||
def canSendException = false
|
||||
}
|
||||
|
||||
/**
|
||||
* A channel which may be forwarded: a message received with such a reply
|
||||
* channel attached can be passed on transparently such that a reply from a
|
||||
* later processing stage is sent directly back to the origin. Keep in mind
|
||||
* that not all channels can be used multiple times.
|
||||
*/
|
||||
trait ForwardableChannel extends UntypedChannel with AvailableChannel[Any] {
|
||||
/**
|
||||
* Get channel by which this channel would reply (ActorRef.forward takes an
|
||||
* implicit ForwardableChannel and uses its .channel as message origin)
|
||||
*/
|
||||
def channel: UntypedChannel
|
||||
}
|
||||
|
||||
object ForwardableChannel {
|
||||
implicit def someS2FC(sender: Some[ActorRef]): ForwardableChannel = sender.get
|
||||
implicit def someIS2FC(implicit sender: Some[ActorRef]): ForwardableChannel = sender.get
|
||||
}
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ import akka.actor.DeploymentConfig._
|
|||
import akka.config.{ ConfigurationException, Config }
|
||||
import akka.routing.RouterType
|
||||
import akka.util.ReflectiveAccess._
|
||||
import akka.serialization.Format
|
||||
import akka.serialization._
|
||||
import akka.AkkaException
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +31,7 @@ object DeploymentConfig {
|
|||
case class Deploy(
|
||||
address: String,
|
||||
routing: Routing = Direct,
|
||||
format: String = Format.defaultSerializerName,
|
||||
format: String = Serializer.defaultSerializerName, // Format.defaultSerializerName,
|
||||
scope: Scope = Local)
|
||||
|
||||
// --------------------------------
|
||||
|
|
@ -62,8 +62,8 @@ object DeploymentConfig {
|
|||
sealed trait Scope
|
||||
case class Clustered(
|
||||
home: Home = Host("localhost"),
|
||||
replication: Replication = NoReplicas,
|
||||
state: State = Stateful) extends Scope
|
||||
replicas: Replicas = NoReplicas,
|
||||
replication: ReplicationScheme = Transient) extends Scope
|
||||
|
||||
// For Java API
|
||||
case class Local() extends Scope
|
||||
|
|
@ -80,33 +80,60 @@ object DeploymentConfig {
|
|||
case class IP(ipAddress: String) extends Home
|
||||
|
||||
// --------------------------------
|
||||
// --- Replication
|
||||
// --- Replicas
|
||||
// --------------------------------
|
||||
sealed trait Replication
|
||||
case class Replicate(factor: Int) extends Replication {
|
||||
if (factor < 1) throw new IllegalArgumentException("Replication factor can not be negative or zero")
|
||||
sealed trait Replicas
|
||||
case class Replicate(factor: Int) extends Replicas {
|
||||
if (factor < 1) throw new IllegalArgumentException("Replicas factor can not be negative or zero")
|
||||
}
|
||||
|
||||
// For Java API
|
||||
case class AutoReplicate() extends Replication
|
||||
case class NoReplicas() extends Replication
|
||||
case class AutoReplicate() extends Replicas
|
||||
case class NoReplicas() extends Replicas
|
||||
|
||||
// For Scala API
|
||||
case object AutoReplicate extends Replication
|
||||
case object NoReplicas extends Replication
|
||||
case object AutoReplicate extends Replicas
|
||||
case object NoReplicas extends Replicas
|
||||
|
||||
// --------------------------------
|
||||
// --- State
|
||||
// --- Replication
|
||||
// --------------------------------
|
||||
sealed trait State
|
||||
sealed trait ReplicationScheme
|
||||
|
||||
// For Java API
|
||||
case class Stateless() extends State
|
||||
case class Stateful() extends State
|
||||
case class Transient() extends ReplicationScheme
|
||||
|
||||
// For Scala API
|
||||
case object Stateless extends State
|
||||
case object Stateful extends State
|
||||
case object Transient extends ReplicationScheme
|
||||
case class Replication(
|
||||
storage: ReplicationStorage,
|
||||
strategy: ReplicationStrategy) extends ReplicationScheme
|
||||
|
||||
// --------------------------------
|
||||
// --- ReplicationStorage
|
||||
// --------------------------------
|
||||
sealed trait ReplicationStorage
|
||||
|
||||
// For Java API
|
||||
case class TransactionLog() extends ReplicationStorage
|
||||
case class DataGrid() extends ReplicationStorage
|
||||
|
||||
// For Scala API
|
||||
case object TransactionLog extends ReplicationStorage
|
||||
case object DataGrid extends ReplicationStorage
|
||||
|
||||
// --------------------------------
|
||||
// --- ReplicationStrategy
|
||||
// --------------------------------
|
||||
sealed trait ReplicationStrategy
|
||||
|
||||
// For Java API
|
||||
case class WriteBehind() extends ReplicationStrategy
|
||||
case class WriteThrough() extends ReplicationStrategy
|
||||
|
||||
// For Scala API
|
||||
case object WriteBehind extends ReplicationStrategy
|
||||
case object WriteThrough extends ReplicationStrategy
|
||||
|
||||
// --------------------------------
|
||||
// --- Helper methods for parsing
|
||||
|
|
@ -114,11 +141,11 @@ object DeploymentConfig {
|
|||
|
||||
def isHomeNode(home: Home): Boolean = home match {
|
||||
case Host(hostname) ⇒ hostname == Config.hostname
|
||||
case IP(address) ⇒ address == "0.0.0.0" // FIXME checking if IP address is on home node is missing
|
||||
case IP(address) ⇒ address == "0.0.0.0" || address == "127.0.0.1" // FIXME look up IP address from the system
|
||||
case Node(nodename) ⇒ nodename == Config.nodename
|
||||
}
|
||||
|
||||
def replicaValueFor(replication: Replication): Int = replication match {
|
||||
def replicaValueFor(replicas: Replicas): Int = replicas match {
|
||||
case Replicate(replicas) ⇒ replicas
|
||||
case AutoReplicate ⇒ -1
|
||||
case AutoReplicate() ⇒ -1
|
||||
|
|
@ -139,6 +166,12 @@ object DeploymentConfig {
|
|||
case LeastRAM() ⇒ RouterType.LeastRAM
|
||||
case LeastMessages ⇒ RouterType.LeastMessages
|
||||
case LeastMessages() ⇒ RouterType.LeastMessages
|
||||
case c: CustomRouter ⇒ throw new UnsupportedOperationException("routerTypeFor: " + c)
|
||||
}
|
||||
|
||||
def isReplicationAsync(strategy: ReplicationStrategy): Boolean = strategy match {
|
||||
case _: WriteBehind | WriteBehind ⇒ true
|
||||
case _: WriteThrough | WriteThrough ⇒ false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +295,7 @@ object Deployer {
|
|||
// --------------------------------
|
||||
val addressPath = "akka.actor.deployment." + address
|
||||
Config.config.getSection(addressPath) match {
|
||||
case None ⇒ Some(Deploy(address, Direct, Format.defaultSerializerName, Local))
|
||||
case None ⇒ Some(Deploy(address, Direct, Serializer.defaultSerializerName, Local))
|
||||
case Some(addressConfig) ⇒
|
||||
|
||||
// --------------------------------
|
||||
|
|
@ -289,14 +322,14 @@ object Deployer {
|
|||
// --------------------------------
|
||||
// akka.actor.deployment.<address>.format
|
||||
// --------------------------------
|
||||
val format = addressConfig.getString("format", Format.defaultSerializerName)
|
||||
val format = addressConfig.getString("format", Serializer.defaultSerializerName)
|
||||
|
||||
// --------------------------------
|
||||
// akka.actor.deployment.<address>.clustered
|
||||
// --------------------------------
|
||||
addressConfig.getSection("clustered") match {
|
||||
case None ⇒
|
||||
Some(Deploy(address, router, Format.defaultSerializerName, Local)) // deploy locally
|
||||
Some(Deploy(address, router, Serializer.defaultSerializerName, Local)) // deploy locally
|
||||
|
||||
case Some(clusteredConfig) ⇒
|
||||
|
||||
|
|
@ -345,18 +378,36 @@ object Deployer {
|
|||
}
|
||||
|
||||
// --------------------------------
|
||||
// akka.actor.deployment.<address>.clustered.stateless
|
||||
// akka.actor.deployment.<address>.clustered.replication
|
||||
// --------------------------------
|
||||
val state =
|
||||
if (clusteredConfig.getBool("stateless", false)) Stateless
|
||||
else Stateful
|
||||
clusteredConfig.getSection("replication") match {
|
||||
case None ⇒
|
||||
Some(Deploy(address, router, format, Clustered(home, replicas, Transient)))
|
||||
|
||||
Some(Deploy(address, router, format, Clustered(home, replicas, state)))
|
||||
case Some(replicationConfig) ⇒
|
||||
val storage = replicationConfig.getString("storage", "transaction-log") match {
|
||||
case "transaction-log" ⇒ TransactionLog
|
||||
case "data-grid" ⇒ DataGrid
|
||||
case unknown ⇒
|
||||
throw new ConfigurationException("Config option [" + addressPath +
|
||||
".clustered.replication.storage] needs to be either [\"transaction-log\"] or [\"data-grid\"] - was [" +
|
||||
unknown + "]")
|
||||
}
|
||||
val strategy = replicationConfig.getString("strategy", "write-through") match {
|
||||
case "write-through" ⇒ WriteThrough
|
||||
case "write-behind" ⇒ WriteBehind
|
||||
case unknown ⇒
|
||||
throw new ConfigurationException("Config option [" + addressPath +
|
||||
".clustered.replication.strategy] needs to be either [\"write-through\"] or [\"write-behind\"] - was [" +
|
||||
unknown + "]")
|
||||
}
|
||||
Some(Deploy(address, router, format, Clustered(home, replicas, Replication(storage, strategy))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def throwDeploymentBoundException(deployment: Deploy): Nothing = {
|
||||
private[akka] def throwDeploymentBoundException(deployment: Deploy): Nothing = {
|
||||
val e = new DeploymentAlreadyBoundException(
|
||||
"Address [" + deployment.address +
|
||||
"] already bound to [" + deployment +
|
||||
|
|
@ -365,7 +416,7 @@ object Deployer {
|
|||
throw e
|
||||
}
|
||||
|
||||
private def thrownNoDeploymentBoundException(address: String): Nothing = {
|
||||
private[akka] def thrownNoDeploymentBoundException(address: String): Nothing = {
|
||||
val e = new NoDeploymentBoundException("Address [" + address + "] is not bound to a deployment")
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
throw e
|
||||
|
|
@ -392,8 +443,7 @@ object LocalDeployer {
|
|||
|
||||
private[akka] def deploy(deployment: Deploy) {
|
||||
if (deployments.putIfAbsent(deployment.address, deployment) != deployment) {
|
||||
// FIXME do automatic 'undeploy' and redeploy (perhaps have it configurable if redeploy should be done or exception thrown)
|
||||
// throwDeploymentBoundException(deployment)
|
||||
//Deployer.throwDeploymentBoundException(deployment) // FIXME uncomment this and fix the issue with multiple deployments
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +465,8 @@ object Address {
|
|||
def validate(address: String) {
|
||||
if (validAddressPattern.matcher(address).matches) true
|
||||
else {
|
||||
val e = new IllegalArgumentException("Address [" + address + "] is not valid, need to follow pattern [0-9a-zA-Z\\-\\_\\$]+")
|
||||
val e = new IllegalArgumentException(
|
||||
"Address [" + address + "] is not valid, need to follow pattern [0-9a-zA-Z\\-\\_\\$]+")
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
throw e
|
||||
}
|
||||
|
|
|
|||
|
|
@ -494,10 +494,7 @@ trait FSM[S, D] extends ListenerManagement {
|
|||
* @return this state transition descriptor
|
||||
*/
|
||||
def replying(replyValue: Any): State = {
|
||||
self.sender match {
|
||||
case Some(sender) ⇒ sender ! replyValue
|
||||
case None ⇒
|
||||
}
|
||||
self.channel safe_! replyValue
|
||||
this
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class SupervisorException private[akka] (message: String, cause: Throwable = nul
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Supervisor {
|
||||
def apply(config: SupervisorConfig): Supervisor = SupervisorFactory(config).newInstance.start
|
||||
def apply(config: SupervisorConfig): Supervisor = SupervisorFactory(config).newInstance.start()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -108,7 +108,7 @@ sealed class Supervisor(handler: FaultHandlingStrategy, maxRestartsHandler: (Act
|
|||
|
||||
def uuid = supervisor.uuid
|
||||
|
||||
def start: Supervisor = {
|
||||
def start(): Supervisor = {
|
||||
this
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ package akka.actor
|
|||
*/
|
||||
|
||||
import akka.japi.{ Creator, Option ⇒ JOption }
|
||||
import akka.actor.Actor.{ actorOf, futureToAnyOptionAsTypedOption }
|
||||
import akka.dispatch.{ MessageDispatcher, Dispatchers, Future }
|
||||
import akka.actor.Actor._
|
||||
import akka.dispatch.{ MessageDispatcher, Dispatchers, Future, FutureTimeoutException }
|
||||
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
|
||||
import akka.util.{ Duration }
|
||||
import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar }
|
||||
import collection.immutable
|
||||
|
||||
//TODO Document this class, not only in Scaladoc, but also in a dedicated typed-actor.rst, for both java and scala
|
||||
object TypedActor {
|
||||
private val selfReference = new ThreadLocal[AnyRef]
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ object TypedActor {
|
|||
case some ⇒ some
|
||||
}
|
||||
|
||||
private class TypedActor[R <: AnyRef, T <: R](val proxyRef: AtomVar[R], createInstance: ⇒ T) extends Actor {
|
||||
private[akka] class TypedActor[R <: AnyRef, T <: R](val proxyRef: AtomVar[R], createInstance: ⇒ T) extends Actor {
|
||||
val me = createInstance
|
||||
def receive = {
|
||||
case m: MethodCall ⇒
|
||||
|
|
@ -41,24 +41,28 @@ object TypedActor {
|
|||
case "equals" ⇒ (args.length == 1 && (proxy eq args(0)) || actor == getActorRefFor(args(0))).asInstanceOf[AnyRef] //Force boxing of the boolean
|
||||
case "hashCode" ⇒ actor.hashCode.asInstanceOf[AnyRef]
|
||||
case _ ⇒
|
||||
implicit val timeout = Actor.Timeout(actor.timeout)
|
||||
MethodCall(method, args) match {
|
||||
case m if m.isOneWay ⇒
|
||||
actor ! m
|
||||
null
|
||||
case m if m.returnsFuture_? ⇒
|
||||
actor !!! m
|
||||
actor ? m
|
||||
case m if m.returnsJOption_? || m.returnsOption_? ⇒
|
||||
(actor !!! m).as[AnyRef] match {
|
||||
case Some(null) | None ⇒ if (m.returnsJOption_?) JOption.none[Any] else None
|
||||
case Some(joption) ⇒ joption
|
||||
val f = actor ? m
|
||||
try { f.await } catch { case _: FutureTimeoutException ⇒ }
|
||||
f.value match {
|
||||
case None | Some(Right(null)) ⇒ if (m.returnsJOption_?) JOption.none[Any] else None
|
||||
case Some(Right(joption: AnyRef)) ⇒ joption
|
||||
case Some(Left(ex)) ⇒ throw ex
|
||||
}
|
||||
case m ⇒
|
||||
(actor !!! m).get
|
||||
(actor ? m).get.asInstanceOf[AnyRef]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Configuration {
|
||||
object Configuration { //TODO: Replace this with the new ActorConfiguration when it exists
|
||||
val defaultTimeout = Duration(Actor.TIMEOUT, "millis")
|
||||
val defaultConfiguration = new Configuration(defaultTimeout, Dispatchers.defaultGlobalDispatcher)
|
||||
def apply(): Configuration = defaultConfiguration
|
||||
|
|
@ -83,6 +87,8 @@ object TypedActor {
|
|||
}
|
||||
|
||||
case class SerializedMethodCall(ownerType: Class[_], methodName: String, parameterTypes: Array[Class[_]], parameterValues: Array[AnyRef]) {
|
||||
//TODO implement writeObject and readObject to serialize
|
||||
//TODO Possible optimization is to special encode the parameter-types to conserve space
|
||||
private def readResolve(): AnyRef = MethodCall(ownerType.getDeclaredMethod(methodName, parameterTypes: _*), parameterValues)
|
||||
}
|
||||
|
||||
|
|
@ -157,9 +163,9 @@ object TypedActor {
|
|||
|
||||
val proxy: T = Proxy.newProxyInstance(loader, interfaces, new TypedActorInvocationHandler(ref)).asInstanceOf[T]
|
||||
proxyRef.set(proxy) // Chicken and egg situation we needed to solve, set the proxy so that we can set the self-reference inside each receive
|
||||
Actor.registry.registerTypedActor(ref.start, proxy) //We only have access to the proxy from the outside, so register it with the ActorRegistry, will be removed on actor.stop
|
||||
Actor.registry.registerTypedActor(ref, proxy) //We only have access to the proxy from the outside, so register it with the ActorRegistry, will be removed on actor.stop
|
||||
proxy
|
||||
}
|
||||
|
||||
private[akka] def extractInterfaces(clazz: Class[_]): Array[Class[_]] = if (clazz.isInterface) Array[Class[_]](clazz) else clazz.getInterfaces
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.cluster
|
|||
import akka.remoteinterface.RemoteSupport
|
||||
import akka.serialization.Serializer
|
||||
import akka.actor._
|
||||
import DeploymentConfig._
|
||||
import akka.dispatch.Future
|
||||
import akka.config.Config
|
||||
import akka.util._
|
||||
|
|
@ -129,13 +130,13 @@ object NodeAddress {
|
|||
trait ClusterNode {
|
||||
import ChangeListener._
|
||||
|
||||
val nodeAddress: NodeAddress
|
||||
val zkServerAddresses: String
|
||||
def nodeAddress: NodeAddress
|
||||
def zkServerAddresses: String
|
||||
|
||||
val remoteClientLifeCycleListener: ActorRef
|
||||
val remoteDaemon: ActorRef
|
||||
val remoteService: RemoteSupport
|
||||
val remoteServerAddress: InetSocketAddress
|
||||
def remoteClientLifeCycleListener: ActorRef
|
||||
def remoteDaemon: ActorRef
|
||||
def remoteService: RemoteSupport
|
||||
def remoteServerAddress: InetSocketAddress
|
||||
|
||||
val isConnected = new Switch(false)
|
||||
val isLeader = new AtomicBoolean(false)
|
||||
|
|
@ -179,6 +180,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationScheme: ReplicationScheme, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -186,6 +194,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -193,6 +208,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -200,6 +222,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -207,6 +236,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store(actorRef: ActorRef, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -214,6 +250,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -221,11 +264,23 @@ trait ClusterNode {
|
|||
*/
|
||||
def store(actorRef: ActorRef, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Needed to have reflection through structural typing work.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: AnyRef): ClusterNode
|
||||
|
||||
/**
|
||||
* Needed to have reflection through structural typing work.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: AnyRef): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
|
|
@ -233,6 +288,13 @@ trait ClusterNode {
|
|||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode
|
||||
|
||||
/**
|
||||
* Removes actor with uuid from the cluster.
|
||||
*/
|
||||
|
|
@ -262,13 +324,13 @@ trait ClusterNode {
|
|||
* Checks out an actor for use on this node, e.g. checked out as a 'LocalActorRef' but it makes it available
|
||||
* for remote access through lookup by its UUID.
|
||||
*/
|
||||
def use[T <: Actor](actorAddress: String): Option[LocalActorRef]
|
||||
def use[T <: Actor](actorAddress: String): Option[ActorRef]
|
||||
|
||||
/**
|
||||
* Checks out an actor for use on this node, e.g. checked out as a 'LocalActorRef' but it makes it available
|
||||
* for remote access through lookup by its UUID.
|
||||
*/
|
||||
def use[T <: Actor](actorAddress: String, format: Serializer): Option[LocalActorRef]
|
||||
def use[T <: Actor](actorAddress: String, format: Serializer): Option[ActorRef]
|
||||
|
||||
/**
|
||||
* Using (checking out) all actors with a specific UUID on all nodes in the cluster.
|
||||
|
|
|
|||
|
|
@ -119,4 +119,12 @@ object Config {
|
|||
|
||||
val startTime = System.currentTimeMillis
|
||||
def uptime = (System.currentTimeMillis - startTime) / 1000
|
||||
|
||||
val serializers = config.getSection("akka.actor.serializers").map(_.map).getOrElse(Map("default" -> "akka.serialization.JavaSerializer"))
|
||||
|
||||
val bindings = config.getSection("akka.actor.bindings")
|
||||
.map(_.map)
|
||||
.map(m ⇒ Map() ++ m.map { case (k, v: List[String]) ⇒ Map() ++ v.map((_, k)) }.flatten)
|
||||
|
||||
val serializerMap = bindings.map(m ⇒ m.map { case (k, v: String) ⇒ (k, serializers(v)) }).getOrElse(Map())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,10 +129,7 @@ class BalancingDispatcher(
|
|||
*/
|
||||
protected def donate(organ: MessageInvocation, recipient: ActorRef): Boolean = {
|
||||
if (organ ne null) {
|
||||
if (organ.senderFuture.isDefined) recipient.postMessageToMailboxAndCreateFutureResultWithTimeout[Any](
|
||||
organ.message, recipient.timeout, organ.sender, organ.senderFuture)
|
||||
else if (organ.sender.isDefined) recipient.postMessageToMailbox(organ.message, organ.sender)
|
||||
else recipient.postMessageToMailbox(organ.message, None)
|
||||
recipient.postMessageToMailbox(organ.message, organ.channel)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@
|
|||
package akka.dispatch
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.actor.{ ActorRef }
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionException, ConcurrentLinkedQueue }
|
||||
import akka.actor.{ ActorKilledException, ActorRef }
|
||||
|
||||
/**
|
||||
* Default settings are:
|
||||
|
|
@ -111,6 +110,8 @@ class Dispatcher(
|
|||
*/
|
||||
protected def getMailbox(receiver: ActorRef) = receiver.mailbox.asInstanceOf[MessageQueue with ExecutableMailbox]
|
||||
|
||||
def mailboxIsEmpty(actorRef: ActorRef): Boolean = getMailbox(actorRef).isEmpty
|
||||
|
||||
override def mailboxSize(actorRef: ActorRef) = getMailbox(actorRef).size
|
||||
|
||||
def createMailbox(actorRef: ActorRef): AnyRef = mailboxType match {
|
||||
|
|
@ -159,6 +160,18 @@ class Dispatcher(
|
|||
private[akka] def reRegisterForExecution(mbox: MessageQueue with ExecutableMailbox): Unit =
|
||||
registerForExecution(mbox)
|
||||
|
||||
protected override def cleanUpMailboxFor(actorRef: ActorRef) {
|
||||
val m = getMailbox(actorRef)
|
||||
if (!m.isEmpty) {
|
||||
var invocation = m.dequeue
|
||||
lazy val exception = new ActorKilledException("Actor has been stopped")
|
||||
while (invocation ne null) {
|
||||
invocation.channel.sendException(exception)
|
||||
invocation = m.dequeue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val toString = getClass.getSimpleName + "[" + name + "]"
|
||||
|
||||
def suspend(actorRef: ActorRef) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ package akka.dispatch
|
|||
|
||||
import akka.AkkaException
|
||||
import akka.event.EventHandler
|
||||
import akka.actor.{ Actor, Channel }
|
||||
import akka.util.Duration
|
||||
import akka.actor.{ Actor, Channel, ForwardableChannel, NullChannel, UntypedChannel, ActorRef }
|
||||
import akka.util.{ Duration, BoxedType }
|
||||
import akka.japi.{ Procedure, Function ⇒ JFunc }
|
||||
|
||||
import scala.util.continuations._
|
||||
|
|
@ -217,19 +217,6 @@ object Future {
|
|||
def apply[T](body: ⇒ T, timeout: Long = Actor.TIMEOUT)(implicit dispatcher: MessageDispatcher): Future[T] =
|
||||
dispatcher.dispatchFuture(() ⇒ body, timeout)
|
||||
|
||||
/**
|
||||
* Construct a completable channel
|
||||
*/
|
||||
def channel(timeout: Long = Actor.TIMEOUT) = new Channel[Any] {
|
||||
val future = empty[Any](timeout)
|
||||
def !(msg: Any) = future completeWithResult msg
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty Future with default timeout
|
||||
*/
|
||||
def empty[T](timeout: Long = Actor.TIMEOUT) = new DefaultPromise[T](timeout)
|
||||
|
||||
import scala.collection.mutable.Builder
|
||||
import scala.collection.generic.CanBuildFrom
|
||||
|
||||
|
|
@ -272,9 +259,11 @@ object Future {
|
|||
*/
|
||||
def flow[A](body: ⇒ A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = {
|
||||
val future = Promise[A](timeout)
|
||||
(reset(future.asInstanceOf[Promise[Any]].completeWithResult(body)): Future[Any]) onComplete { f ⇒
|
||||
val opte = f.exception
|
||||
if (opte.isDefined) future completeWithException (opte.get)
|
||||
(reset(future.asInstanceOf[Promise[Any]].completeWithResult(body)): Future[Any]) onComplete {
|
||||
_.exception match {
|
||||
case Some(e) ⇒ future completeWithException e
|
||||
case None ⇒
|
||||
}
|
||||
}
|
||||
future
|
||||
}
|
||||
|
|
@ -289,7 +278,7 @@ sealed trait Future[+T] {
|
|||
* continuation until the result is available.
|
||||
*
|
||||
* If this Future is untyped (a Future[Nothing]), a type parameter must be explicitly provided or
|
||||
* execution will fail. The normal result of getting a Future from an ActorRef using !!! will return
|
||||
* execution will fail. The normal result of getting a Future from an ActorRef using ? will return
|
||||
* an untyped Future.
|
||||
*/
|
||||
def apply[A >: T](): A @cps[Future[Any]] = shift(this flatMap (_: A ⇒ Future[Any]))
|
||||
|
|
@ -320,12 +309,21 @@ sealed trait Future[+T] {
|
|||
def await(atMost: Duration): Future[T]
|
||||
|
||||
/**
|
||||
* Blocks the current thread until the Future has been completed. Use
|
||||
* caution with this method as it ignores the timeout and will block
|
||||
* indefinitely if the Future is never completed.
|
||||
* Await completion of this Future (as `await`) and return its value if it
|
||||
* conforms to A's erased type.
|
||||
*
|
||||
* def as[A](implicit m: Manifest[A]): Option[A] =
|
||||
* try {
|
||||
* await
|
||||
* value match {
|
||||
* case None ⇒ None
|
||||
* case Some(_: Left[_, _]) ⇒ None
|
||||
* case Some(Right(v)) ⇒ Some(BoxedType(m.erasure).cast(v).asInstanceOf[A])
|
||||
* }
|
||||
* } catch {
|
||||
* case _: Exception ⇒ None
|
||||
* }
|
||||
*/
|
||||
@deprecated("Will be removed after 1.1, it's dangerous and can cause deadlocks, agony and insanity.", "1.1")
|
||||
def awaitBlocking: Future[T]
|
||||
|
||||
/**
|
||||
* Tests whether this Future has been completed.
|
||||
|
|
@ -376,23 +374,41 @@ sealed trait Future[+T] {
|
|||
* Future. If the Future has already been completed, this will apply
|
||||
* immediately.
|
||||
*/
|
||||
def onComplete(func: Future[T] ⇒ Unit): Future[T]
|
||||
def onComplete(func: Future[T] ⇒ Unit): this.type
|
||||
|
||||
/**
|
||||
* When the future is completed with a valid result, apply the provided
|
||||
* PartialFunction to the result.
|
||||
* <pre>
|
||||
* val result = future receive {
|
||||
* val result = future onResult {
|
||||
* case Foo => "foo"
|
||||
* case Bar => "bar"
|
||||
* }.await.result
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
final def receive(pf: PartialFunction[Any, Unit]): Future[T] = onComplete { f ⇒
|
||||
final def onResult(pf: PartialFunction[Any, Unit]): this.type = onComplete { f ⇒
|
||||
val optr = f.result
|
||||
if (optr.isDefined) {
|
||||
val r = optr.get
|
||||
if (pf.isDefinedAt(r)) pf(r)
|
||||
if (pf isDefinedAt r) pf(r)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the future is completed with an exception, apply the provided
|
||||
* PartialFunction to the exception.
|
||||
* <pre>
|
||||
* val result = future onException {
|
||||
* case Foo => "foo"
|
||||
* case Bar => "bar"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
final def onException(pf: PartialFunction[Throwable, Unit]): Future[T] = onComplete { f ⇒
|
||||
val opte = f.exception
|
||||
if (opte.isDefined) {
|
||||
val e = opte.get
|
||||
if (pf isDefinedAt e) pf(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -404,9 +420,9 @@ sealed trait Future[+T] {
|
|||
* Example:
|
||||
* <pre>
|
||||
* val future1 = for {
|
||||
* a <- actor !!! Req("Hello") collect { case Res(x: Int) => x }
|
||||
* b <- actor !!! Req(a) collect { case Res(x: String) => x }
|
||||
* c <- actor !!! Req(7) collect { case Res(x: String) => x }
|
||||
* a <- actor ? Req("Hello") collect { case Res(x: Int) => x }
|
||||
* b <- actor ? Req(a) collect { case Res(x: String) => x }
|
||||
* c <- actor ? Req(7) collect { case Res(x: String) => x }
|
||||
* } yield b + "-" + c
|
||||
* </pre>
|
||||
*/
|
||||
|
|
@ -418,12 +434,12 @@ sealed trait Future[+T] {
|
|||
* a valid result then the new Future will contain the same.
|
||||
* Example:
|
||||
* <pre>
|
||||
* Future(6 / 0) failure { case e: ArithmeticException => 0 } // result: 0
|
||||
* Future(6 / 0) failure { case e: NotFoundException => 0 } // result: exception
|
||||
* Future(6 / 2) failure { case e: ArithmeticException => 0 } // result: 3
|
||||
* Future(6 / 0) recover { case e: ArithmeticException => 0 } // result: 0
|
||||
* Future(6 / 0) recover { case e: NotFoundException => 0 } // result: exception
|
||||
* Future(6 / 2) recover { case e: ArithmeticException => 0 } // result: 3
|
||||
* </pre>
|
||||
*/
|
||||
def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A]
|
||||
def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A]
|
||||
|
||||
/**
|
||||
* Creates a new Future by applying a function to the successful result of
|
||||
|
|
@ -432,14 +448,34 @@ sealed trait Future[+T] {
|
|||
* Example:
|
||||
* <pre>
|
||||
* val future1 = for {
|
||||
* a: Int <- actor !!! "Hello" // returns 5
|
||||
* b: String <- actor !!! a // returns "10"
|
||||
* c: String <- actor !!! 7 // returns "14"
|
||||
* a: Int <- actor ? "Hello" // returns 5
|
||||
* b: String <- actor ? a // returns "10"
|
||||
* c: String <- actor ? 7 // returns "14"
|
||||
* } yield b + "-" + c
|
||||
* </pre>
|
||||
*/
|
||||
def map[A](f: T ⇒ A): Future[A]
|
||||
|
||||
/**
|
||||
* Creates a new Future[A] which is completed with this Future's result if
|
||||
* that conforms to A's erased type or a ClassCastException otherwise.
|
||||
*/
|
||||
final def mapTo[A](implicit m: Manifest[A]): Future[A] = {
|
||||
val fa = new DefaultPromise[A](timeoutInNanos, NANOS)
|
||||
onComplete { ft ⇒
|
||||
fa complete (ft.value.get match {
|
||||
case l: Left[_, _] ⇒ l.asInstanceOf[Either[Throwable, A]]
|
||||
case Right(t) ⇒
|
||||
try {
|
||||
Right(BoxedType(m.erasure).cast(t).asInstanceOf[A])
|
||||
} catch {
|
||||
case e: ClassCastException ⇒ Left(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
fa
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Future by applying a function to the successful result of
|
||||
* this Future, and returns the result of the function as the new Future.
|
||||
|
|
@ -448,21 +484,31 @@ sealed trait Future[+T] {
|
|||
* Example:
|
||||
* <pre>
|
||||
* val future1 = for {
|
||||
* a: Int <- actor !!! "Hello" // returns 5
|
||||
* b: String <- actor !!! a // returns "10"
|
||||
* c: String <- actor !!! 7 // returns "14"
|
||||
* a: Int <- actor ? "Hello" // returns 5
|
||||
* b: String <- actor ? a // returns "10"
|
||||
* c: String <- actor ? 7 // returns "14"
|
||||
* } yield b + "-" + c
|
||||
* </pre>
|
||||
*/
|
||||
def flatMap[A](f: T ⇒ Future[A]): Future[A]
|
||||
|
||||
final def foreach(f: T ⇒ Unit): Unit = onComplete { ft ⇒
|
||||
val optr = ft.result
|
||||
if (optr.isDefined)
|
||||
f(optr.get)
|
||||
final def foreach(f: T ⇒ Unit): Unit = onComplete {
|
||||
_.result match {
|
||||
case Some(v) ⇒ f(v)
|
||||
case None ⇒
|
||||
}
|
||||
}
|
||||
|
||||
def filter(p: Any ⇒ Boolean): Future[Any]
|
||||
final def withFilter(p: T ⇒ Boolean) = new FutureWithFilter[T](this, p)
|
||||
|
||||
final class FutureWithFilter[+A](self: Future[A], p: A ⇒ Boolean) {
|
||||
def foreach(f: A ⇒ Unit): Unit = self filter p foreach f
|
||||
def map[B](f: A ⇒ B): Future[B] = self filter p map f
|
||||
def flatMap[B](f: A ⇒ Future[B]): Future[B] = self filter p flatMap f
|
||||
def withFilter(q: A ⇒ Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x))
|
||||
}
|
||||
|
||||
def filter(p: T ⇒ Boolean): Future[T]
|
||||
|
||||
/**
|
||||
* Returns the current result, throws the exception is one has been raised, else returns None
|
||||
|
|
@ -477,7 +523,7 @@ sealed trait Future[+T] {
|
|||
}
|
||||
|
||||
/* Java API */
|
||||
final def onComplete[A >: T](proc: Procedure[Future[A]]): Future[T] = onComplete(proc(_))
|
||||
final def onComplete[A >: T](proc: Procedure[Future[A]]): this.type = onComplete(proc(_))
|
||||
|
||||
final def map[A >: T, B](f: JFunc[A, B]): Future[B] = map(f(_))
|
||||
|
||||
|
|
@ -495,6 +541,11 @@ object Promise {
|
|||
|
||||
def apply[A](): Promise[A] = apply(Actor.TIMEOUT)
|
||||
|
||||
/**
|
||||
* Construct a completable channel
|
||||
*/
|
||||
def channel(timeout: Long = Actor.TIMEOUT): ActorPromise = new ActorPromise(timeout)
|
||||
|
||||
private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() {
|
||||
override def initialValue = None
|
||||
}
|
||||
|
|
@ -508,26 +559,26 @@ trait Promise[T] extends Future[T] {
|
|||
* Completes this Future with the specified result, if not already completed.
|
||||
* @return this
|
||||
*/
|
||||
def complete(value: Either[Throwable, T]): Future[T]
|
||||
def complete(value: Either[Throwable, T]): this.type
|
||||
|
||||
/**
|
||||
* Completes this Future with the specified result, if not already completed.
|
||||
* @return this
|
||||
*/
|
||||
final def completeWithResult(result: T): Future[T] = complete(Right(result))
|
||||
final def completeWithResult(result: T): this.type = complete(Right(result))
|
||||
|
||||
/**
|
||||
* Completes this Future with the specified exception, if not already completed.
|
||||
* @return this
|
||||
*/
|
||||
final def completeWithException(exception: Throwable): Future[T] = complete(Left(exception))
|
||||
final def completeWithException(exception: Throwable): this.type = complete(Left(exception))
|
||||
|
||||
/**
|
||||
* Completes this Future with the specified other Future, when that Future is completed,
|
||||
* unless this Future has already been completed.
|
||||
* @return this.
|
||||
*/
|
||||
final def completeWith(other: Future[T]): Future[T] = {
|
||||
final def completeWith(other: Future[T]): this.type = {
|
||||
other onComplete { f ⇒ complete(f.value.get) }
|
||||
this
|
||||
}
|
||||
|
|
@ -611,18 +662,6 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
else throw new FutureTimeoutException("Futures timed out after [" + NANOS.toMillis(timeoutInNanos) + "] milliseconds")
|
||||
}
|
||||
|
||||
def awaitBlocking = {
|
||||
_lock.lock
|
||||
try {
|
||||
while (_value.isEmpty) {
|
||||
_signal.await
|
||||
}
|
||||
this
|
||||
} finally {
|
||||
_lock.unlock
|
||||
}
|
||||
}
|
||||
|
||||
def isExpired: Boolean = timeLeft() <= 0
|
||||
|
||||
def value: Option[Either[Throwable, T]] = {
|
||||
|
|
@ -634,14 +673,19 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
}
|
||||
}
|
||||
|
||||
def complete(value: Either[Throwable, T]): DefaultPromise[T] = {
|
||||
def complete(value: Either[Throwable, T]): this.type = {
|
||||
_lock.lock
|
||||
val notifyTheseListeners = try {
|
||||
if (_value.isEmpty && !isExpired) { //Only complete if we aren't expired
|
||||
_value = Some(value)
|
||||
val existingListeners = _listeners
|
||||
_listeners = Nil
|
||||
existingListeners
|
||||
if (_value.isEmpty) { //Only complete if we aren't expired
|
||||
if (!isExpired) {
|
||||
_value = Some(value)
|
||||
val existingListeners = _listeners
|
||||
_listeners = Nil
|
||||
existingListeners
|
||||
} else {
|
||||
_listeners = Nil
|
||||
Nil
|
||||
}
|
||||
} else Nil
|
||||
} finally {
|
||||
_signal.signalAll
|
||||
|
|
@ -676,7 +720,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
this
|
||||
}
|
||||
|
||||
def onComplete(func: Future[T] ⇒ Unit): Promise[T] = {
|
||||
def onComplete(func: Future[T] ⇒ Unit): this.type = {
|
||||
_lock.lock
|
||||
val notifyNow = try {
|
||||
if (_value.isEmpty) {
|
||||
|
|
@ -723,7 +767,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
future
|
||||
}
|
||||
|
||||
final def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = {
|
||||
final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = {
|
||||
val future = new DefaultPromise[A](timeoutInNanos, NANOS)
|
||||
onComplete { self ⇒
|
||||
future complete {
|
||||
|
|
@ -734,7 +778,6 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
else Left(e)
|
||||
} catch {
|
||||
case x: Exception ⇒
|
||||
EventHandler.error(e, this, e.getMessage)
|
||||
Left(x)
|
||||
}
|
||||
case v ⇒ v
|
||||
|
|
@ -782,7 +825,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
future
|
||||
}
|
||||
|
||||
final def filter(p: Any ⇒ Boolean): Future[Any] = {
|
||||
final def filter(p: T ⇒ Boolean): Future[T] = {
|
||||
val future = new DefaultPromise[T](timeoutInNanos, NANOS)
|
||||
onComplete { self ⇒
|
||||
future complete {
|
||||
|
|
@ -811,6 +854,36 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
private def timeLeft(): Long = timeoutInNanos - (currentTimeInNanos - _startTimeInNanos)
|
||||
}
|
||||
|
||||
class ActorPromise(timeout: Long, timeunit: TimeUnit)
|
||||
extends DefaultPromise[Any](timeout, timeunit)
|
||||
with ForwardableChannel {
|
||||
def this() = this(0, MILLIS)
|
||||
def this(timeout: Long) = this(timeout, MILLIS)
|
||||
|
||||
def !(message: Any)(implicit channel: UntypedChannel = NullChannel) = completeWithResult(message)
|
||||
|
||||
def sendException(ex: Throwable) = completeWithException(ex)
|
||||
|
||||
def channel: UntypedChannel = this
|
||||
|
||||
def isUsableOnlyOnce = true
|
||||
def isUsable = !isCompleted
|
||||
def isReplyable = false
|
||||
def canSendException = true
|
||||
|
||||
@deprecated("ActorPromise merged with Channel[Any], just use 'this'", "1.2")
|
||||
def future = this
|
||||
}
|
||||
|
||||
object ActorPromise {
|
||||
def apply(f: Promise[Any]): ActorPromise =
|
||||
new ActorPromise(f.timeoutInNanos, NANOS) {
|
||||
completeWith(f)
|
||||
override def !(message: Any)(implicit channel: UntypedChannel) = f completeWithResult message
|
||||
override def sendException(ex: Throwable) = f completeWithException ex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An already completed Future is seeded with it's result at creation, is useful for when you are participating in
|
||||
* a Future-composition but you already have a value to contribute.
|
||||
|
|
@ -818,11 +891,10 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] {
|
|||
sealed class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise[T] {
|
||||
val value = Some(suppliedValue)
|
||||
|
||||
def complete(value: Either[Throwable, T]): Promise[T] = this
|
||||
def onComplete(func: Future[T] ⇒ Unit): Future[T] = { func(this); this }
|
||||
def await(atMost: Duration): Future[T] = this
|
||||
def await: Future[T] = this
|
||||
def awaitBlocking: Future[T] = this
|
||||
def complete(value: Either[Throwable, T]): this.type = this
|
||||
def onComplete(func: Future[T] ⇒ Unit): this.type = { func(this); this }
|
||||
def await(atMost: Duration): this.type = this
|
||||
def await: this.type = this
|
||||
def isExpired: Boolean = true
|
||||
def timeoutInNanos: Long = 0
|
||||
|
||||
|
|
@ -841,7 +913,7 @@ sealed class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise
|
|||
case _ ⇒ this.asInstanceOf[KeptPromise[A]]
|
||||
}
|
||||
|
||||
final def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = value.get match {
|
||||
final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = value.get match {
|
||||
case Left(e) ⇒
|
||||
try {
|
||||
if (pf isDefinedAt e)
|
||||
|
|
@ -880,7 +952,7 @@ sealed class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise
|
|||
case _ ⇒ this.asInstanceOf[KeptPromise[A]]
|
||||
}
|
||||
|
||||
final def filter(p: Any ⇒ Boolean): Future[Any] = value.get match {
|
||||
final def filter(p: T ⇒ Boolean): Future[T] = value.get match {
|
||||
case Right(r) ⇒
|
||||
try {
|
||||
if (p(r))
|
||||
|
|
|
|||
|
|
@ -16,19 +16,13 @@ import akka.actor._
|
|||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final case class MessageInvocation(receiver: ActorRef,
|
||||
message: Any,
|
||||
sender: Option[ActorRef],
|
||||
senderFuture: Option[Promise[Any]]) {
|
||||
final case class MessageInvocation(val receiver: ActorRef,
|
||||
val message: Any,
|
||||
val channel: UntypedChannel) {
|
||||
if (receiver eq null) throw new IllegalArgumentException("Receiver can't be null")
|
||||
|
||||
def invoke() {
|
||||
try {
|
||||
receiver.invoke(this)
|
||||
} catch {
|
||||
case e: NullPointerException ⇒ throw new ActorInitializationException(
|
||||
"Don't call 'self ! message' in the Actor's constructor (in Scala this means in the body of the class).")
|
||||
}
|
||||
final def invoke() {
|
||||
receiver invoke this
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +141,7 @@ trait MessageDispatcher {
|
|||
|
||||
private[akka] def unregister(actorRef: ActorRef) = {
|
||||
if (uuids remove actorRef.uuid) {
|
||||
cleanUpMailboxFor(actorRef)
|
||||
actorRef.mailbox = null
|
||||
if (uuids.isEmpty && futures.get == 0) {
|
||||
shutdownSchedule match {
|
||||
|
|
@ -161,6 +156,12 @@ trait MessageDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable callback to clean up the mailbox for a given actor,
|
||||
* called when an actor is unregistered.
|
||||
*/
|
||||
protected def cleanUpMailboxFor(actorRef: ActorRef) {}
|
||||
|
||||
/**
|
||||
* Traverses the list of actors (uuids) currently being attached to this dispatcher and stops those actors
|
||||
*/
|
||||
|
|
@ -170,7 +171,7 @@ trait MessageDispatcher {
|
|||
val uuid = i.next()
|
||||
Actor.registry.local.actorFor(uuid) match {
|
||||
case Some(actor) ⇒ actor.stop()
|
||||
case None ⇒ {}
|
||||
case None ⇒
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -233,6 +234,11 @@ trait MessageDispatcher {
|
|||
*/
|
||||
def mailboxSize(actorRef: ActorRef): Int
|
||||
|
||||
/**
|
||||
* Returns the "current" emptiness status of the mailbox for the specified actor
|
||||
*/
|
||||
def mailboxIsEmpty(actorRef: ActorRef): Boolean
|
||||
|
||||
/**
|
||||
* Returns the amount of futures queued for execution
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ class MonitorableThreadFactory(val name: String) extends ThreadFactory {
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object MonitorableThread {
|
||||
val DEFAULT_NAME = "MonitorableThread"
|
||||
val DEFAULT_NAME = "MonitorableThread".intern
|
||||
|
||||
// FIXME use MonitorableThread.created and MonitorableThread.alive in monitoring
|
||||
val created = new AtomicInteger
|
||||
|
|
|
|||
39
akka-actor/src/main/scala/akka/package.scala
Normal file
39
akka-actor/src/main/scala/akka/package.scala
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
import akka.dispatch.{ FutureTimeoutException, Future }
|
||||
import akka.util.Helpers.{ narrow, narrowSilently }
|
||||
|
||||
package object akka {
|
||||
/**
|
||||
* Implicitly converts the given Option[Any] to a AnyOptionAsTypedOption which offers the method <code>as[T]</code>
|
||||
* to convert an Option[Any] to an Option[T].
|
||||
*/
|
||||
implicit def toAnyOptionAsTypedOption(anyOption: Option[Any]) = new AnyOptionAsTypedOption(anyOption)
|
||||
|
||||
/**
|
||||
* Implicitly converts the given Future[_] to a AnyOptionAsTypedOption which offers the method <code>as[T]</code>
|
||||
* to convert an Option[Any] to an Option[T].
|
||||
* This means that the following code is equivalent:
|
||||
* (actor ? "foo").as[Int] (Recommended)
|
||||
*/
|
||||
implicit def futureToAnyOptionAsTypedOption(anyFuture: Future[_]) = new AnyOptionAsTypedOption({
|
||||
try { anyFuture.await } catch { case t: FutureTimeoutException ⇒ }
|
||||
anyFuture.resultOrException
|
||||
})
|
||||
|
||||
private[akka] class AnyOptionAsTypedOption(anyOption: Option[Any]) {
|
||||
/**
|
||||
* Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException
|
||||
* if the actual type is not assignable from the given one.
|
||||
*/
|
||||
def as[T]: Option[T] = narrow[T](anyOption)
|
||||
|
||||
/**
|
||||
* Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible
|
||||
* ClassCastException and return None in that case.
|
||||
*/
|
||||
def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
package akka.routing
|
||||
|
||||
import akka.actor.{ Actor, ActorRef, PoisonPill }
|
||||
import akka.dispatch.{ Promise }
|
||||
|
||||
/**
|
||||
* Actor pooling
|
||||
|
|
@ -116,7 +117,7 @@ trait SmallestMailboxSelector {
|
|||
var take = if (partialFill) math.min(selectionCount, delegates.length) else selectionCount
|
||||
|
||||
while (take > 0) {
|
||||
set = delegates.sortWith(_.mailboxSize < _.mailboxSize).take(take) ++ set //Question, doesn't this risk selecting the same actor multiple times?
|
||||
set = delegates.sortWith((a, b) ⇒ a.dispatcher.mailboxSize(a) < b.dispatcher.mailboxSize(b)).take(take) ++ set //Question, doesn't this risk selecting the same actor multiple times?
|
||||
take -= set.size
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ trait BoundedCapacitor {
|
|||
trait MailboxPressureCapacitor {
|
||||
def pressureThreshold: Int
|
||||
def pressure(delegates: Seq[ActorRef]): Int =
|
||||
delegates count { _.mailboxSize > pressureThreshold }
|
||||
delegates count { a ⇒ a.dispatcher.mailboxSize(a) > pressureThreshold }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -195,7 +196,7 @@ trait MailboxPressureCapacitor {
|
|||
*/
|
||||
trait ActiveFuturesPressureCapacitor {
|
||||
def pressure(delegates: Seq[ActorRef]): Int =
|
||||
delegates count { _.senderFuture.isDefined }
|
||||
delegates count { _.channel.isInstanceOf[Promise[Any]] }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -90,8 +90,6 @@ trait LoadBalancer extends Router { self: Actor ⇒
|
|||
}
|
||||
|
||||
override def broadcast(message: Any) = seq.items.foreach(_ ! message)
|
||||
|
||||
override def isDefinedAt(msg: Any) = seq.exists(_.isDefinedAt(msg))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -106,8 +104,6 @@ abstract class UntypedLoadBalancer extends UntypedRouter {
|
|||
else null
|
||||
|
||||
override def broadcast(message: Any) = seq.items.foreach(_ ! message)
|
||||
|
||||
override def isDefinedAt(msg: Any) = seq.exists(_.isDefinedAt(msg))
|
||||
}
|
||||
|
||||
object Routing {
|
||||
|
|
@ -210,7 +206,7 @@ case class SmallestMailboxFirstIterator(val items: Seq[ActorRef]) extends Infini
|
|||
def this(items: java.util.List[ActorRef]) = this(items.toList)
|
||||
def hasNext = items != Nil
|
||||
|
||||
def next = items.reduceLeft((a1, a2) ⇒ if (a1.mailboxSize < a2.mailboxSize) a1 else a2)
|
||||
def next = items.reduceLeft((a1, a2) ⇒ if (a1.dispatcher.mailboxSize(a1) < a2.dispatcher.mailboxSize(a2)) a1 else a2)
|
||||
|
||||
override def exists(f: ActorRef ⇒ Boolean): Boolean = items.exists(f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,44 +10,44 @@ import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, B
|
|||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
* trait Serializer extends scala.Serializable {
|
||||
* @volatile
|
||||
* var classLoader: Option[ClassLoader] = None
|
||||
* def deepClone(obj: AnyRef): AnyRef = fromBinary(toBinary(obj), Some(obj.getClass))
|
||||
*
|
||||
* def toBinary(obj: AnyRef): Array[Byte]
|
||||
* def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef
|
||||
* }
|
||||
*/
|
||||
trait Serializer extends scala.Serializable {
|
||||
@volatile
|
||||
var classLoader: Option[ClassLoader] = None
|
||||
def deepClone(obj: AnyRef): AnyRef = fromBinary(toBinary(obj), Some(obj.getClass))
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte]
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* object Format {
|
||||
* implicit object Default extends Serializer {
|
||||
* import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, ByteArrayInputStream }
|
||||
* //import org.apache.commons.io.input.ClassLoaderObjectInputStream
|
||||
*
|
||||
* def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
* val bos = new ByteArrayOutputStream
|
||||
* val out = new ObjectOutputStream(bos)
|
||||
* out.writeObject(obj)
|
||||
* out.close()
|
||||
* bos.toByteArray
|
||||
* }
|
||||
*
|
||||
* def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]], classLoader: Option[ClassLoader] = None): AnyRef = {
|
||||
* val in =
|
||||
* //if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) else
|
||||
* new ObjectInputStream(new ByteArrayInputStream(bytes))
|
||||
* val obj = in.readObject
|
||||
* in.close()
|
||||
* obj
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* val defaultSerializerName = Default.getClass.getName
|
||||
* }
|
||||
*/
|
||||
object Format {
|
||||
implicit object Default extends Serializer {
|
||||
import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, ByteArrayInputStream }
|
||||
//import org.apache.commons.io.input.ClassLoaderObjectInputStream
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
val bos = new ByteArrayOutputStream
|
||||
val out = new ObjectOutputStream(bos)
|
||||
out.writeObject(obj)
|
||||
out.close()
|
||||
bos.toByteArray
|
||||
}
|
||||
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = {
|
||||
val in =
|
||||
//if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) else
|
||||
new ObjectInputStream(new ByteArrayInputStream(bytes))
|
||||
val obj = in.readObject
|
||||
in.close()
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
val defaultSerializerName = Default.getClass.getName
|
||||
}
|
||||
|
||||
trait FromBinary[T <: Actor] {
|
||||
def fromBinary(bytes: Array[Byte], act: T): T
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package akka.serialization
|
||||
|
||||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
import akka.util.ReflectiveAccess._
|
||||
import akka.config.Config
|
||||
import akka.config.Config._
|
||||
import akka.actor.{ ActorRef, Actor }
|
||||
|
||||
object Serialization {
|
||||
case class NoSerializerFoundException(m: String) extends Exception(m)
|
||||
|
||||
def serialize(o: AnyRef): Either[Exception, Array[Byte]] =
|
||||
getSerializer(o.getClass).fold((ex) ⇒ Left(ex), (ser) ⇒ Right(ser.toBinary(o)))
|
||||
|
||||
def deserialize(
|
||||
bytes: Array[Byte],
|
||||
clazz: Class[_],
|
||||
classLoader: Option[ClassLoader]): Either[Exception, AnyRef] =
|
||||
getSerializer(clazz)
|
||||
.fold((ex) ⇒ Left(ex),
|
||||
(ser) ⇒ Right(ser.fromBinary(bytes, Some(clazz), classLoader)))
|
||||
|
||||
def getSerializer(clazz: Class[_]): Either[Exception, Serializer] = {
|
||||
Config.serializerMap.get(clazz.getName) match {
|
||||
case Some(serializerName: String) ⇒
|
||||
getClassFor(serializerName) match {
|
||||
case Right(serializer) ⇒ Right(serializer.newInstance.asInstanceOf[Serializer])
|
||||
case Left(exception) ⇒ Left(exception)
|
||||
}
|
||||
case _ ⇒
|
||||
getDefaultSerializer match {
|
||||
case Some(s: Serializer) ⇒ Right(s)
|
||||
case None ⇒ Left(new Exception("No default serializer found for " + clazz))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def getDefaultSerializer = {
|
||||
Config.serializers.get("default") match {
|
||||
case Some(ser: String) ⇒
|
||||
getClassFor(ser) match {
|
||||
case Right(srializer) ⇒ Some(srializer.newInstance.asInstanceOf[Serializer])
|
||||
case Left(exception) ⇒ None
|
||||
}
|
||||
case None ⇒ None
|
||||
}
|
||||
}
|
||||
|
||||
private def getSerializerInstanceForBestMatchClass(
|
||||
configMap: collection.mutable.Map[String, String],
|
||||
cl: Class[_]) = {
|
||||
configMap
|
||||
.find {
|
||||
case (clazzName, ser) ⇒
|
||||
getClassFor(clazzName) match {
|
||||
case Right(clazz) ⇒ clazz.isAssignableFrom(cl)
|
||||
case _ ⇒ false
|
||||
}
|
||||
}
|
||||
.map {
|
||||
case (_, ser) ⇒
|
||||
getClassFor(ser) match {
|
||||
case Right(s) ⇒ Right(s.newInstance.asInstanceOf[Serializer])
|
||||
case _ ⇒ Left(new Exception("Error instantiating " + ser))
|
||||
}
|
||||
}.getOrElse(Left(NoSerializerFoundException("No mapping serializer found for " + cl)))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package akka.serialization
|
||||
|
||||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, ByteArrayInputStream }
|
||||
import akka.util.ClassLoaderObjectInputStream
|
||||
import akka.actor.ActorRef
|
||||
|
||||
trait Serializer extends scala.Serializable {
|
||||
def toBinary(o: AnyRef): Array[Byte]
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]] = None, classLoader: Option[ClassLoader] = None): AnyRef
|
||||
}
|
||||
|
||||
class JavaSerializer extends Serializer {
|
||||
def toBinary(o: AnyRef): Array[Byte] = {
|
||||
val bos = new ByteArrayOutputStream
|
||||
val out = new ObjectOutputStream(bos)
|
||||
out.writeObject(o)
|
||||
out.close()
|
||||
bos.toByteArray
|
||||
}
|
||||
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]] = None,
|
||||
classLoader: Option[ClassLoader] = None): AnyRef = {
|
||||
val in =
|
||||
if (classLoader.isDefined) new ClassLoaderObjectInputStream(classLoader.get, new ByteArrayInputStream(bytes)) else
|
||||
new ObjectInputStream(new ByteArrayInputStream(bytes))
|
||||
val obj = in.readObject
|
||||
in.close()
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
object JavaSerializer extends JavaSerializer
|
||||
object Serializer {
|
||||
val defaultSerializerName = JavaSerializer.getClass.getName
|
||||
}
|
||||
25
akka-actor/src/main/scala/akka/util/BoxedType.scala
Normal file
25
akka-actor/src/main/scala/akka/util/BoxedType.scala
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package akka.util
|
||||
|
||||
import java.{ lang ⇒ jl }
|
||||
|
||||
object BoxedType {
|
||||
|
||||
private val toBoxed = Map[Class[_], Class[_]](
|
||||
classOf[Boolean] -> classOf[jl.Boolean],
|
||||
classOf[Byte] -> classOf[jl.Byte],
|
||||
classOf[Char] -> classOf[jl.Character],
|
||||
classOf[Short] -> classOf[jl.Short],
|
||||
classOf[Int] -> classOf[jl.Integer],
|
||||
classOf[Long] -> classOf[jl.Long],
|
||||
classOf[Float] -> classOf[jl.Float],
|
||||
classOf[Double] -> classOf[jl.Double],
|
||||
classOf[Unit] -> classOf[scala.runtime.BoxedUnit])
|
||||
|
||||
def apply(c: Class[_]): Class[_] = {
|
||||
if (c.isPrimitive) toBoxed(c) else c
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
|
||||
import java.io.{ InputStream, ObjectInputStream, ObjectStreamClass }
|
||||
|
||||
class ClassLoaderObjectInputStream(classLoader: ClassLoader, is: InputStream) extends ObjectInputStream(is) {
|
||||
override protected def resolveClass(objectStreamClass: ObjectStreamClass): Class[_] = {
|
||||
Class.forName(objectStreamClass.getName, false, classLoader) match {
|
||||
case null ⇒ super.resolveClass(objectStreamClass)
|
||||
case clazz ⇒ clazz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import akka.dispatch.{ Future, Promise, MessageInvocation }
|
|||
import akka.config.{ Config, ModuleNotAvailableException }
|
||||
import akka.remoteinterface.RemoteSupport
|
||||
import akka.actor._
|
||||
import DeploymentConfig.Deploy
|
||||
import DeploymentConfig.{ Deploy, ReplicationScheme, ReplicationStrategy }
|
||||
import akka.event.EventHandler
|
||||
import akka.serialization.Format
|
||||
import akka.cluster.ClusterNode
|
||||
|
|
@ -62,6 +62,13 @@ object ReflectiveAccess {
|
|||
None
|
||||
}
|
||||
|
||||
lazy val transactionLogInstance: Option[TransactionLogObject] = getObjectFor("akka.cluster.TransactionLog$") match {
|
||||
case Right(value) ⇒ Some(value)
|
||||
case Left(exception) ⇒
|
||||
EventHandler.debug(this, exception.toString)
|
||||
None
|
||||
}
|
||||
|
||||
lazy val node: ClusterNode = {
|
||||
ensureEnabled()
|
||||
clusterInstance.get.node
|
||||
|
|
@ -72,6 +79,11 @@ object ReflectiveAccess {
|
|||
clusterDeployerInstance.get
|
||||
}
|
||||
|
||||
lazy val transactionLog: TransactionLogObject = {
|
||||
ensureEnabled()
|
||||
transactionLogInstance.get
|
||||
}
|
||||
|
||||
type ClusterDeployer = {
|
||||
def init(deployments: List[Deploy])
|
||||
def shutdown()
|
||||
|
|
@ -94,6 +106,35 @@ object ReflectiveAccess {
|
|||
def toBinary(obj: AnyRef): Array[Byte]
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef
|
||||
}
|
||||
|
||||
type TransactionLogObject = {
|
||||
def newLogFor(
|
||||
id: String,
|
||||
isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer): TransactionLog
|
||||
|
||||
def logFor(
|
||||
id: String,
|
||||
isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer): TransactionLog
|
||||
|
||||
def shutdown()
|
||||
}
|
||||
|
||||
type TransactionLog = {
|
||||
def recordEntry(messageHandle: MessageInvocation, actorRef: ActorRef)
|
||||
def recordEntry(entry: Array[Byte])
|
||||
def recordSnapshot(snapshot: Array[Byte])
|
||||
def entries: Vector[Array[Byte]]
|
||||
def entriesFromLatestSnapshot: Tuple2[Array[Byte], Vector[Array[Byte]]]
|
||||
def entriesInRange(from: Long, to: Long): Vector[Array[Byte]]
|
||||
def latestEntryId: Long
|
||||
def latestSnapshotId: Long
|
||||
def delete()
|
||||
def close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +145,7 @@ object ReflectiveAccess {
|
|||
object RemoteModule {
|
||||
val TRANSPORT = Config.config.getString("akka.remote.layer", "akka.remote.netty.NettyRemoteSupport")
|
||||
|
||||
private[akka] val configDefaultAddress = new InetSocketAddress(Config.hostname, Config.remoteServerPort)
|
||||
val configDefaultAddress = new InetSocketAddress(Config.hostname, Config.remoteServerPort)
|
||||
|
||||
lazy val isEnabled = remoteSupportClass.isDefined
|
||||
|
||||
|
|
@ -166,7 +207,7 @@ object ReflectiveAccess {
|
|||
classloader: ClassLoader = loader): Either[Exception, T] = try {
|
||||
assert(params ne null)
|
||||
assert(args ne null)
|
||||
getClassFor(fqn) match {
|
||||
getClassFor(fqn, classloader) match {
|
||||
case Right(value) ⇒
|
||||
val ctor = value.getDeclaredConstructor(params: _*)
|
||||
ctor.setAccessible(true)
|
||||
|
|
@ -180,7 +221,7 @@ object ReflectiveAccess {
|
|||
|
||||
//Obtains a reference to fqn.MODULE$
|
||||
def getObjectFor[T](fqn: String, classloader: ClassLoader = loader): Either[Exception, T] = try {
|
||||
getClassFor(fqn) match {
|
||||
getClassFor(fqn, classloader) match {
|
||||
case Right(value) ⇒
|
||||
val instance = value.getDeclaredField("MODULE$")
|
||||
instance.setAccessible(true)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,16 @@
|
|||
package akka.camel
|
||||
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy._
|
||||
|
||||
import akka.actor.{ TypedActor, ActorRef }
|
||||
import akka.actor.TypedActor._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object TypedConsumer {
|
||||
|
||||
/**
|
||||
* Applies a function <code>f</code> to <code>actorRef</code> if <code>actorRef</code>
|
||||
* references a typed consumer actor. A valid reference to a typed consumer actor is a
|
||||
|
|
@ -21,18 +24,35 @@ private[camel] object TypedConsumer {
|
|||
* is called with the corresponding <code>method</code> instance and the return value is
|
||||
* added to a list which is then returned by this method.
|
||||
*/
|
||||
def withTypedConsumer[T](actorRef: ActorRef)(f: Method ⇒ T): List[T] = {
|
||||
if (!actorRef.actor.isInstanceOf[TypedActor]) Nil
|
||||
else if (actorRef.homeAddress.isDefined) Nil
|
||||
else {
|
||||
val typedActor = actorRef.actor.asInstanceOf[TypedActor]
|
||||
// TODO: support consumer annotation inheritance
|
||||
// - visit overridden methods in superclasses
|
||||
// - visit implemented method declarations in interfaces
|
||||
val intfClass = typedActor.proxy.getClass
|
||||
val implClass = typedActor.getClass
|
||||
(for (m ← intfClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m)) ++
|
||||
(for (m ← implClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(m))
|
||||
def withTypedConsumer[T](actorRef: ActorRef, typedActor: Option[AnyRef])(f: (AnyRef, Method) ⇒ T): List[T] = {
|
||||
typedActor match {
|
||||
case None ⇒ Nil
|
||||
case Some(tc) ⇒ {
|
||||
withConsumeAnnotatedMethodsOnInterfaces(tc, f) ++
|
||||
withConsumeAnnotatedMethodsonImplClass(tc, actorRef, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private implicit def class2ProxyClass(c: Class[_]) = new ProxyClass(c)
|
||||
|
||||
private def withConsumeAnnotatedMethodsOnInterfaces[T](tc: AnyRef, f: (AnyRef, Method) ⇒ T): List[T] = for {
|
||||
i ← tc.getClass.allInterfaces
|
||||
m ← i.getDeclaredMethods.toList
|
||||
if (m.isAnnotationPresent(classOf[consume]))
|
||||
} yield f(tc, m)
|
||||
|
||||
private def withConsumeAnnotatedMethodsonImplClass[T](tc: AnyRef, actorRef: ActorRef, f: (AnyRef, Method) ⇒ T): List[T] = {
|
||||
val implClass = actorRef.actor.asInstanceOf[TypedActor.TypedActor[AnyRef, AnyRef]].me.getClass
|
||||
for (m ← implClass.getDeclaredMethods.toList; if (m.isAnnotationPresent(classOf[consume]))) yield f(tc, m)
|
||||
|
||||
}
|
||||
|
||||
private class ProxyClass(c: Class[_]) {
|
||||
def allInterfaces: List[Class[_]] = allInterfaces(c.getInterfaces.toList)
|
||||
def allInterfaces(is: List[Class[_]]): List[Class[_]] = is match {
|
||||
case Nil ⇒ Nil
|
||||
case x :: xs ⇒ x :: allInterfaces(x.getInterfaces.toList) ::: allInterfaces(xs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package akka.camel
|
|||
import java.lang.reflect.Method
|
||||
|
||||
import akka.actor._
|
||||
import akka.event.EventHandler
|
||||
import akka.camel.component.TypedActorComponent
|
||||
import akka.event.EventHandler
|
||||
|
||||
/**
|
||||
* Concrete publish requestor that requests publication of typed consumer actor methods on
|
||||
|
|
@ -19,8 +19,8 @@ import akka.camel.component.TypedActorComponent
|
|||
*/
|
||||
private[camel] class TypedConsumerPublishRequestor extends PublishRequestor {
|
||||
def receiveActorRegistryEvent = {
|
||||
case ActorRegistered(actor) ⇒ for (event ← ConsumerMethodRegistered.eventsFor(actor)) deliverCurrentEvent(event)
|
||||
case ActorUnregistered(actor) ⇒ for (event ← ConsumerMethodUnregistered.eventsFor(actor)) deliverCurrentEvent(event)
|
||||
case ActorRegistered(_, actor, typedActor) ⇒ for (event ← ConsumerMethodRegistered.eventsFor(actor, typedActor)) deliverCurrentEvent(event)
|
||||
case ActorUnregistered(_, actor, typedActor) ⇒ for (event ← ConsumerMethodUnregistered.eventsFor(actor, typedActor)) deliverCurrentEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,12 +84,12 @@ private[camel] class ConsumerMethodRouteBuilder(event: ConsumerMethodRegistered)
|
|||
*/
|
||||
private[camel] trait ConsumerMethodEvent extends ConsumerEvent {
|
||||
val actorRef: ActorRef
|
||||
val typedActor: AnyRef
|
||||
val method: Method
|
||||
|
||||
val uuid = actorRef.uuid.toString
|
||||
val methodName = method.getName
|
||||
val methodUuid = "%s_%s" format (uuid, methodName)
|
||||
val typedActor = actorRef.actor.asInstanceOf[TypedActor].proxy
|
||||
|
||||
lazy val routeDefinitionHandler = consumeAnnotation.routeDefinitionHandler.newInstance
|
||||
lazy val consumeAnnotation = method.getAnnotation(classOf[consume])
|
||||
|
|
@ -100,13 +100,13 @@ private[camel] trait ConsumerMethodEvent extends ConsumerEvent {
|
|||
* Event indicating that a typed consumer actor has been registered at the actor registry. For
|
||||
* each <code>@consume</code> annotated typed actor method a separate event is created.
|
||||
*/
|
||||
private[camel] case class ConsumerMethodRegistered(actorRef: ActorRef, method: Method) extends ConsumerMethodEvent
|
||||
private[camel] case class ConsumerMethodRegistered(actorRef: ActorRef, typedActor: AnyRef, method: Method) extends ConsumerMethodEvent
|
||||
|
||||
/**
|
||||
* Event indicating that a typed consumer actor has been unregistered from the actor registry. For
|
||||
* each <code>@consume</code> annotated typed actor method a separate event is created.
|
||||
*/
|
||||
private[camel] case class ConsumerMethodUnregistered(actorRef: ActorRef, method: Method) extends ConsumerMethodEvent
|
||||
private[camel] case class ConsumerMethodUnregistered(actorRef: ActorRef, typedActor: AnyRef, method: Method) extends ConsumerMethodEvent
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
|
|
@ -116,9 +116,9 @@ private[camel] object ConsumerMethodRegistered {
|
|||
* Creates a list of ConsumerMethodRegistered event messages for a typed consumer actor or an empty
|
||||
* list if <code>actorRef</code> doesn't reference a typed consumer actor.
|
||||
*/
|
||||
def eventsFor(actorRef: ActorRef): List[ConsumerMethodRegistered] = {
|
||||
TypedConsumer.withTypedConsumer(actorRef: ActorRef) { m ⇒
|
||||
ConsumerMethodRegistered(actorRef, m)
|
||||
def eventsFor(actorRef: ActorRef, typedActor: Option[AnyRef]): List[ConsumerMethodRegistered] = {
|
||||
TypedConsumer.withTypedConsumer(actorRef, typedActor) { (tc, m) ⇒
|
||||
ConsumerMethodRegistered(actorRef, tc, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,9 +131,9 @@ private[camel] object ConsumerMethodUnregistered {
|
|||
* Creates a list of ConsumerMethodUnregistered event messages for a typed consumer actor or an empty
|
||||
* list if <code>actorRef</code> doesn't reference a typed consumer actor.
|
||||
*/
|
||||
def eventsFor(actorRef: ActorRef): List[ConsumerMethodUnregistered] = {
|
||||
TypedConsumer.withTypedConsumer(actorRef) { m ⇒
|
||||
ConsumerMethodUnregistered(actorRef, m)
|
||||
def eventsFor(actorRef: ActorRef, typedActor: Option[AnyRef]): List[ConsumerMethodUnregistered] = {
|
||||
TypedConsumer.withTypedConsumer(actorRef, typedActor) { (tc, m) ⇒
|
||||
ConsumerMethodUnregistered(actorRef, tc, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,10 +65,10 @@ class TypedActorHolder(uri: String, context: CamelContext, name: String)
|
|||
extends RegistryBean(context, name) {
|
||||
|
||||
/**
|
||||
* Returns an <code>akka.camel.component.TypedActorInfo</code> instance.
|
||||
* Returns an <code>akka.camel.component.BeanInfo</code> instance.
|
||||
*/
|
||||
override def getBeanInfo: BeanInfo =
|
||||
new TypedActorInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
||||
new BeanInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
||||
|
||||
/**
|
||||
* Obtains a typed actor from <code>Actor.registry</code> if the schema is
|
||||
|
|
@ -80,39 +80,6 @@ class TypedActorHolder(uri: String, context: CamelContext, name: String)
|
|||
*/
|
||||
override def getBean: AnyRef = {
|
||||
val internal = uri.startsWith(TypedActorComponent.InternalSchema)
|
||||
if (internal) Actor.registry.typedActorFor(uuidFrom(getName)) getOrElse null else super.getBean
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typed actor meta information.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class TypedActorInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy)
|
||||
extends BeanInfo(context, clazz, strategy) {
|
||||
|
||||
/**
|
||||
* Introspects AspectWerkz proxy classes.
|
||||
*
|
||||
* @param clazz AspectWerkz proxy class.
|
||||
*/
|
||||
protected override def introspect(clazz: Class[_]): Unit = {
|
||||
|
||||
// TODO: fix target class detection in BeanInfo.introspect(Class)
|
||||
// Camel assumes that classes containing a '$$' in the class name
|
||||
// are classes generated with CGLIB. This conflicts with proxies
|
||||
// created from interfaces with AspectWerkz. Once the fix is in
|
||||
// place this method can be removed.
|
||||
|
||||
for (method ← clazz.getDeclaredMethods) {
|
||||
if (isValidMethod(clazz, method)) {
|
||||
introspect(clazz, method)
|
||||
}
|
||||
}
|
||||
val superclass = clazz.getSuperclass
|
||||
if ((superclass ne null) && !superclass.equals(classOf[AnyRef])) {
|
||||
introspect(superclass)
|
||||
}
|
||||
if (internal) Actor.registry.local.typedActorFor(uuidFrom(getName)) getOrElse null else super.getBean
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleErrorHandlingTypedConsumerImpl extends TypedActor implements SampleErrorHandlingTypedConsumer {
|
||||
public class SampleErrorHandlingTypedConsumerImpl implements SampleErrorHandlingTypedConsumer {
|
||||
|
||||
public String willFail(String s) {
|
||||
throw new RuntimeException(String.format("error: %s", s));
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleRemoteTypedConsumerImpl extends TypedActor implements SampleRemoteTypedConsumer {
|
||||
public class SampleRemoteTypedConsumerImpl implements SampleRemoteTypedConsumer {
|
||||
|
||||
public String foo(String s) {
|
||||
return String.format("remote typed actor: %s", s);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import akka.actor.TypedActor;
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedActorImpl extends TypedActor implements SampleTypedActor {
|
||||
public class SampleTypedActorImpl implements SampleTypedActor {
|
||||
|
||||
public String foo(String s) {
|
||||
return String.format("foo: %s", s);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedConsumerImpl extends TypedActor implements SampleTypedConsumer {
|
||||
public class SampleTypedConsumerImpl implements SampleTypedConsumer {
|
||||
|
||||
public String m1(String b, String h) {
|
||||
return "m1: " + b + " " + h;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.TypedActor;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class SampleTypedSingleConsumerImpl extends TypedActor implements SampleTypedSingleConsumer {
|
||||
public class SampleTypedSingleConsumerImpl implements SampleTypedSingleConsumer {
|
||||
|
||||
public void foo(String b) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package akka.camel;
|
||||
|
||||
import akka.actor.Actor;
|
||||
import akka.actor.TypedActor;
|
||||
import akka.actor.TypedActor.Configuration;
|
||||
import akka.dispatch.Dispatchers;
|
||||
import akka.japi.SideEffect;
|
||||
import akka.util.FiniteDuration;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -28,16 +32,18 @@ public class TypedConsumerJavaTestBase {
|
|||
@AfterClass
|
||||
public static void tearDownAfterClass() {
|
||||
stopCamelService();
|
||||
registry().shutdownAll();
|
||||
registry().local().shutdownAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleExceptionThrownByTypedActorAndGenerateCustomResponse() {
|
||||
getMandatoryService().awaitEndpointActivation(1, new SideEffect() {
|
||||
public void apply() {
|
||||
consumer = TypedActor.newInstance(
|
||||
consumer = TypedActor.typedActorOf(
|
||||
SampleErrorHandlingTypedConsumer.class,
|
||||
SampleErrorHandlingTypedConsumerImpl.class);
|
||||
SampleErrorHandlingTypedConsumerImpl.class,
|
||||
new Configuration(new FiniteDuration(5000, "millis"), Dispatchers.defaultGlobalDispatcher()
|
||||
));
|
||||
}
|
||||
});
|
||||
String result = getMandatoryTemplate().requestBody("direct:error-handler-test-java-typed", "hello", String.class);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import org.scalatest.junit.JUnitSuite
|
|||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.actor.TypedActor.Configuration._
|
||||
import akka.camel.TypedCamelTestSupport.{ SetExpectedMessageCount ⇒ SetExpectedTestMessageCount, _ }
|
||||
|
||||
class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
||||
|
|
@ -33,16 +34,16 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
|||
@After
|
||||
def tearDown = {
|
||||
Actor.registry.removeListener(requestor);
|
||||
Actor.registry.shutdownAll
|
||||
Actor.registry.local.shutdownAll
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldReceiveOneConsumerMethodRegisteredEvent = {
|
||||
Actor.registry.addListener(requestor)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl])
|
||||
val latch = (publisher ? SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], defaultConfiguration)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val event = (publisher !! GetRetainedMessage).as[ConsumerMethodRegistered].get
|
||||
val event = (publisher ? GetRetainedMessage).as[ConsumerMethodRegistered].get
|
||||
assert(event.endpointUri === "direct:foo")
|
||||
assert(event.typedActor === obj)
|
||||
assert(event.methodName === "foo")
|
||||
|
|
@ -50,12 +51,12 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
|||
|
||||
@Test
|
||||
def shouldReceiveOneConsumerMethodUnregisteredEvent = {
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl])
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedSingleConsumer], classOf[SampleTypedSingleConsumerImpl], defaultConfiguration)
|
||||
val latch = (publisher ? SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
Actor.registry.addListener(requestor)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val event = (publisher !! GetRetainedMessage).as[ConsumerMethodUnregistered].get
|
||||
val event = (publisher ? GetRetainedMessage).as[ConsumerMethodUnregistered].get
|
||||
assert(event.endpointUri === "direct:foo")
|
||||
assert(event.typedActor === obj)
|
||||
assert(event.methodName === "foo")
|
||||
|
|
@ -64,23 +65,23 @@ class TypedConsumerPublishRequestorTest extends JUnitSuite {
|
|||
@Test
|
||||
def shouldReceiveThreeConsumerMethodRegisteredEvents = {
|
||||
Actor.registry.addListener(requestor)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
val latch = (publisher ? SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], defaultConfiguration)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodRegistered])
|
||||
val events = (publisher !! request).as[List[ConsumerMethodRegistered]].get
|
||||
val events = (publisher ? request).as[List[ConsumerMethodRegistered]].get
|
||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldReceiveThreeConsumerMethodUnregisteredEvents = {
|
||||
val obj = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
val obj = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], defaultConfiguration)
|
||||
val latch = (publisher ? SetExpectedTestMessageCount(3)).as[CountDownLatch].get
|
||||
Actor.registry.addListener(requestor)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
val request = GetRetainedMessages(_.isInstanceOf[ConsumerMethodUnregistered])
|
||||
val events = (publisher !! request).as[List[ConsumerMethodUnregistered]].get
|
||||
val events = (publisher ? request).as[List[ConsumerMethodUnregistered]].get
|
||||
assert(events.map(_.method.getName).sortWith(_ < _) === List("m2", "m3", "m4"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
|
||||
import akka.actor.Actor._
|
||||
import akka.actor._
|
||||
import akka.actor.TypedActor.Configuration._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
|
|
@ -18,13 +19,13 @@ class TypedConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMa
|
|||
var service: CamelService = _
|
||||
|
||||
override protected def beforeAll = {
|
||||
registry.shutdownAll
|
||||
registry.local.shutdownAll
|
||||
service = CamelServiceManager.startCamelService
|
||||
}
|
||||
|
||||
override protected def afterAll = {
|
||||
service.stop
|
||||
registry.shutdownAll
|
||||
registry.local.shutdownAll
|
||||
}
|
||||
|
||||
"A responding, typed consumer" when {
|
||||
|
|
@ -32,7 +33,7 @@ class TypedConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMa
|
|||
"started" must {
|
||||
"support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointActivation(3) {
|
||||
actor = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
actor = TypedActor.typedActorOf(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl], defaultConfiguration)
|
||||
} must be(true)
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m2", "x", "test", "y") must equal("m2: x y")
|
||||
mandatoryTemplate.requestBodyAndHeader("direct:m3", "x", "test", "y") must equal("m3: x y")
|
||||
|
|
@ -62,7 +63,7 @@ class TypedConsumerScalaTest extends WordSpec with BeforeAndAfterAll with MustMa
|
|||
"started" must {
|
||||
"support in-out message exchanges via its endpoints" in {
|
||||
service.awaitEndpointActivation(2) {
|
||||
actor = TypedActor.newInstance(classOf[TestTypedConsumer], classOf[TestTypedConsumerImpl])
|
||||
actor = TypedActor.typedActorOf(classOf[TestTypedConsumer], classOf[TestTypedConsumerImpl], defaultConfiguration)
|
||||
} must be(true)
|
||||
mandatoryTemplate.requestBody("direct:publish-test-3", "x") must equal("foo: x")
|
||||
mandatoryTemplate.requestBody("direct:publish-test-4", "x") must equal("bar: x")
|
||||
|
|
@ -91,7 +92,7 @@ object TypedConsumerScalaTest {
|
|||
def bar(s: String): String
|
||||
}
|
||||
|
||||
class TestTypedConsumerImpl extends TypedActor with TestTypedConsumer {
|
||||
class TestTypedConsumerImpl extends TestTypedConsumer {
|
||||
def foo(s: String) = "foo: %s" format s
|
||||
@consume("direct:publish-test-4")
|
||||
def bar(s: String) = "bar: %s" format s
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import org.apache.camel.impl.{ DefaultCamelContext, SimpleRegistry }
|
|||
import org.scalatest.{ BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec }
|
||||
|
||||
import akka.actor.{ Actor, TypedActor }
|
||||
import akka.actor.TypedActor.Configuration._
|
||||
import akka.camel._
|
||||
import akka.util.ReflectiveAccess.TypedActorModule
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
|
|
@ -19,10 +19,14 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll
|
|||
var typedConsumerUuid: String = _
|
||||
|
||||
override protected def beforeAll = {
|
||||
val typedActor = TypedActor.newInstance(classOf[SampleTypedActor], classOf[SampleTypedActorImpl]) // not a consumer
|
||||
val typedConsumer = TypedActor.newInstance(classOf[SampleTypedConsumer], classOf[SampleTypedConsumerImpl])
|
||||
val typedActor = TypedActor.typedActorOf(
|
||||
classOf[SampleTypedActor],
|
||||
classOf[SampleTypedActorImpl], defaultConfiguration) // not a consumer
|
||||
val typedConsumer = TypedActor.typedActorOf(
|
||||
classOf[SampleTypedConsumer],
|
||||
classOf[SampleTypedConsumerImpl], defaultConfiguration)
|
||||
|
||||
typedConsumerUuid = TypedActorModule.typedActorObjectInstance.get.actorFor(typedConsumer).get.uuid.toString
|
||||
typedConsumerUuid = TypedActor.getActorRefFor(typedConsumer).uuid.toString
|
||||
|
||||
val registry = new SimpleRegistry
|
||||
// external registration
|
||||
|
|
@ -35,7 +39,7 @@ class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll
|
|||
|
||||
override protected def afterAll = {
|
||||
CamelContextManager.stop
|
||||
Actor.registry.shutdownAll
|
||||
Actor.registry.local.shutdownAll
|
||||
}
|
||||
|
||||
feature("Communicate with an internally-registered typed actor using typed-actor-internal endpoint URIs") {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import akka.event.EventHandler
|
|||
*/
|
||||
private[camel] class ConsumerPublishRequestor extends PublishRequestor {
|
||||
def receiveActorRegistryEvent = {
|
||||
case ActorRegistered(_, actor) ⇒ for (event ← ConsumerActorRegistered.eventFor(actor)) deliverCurrentEvent(event)
|
||||
case ActorUnregistered(_, actor) ⇒ for (event ← ConsumerActorUnregistered.eventFor(actor)) deliverCurrentEvent(event)
|
||||
case ActorRegistered(_, actor, None) ⇒ for (event ← ConsumerActorRegistered.eventFor(actor)) deliverCurrentEvent(event)
|
||||
case ActorUnregistered(_, actor, None) ⇒ for (event ← ConsumerActorUnregistered.eventFor(actor)) deliverCurrentEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import org.apache.camel._
|
|||
import org.apache.camel.processor.SendProcessor
|
||||
|
||||
import akka.actor.{ Actor, ActorRef, UntypedActor }
|
||||
import akka.dispatch.ActorPromise
|
||||
|
||||
/**
|
||||
* Support trait for producing messages to Camel endpoints.
|
||||
|
|
@ -96,10 +97,9 @@ trait ProducerSupport { this: Actor ⇒
|
|||
val exchange = createExchange(pattern).fromRequestMessage(cmsg)
|
||||
processor.process(exchange, new AsyncCallback {
|
||||
val producer = self
|
||||
// Need copies of sender and senderFuture references here
|
||||
// since the callback could be done later by another thread.
|
||||
val sender = self.sender
|
||||
val senderFuture = self.senderFuture
|
||||
// Need copies of channel reference here since the callback could be done
|
||||
// later by another thread.
|
||||
val channel = self.channel
|
||||
|
||||
def done(doneSync: Boolean): Unit = {
|
||||
(doneSync, exchange.isFailed) match {
|
||||
|
|
@ -114,10 +114,12 @@ trait ProducerSupport { this: Actor ⇒
|
|||
receiveAfterProduce(result)
|
||||
|
||||
private def dispatchAsync(result: Any) = {
|
||||
if (senderFuture.isDefined)
|
||||
producer.postMessageToMailboxAndCreateFutureResultWithTimeout(result, producer.timeout, sender, senderFuture)
|
||||
else
|
||||
producer.postMessageToMailbox(result, sender)
|
||||
channel match {
|
||||
case _: ActorPromise ⇒
|
||||
producer.postMessageToMailboxAndCreateFutureResultWithTimeout(result, producer.timeout, channel)
|
||||
case _ ⇒
|
||||
producer.postMessageToMailbox(result, channel)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ private[camel] abstract class PublishRequestor extends Actor {
|
|||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] object PublishRequestor {
|
||||
def pastActorRegisteredEvents = for (actor ← Actor.registry.local.actors) yield ActorRegistered(actor.address, actor)
|
||||
def pastActorRegisteredEvents = for (actor ← Actor.registry.local.actors) yield ActorRegistered(actor.address, actor, None)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall
|
|||
* @param message reply message
|
||||
* @param sender ignored
|
||||
*/
|
||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) = {
|
||||
protected[akka] def postMessageToMailbox(message: Any, channel: UntypedChannel) = {
|
||||
message match {
|
||||
case Ack ⇒ { /* no response message to set */ }
|
||||
case msg: Failure ⇒ exchange.fromFailureMessage(msg)
|
||||
|
|
@ -295,7 +295,6 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall
|
|||
}
|
||||
|
||||
def actorClass: Class[_ <: Actor] = unsupported
|
||||
def actorClassName = unsupported
|
||||
def dispatcher_=(md: MessageDispatcher): Unit = unsupported
|
||||
def dispatcher: MessageDispatcher = unsupported
|
||||
def makeRemote(hostname: String, port: Int): Unit = unsupported
|
||||
|
|
@ -313,7 +312,7 @@ private[akka] class AsyncCallbackAdapter(exchange: Exchange, callback: AsyncCall
|
|||
def shutdownLinkedActors: Unit = unsupported
|
||||
def supervisor: Option[ActorRef] = unsupported
|
||||
def homeAddress: Option[InetSocketAddress] = None
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](message: Any, timeout: Long, senderOption: Option[ActorRef], senderFuture: Option[Promise[T]]) = unsupported
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout(message: Any, timeout: Long, channel: UntypedChannel) = unsupported
|
||||
protected[akka] def mailbox: AnyRef = unsupported
|
||||
protected[akka] def mailbox_=(msg: AnyRef): AnyRef = unsupported
|
||||
protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]): Unit = unsupported
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class ConsumerPublishRequestorTest extends JUnitSuite {
|
|||
@Test
|
||||
def shouldReceiveOneConsumerRegisteredEvent = {
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! ActorRegistered(consumer.address, consumer)
|
||||
requestor ! ActorRegistered(consumer.address, consumer, None)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert((publisher !! GetRetainedMessage) ===
|
||||
Some(ConsumerActorRegistered(consumer, consumer.actor.asInstanceOf[Consumer])))
|
||||
|
|
@ -45,7 +45,7 @@ class ConsumerPublishRequestorTest extends JUnitSuite {
|
|||
@Test
|
||||
def shouldReceiveOneConsumerUnregisteredEvent = {
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! ActorUnregistered(consumer.address, consumer)
|
||||
requestor ! ActorUnregistered(consumer.address, consumer, None)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
assert((publisher !! GetRetainedMessage) ===
|
||||
Some(ConsumerActorUnregistered(consumer, consumer.actor.asInstanceOf[Consumer])))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,6 +12,7 @@ import org.I0Itec.zkclient._
|
|||
import org.I0Itec.zkclient.serialize._
|
||||
import org.I0Itec.zkclient.exception._
|
||||
|
||||
import java.util.{ List ⇒ JList }
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference, AtomicInteger }
|
||||
import java.util.concurrent.{ ConcurrentSkipListSet, CopyOnWriteArrayList, Callable, ConcurrentHashMap }
|
||||
import java.net.InetSocketAddress
|
||||
|
|
@ -26,16 +27,22 @@ import RemoteDaemonMessageType._
|
|||
|
||||
import akka.util._
|
||||
import Helpers._
|
||||
|
||||
import akka.actor._
|
||||
import Actor._
|
||||
import Status._
|
||||
import DeploymentConfig.{ ReplicationScheme, ReplicationStrategy, Transient, WriteThrough, WriteBehind }
|
||||
|
||||
import akka.event.EventHandler
|
||||
import akka.dispatch.{ Dispatchers, Future }
|
||||
import akka.remoteinterface._
|
||||
import akka.routing.RouterType
|
||||
import akka.config.Config
|
||||
|
||||
import akka.config.{ Config, Supervision }
|
||||
import Supervision._
|
||||
import Config._
|
||||
import akka.serialization.{ Format, Serializers, Serializer, Compression }
|
||||
|
||||
import akka.serialization.{ Serialization, Serializer, Compression }
|
||||
import Compression.LZF
|
||||
import akka.AkkaException
|
||||
|
||||
|
|
@ -45,7 +52,6 @@ import akka.cluster.ChangeListener._
|
|||
import com.eaio.uuid.UUID
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import java.util.{ List ⇒ JList }
|
||||
|
||||
// FIXME add watch for each node that when the entry for the node is removed then the node shuts itself down
|
||||
// FIXME Provisioning data in ZK (file names etc) and files in S3 and on disk
|
||||
|
|
@ -225,7 +231,7 @@ object Cluster {
|
|||
/**
|
||||
* Creates a new AkkaZkClient.
|
||||
*/
|
||||
def newZkClient: AkkaZkClient = new AkkaZkClient(zooKeeperServers, sessionTimeout, connectionTimeout, defaultSerializer)
|
||||
def newZkClient(): AkkaZkClient = new AkkaZkClient(zooKeeperServers, sessionTimeout, connectionTimeout, defaultSerializer)
|
||||
|
||||
def createQueue(rootPath: String, blocking: Boolean = true) = new ZooKeeperQueue(node.zkClient, rootPath, blocking)
|
||||
|
||||
|
|
@ -282,8 +288,18 @@ class DefaultClusterNode private[akka] (
|
|||
case RemoteClientDisconnected(client, address) ⇒ client.shutdownClientModule()
|
||||
case _ ⇒ //ignore other
|
||||
}
|
||||
}, "akka.cluster.remoteClientLifeCycleListener").start()
|
||||
}, "akka.cluster.RemoteClientLifeCycleListener").start()
|
||||
|
||||
lazy val remoteDaemon = actorOf(new RemoteClusterDaemon(this), RemoteClusterDaemon.ADDRESS).start()
|
||||
|
||||
lazy val remoteDaemonSupervisor = Supervisor(
|
||||
SupervisorConfig(
|
||||
OneForOneStrategy(List(classOf[Exception]), Int.MaxValue, Int.MaxValue), // is infinite restart what we want?
|
||||
Supervise(
|
||||
remoteDaemon,
|
||||
Permanent)
|
||||
:: Nil))
|
||||
|
||||
lazy val remoteService: RemoteSupport = {
|
||||
val remote = new akka.remote.netty.NettyRemoteSupport
|
||||
remote.start(nodeAddress.hostname, nodeAddress.port)
|
||||
|
|
@ -291,6 +307,7 @@ class DefaultClusterNode private[akka] (
|
|||
remote.addListener(remoteClientLifeCycleListener)
|
||||
remote
|
||||
}
|
||||
|
||||
lazy val remoteServerAddress: InetSocketAddress = remoteService.address
|
||||
|
||||
// static nodes
|
||||
|
|
@ -451,7 +468,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, 0, false, format)
|
||||
store(Actor.actorOf(actorClass, address).start, 0, Transient, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationScheme: ReplicationScheme, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, 0, replicationScheme, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -459,7 +484,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, false, format)
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, Transient, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, replicationScheme, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -467,7 +500,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, 0, serializeMailbox, format)
|
||||
store(Actor.actorOf(actorClass, address).start, 0, Transient, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, 0, replicationScheme, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -475,7 +516,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, serializeMailbox, format)
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, Transient, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor of a specific type. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store[T <: Actor](address: String, actorClass: Class[T], replicationFactor: Int, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(Actor.actorOf(actorClass, address).start, replicationFactor, replicationScheme, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -483,7 +532,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, format: Serializer): ClusterNode =
|
||||
store(actorRef, 0, false, format)
|
||||
store(actorRef, 0, Transient, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode =
|
||||
store(actorRef, 0, replicationScheme, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -491,7 +548,15 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, format: Serializer): ClusterNode =
|
||||
store(actorRef, replicationFactor, false, format)
|
||||
store(actorRef, replicationFactor, Transient, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, replicationScheme: ReplicationScheme, format: Serializer): ClusterNode =
|
||||
store(actorRef, replicationFactor, replicationScheme, false, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
|
|
@ -499,20 +564,47 @@ class DefaultClusterNode private[akka] (
|
|||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(actorRef, 0, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Needed to have reflection through structural typing work.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: AnyRef): ClusterNode =
|
||||
store(actorRef, replicationFactor, serializeMailbox, format.asInstanceOf[Serializer])
|
||||
store(actorRef, 0, Transient, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: Serializer): ClusterNode = if (isConnected.isOn) {
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(actorRef, replicationFactor, Transient, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: Serializer): ClusterNode =
|
||||
store(actorRef, 0, replicationScheme, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Needed to have reflection through structural typing work.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, replicationScheme: ReplicationScheme, serializeMailbox: Boolean, format: AnyRef): ClusterNode =
|
||||
store(actorRef, replicationFactor, replicationScheme, serializeMailbox, format.asInstanceOf[Serializer])
|
||||
|
||||
/**
|
||||
* Needed to have reflection through structural typing work.
|
||||
*/
|
||||
def store(actorRef: ActorRef, replicationFactor: Int, serializeMailbox: Boolean, format: AnyRef): ClusterNode =
|
||||
store(actorRef, replicationFactor, Transient, serializeMailbox, format)
|
||||
|
||||
/**
|
||||
* Clusters an actor with UUID. If the actor is already clustered then the clustered version will be updated
|
||||
* with the actor passed in as argument. You can use this to save off snapshots of the actor to a highly
|
||||
* available durable store.
|
||||
*/
|
||||
def store(
|
||||
actorRef: ActorRef,
|
||||
replicationFactor: Int,
|
||||
replicationScheme: ReplicationScheme,
|
||||
serializeMailbox: Boolean,
|
||||
format: Serializer): ClusterNode = if (isConnected.isOn) {
|
||||
|
||||
import akka.serialization.ActorSerialization._
|
||||
|
||||
|
|
@ -523,12 +615,14 @@ class DefaultClusterNode private[akka] (
|
|||
EventHandler.debug(this,
|
||||
"Storing actor [%s] with UUID [%s] in cluster".format(actorRef.address, uuid))
|
||||
|
||||
val actorBytes = if (shouldCompressData) LZF.compress(toBinary(actorRef, serializeMailbox)(format))
|
||||
else toBinary(actorRef)(format)
|
||||
val actorBytes =
|
||||
if (shouldCompressData) LZF.compress(toBinary(actorRef, serializeMailbox, replicationScheme))
|
||||
else toBinary(actorRef, serializeMailbox, replicationScheme)
|
||||
|
||||
val actorRegistryPath = actorRegistryPathFor(uuid)
|
||||
|
||||
// create UUID -> Array[Byte] for actor registry
|
||||
if (zkClient.exists(actorRegistryPath)) zkClient.writeData(actorRegistryPath, actorBytes) // FIXME check for size and warn if too big
|
||||
if (zkClient.exists(actorRegistryPath)) zkClient.writeData(actorRegistryPath, actorBytes) // FIXME Store actor bytes in Data Grid not ZooKeeper
|
||||
else {
|
||||
zkClient.retryUntilConnected(new Callable[Either[String, Exception]]() {
|
||||
def call: Either[String, Exception] = {
|
||||
|
|
@ -575,12 +669,10 @@ class DefaultClusterNode private[akka] (
|
|||
.build
|
||||
|
||||
replicaConnectionsForReplicationFactor(replicationFactor) foreach { connection ⇒
|
||||
(connection !! (command, remoteDaemonAckTimeout)) match {
|
||||
(connection ? (command, remoteDaemonAckTimeout)).as[Status] match {
|
||||
|
||||
case Some(Success) ⇒
|
||||
EventHandler.debug(this,
|
||||
"Replica for [%s] successfully created on [%s]"
|
||||
.format(actorRef.address, connection))
|
||||
EventHandler.debug(this, "Replica for [%s] successfully created".format(actorRef.address))
|
||||
|
||||
case Some(Failure(cause)) ⇒
|
||||
EventHandler.error(cause, this, cause.toString)
|
||||
|
|
@ -588,7 +680,7 @@ class DefaultClusterNode private[akka] (
|
|||
|
||||
case None ⇒
|
||||
val error = new ClusterException(
|
||||
"Operation to instantiate replicas throughout the cluster timed out, cause of error unknow")
|
||||
"Operation to instantiate replicas throughout the cluster timed out")
|
||||
EventHandler.error(error, this, error.toString)
|
||||
throw error
|
||||
}
|
||||
|
|
@ -604,8 +696,9 @@ class DefaultClusterNode private[akka] (
|
|||
releaseActorOnAllNodes(uuid)
|
||||
|
||||
locallyCheckedOutActors.remove(uuid)
|
||||
|
||||
// warning: ordering matters here
|
||||
ignore[ZkNoNodeException](zkClient.deleteRecursive(actorAddressToUuidsPathFor(actorAddressForUuid(uuid)))) // remove ADDRESS to UUID mapping
|
||||
ignore[ZkNoNodeException](zkClient.deleteRecursive(actorAddressToUuidsPathFor(actorAddressForUuid(uuid)))) // FIXME remove ADDRESS to UUID mapping?
|
||||
ignore[ZkNoNodeException](zkClient.deleteRecursive(actorAtNodePathFor(nodeAddress.nodeName, uuid)))
|
||||
ignore[ZkNoNodeException](zkClient.deleteRecursive(actorRegistryPathFor(uuid)))
|
||||
ignore[ZkNoNodeException](zkClient.deleteRecursive(actorLocationsPathFor(uuid)))
|
||||
|
|
@ -650,20 +743,17 @@ class DefaultClusterNode private[akka] (
|
|||
* Checks out an actor for use on this node, e.g. checked out as a 'LocalActorRef' but it makes it available
|
||||
* for remote access through lookup by its UUID.
|
||||
*/
|
||||
def use[T <: Actor](actorAddress: String): Option[LocalActorRef] = use(actorAddress, formatForActor(actorAddress))
|
||||
def use[T <: Actor](actorAddress: String): Option[ActorRef] = use(actorAddress, formatForActor(actorAddress))
|
||||
|
||||
/**
|
||||
* Checks out an actor for use on this node, e.g. checked out as a 'LocalActorRef' but it makes it available
|
||||
* for remote access through lookup by its UUID.
|
||||
*/
|
||||
def use[T <: Actor](actorAddress: String, format: Serializer): Option[LocalActorRef] = if (isConnected.isOn) {
|
||||
def use[T <: Actor](actorAddress: String, format: Serializer): Option[ActorRef] = if (isConnected.isOn) {
|
||||
|
||||
import akka.serialization.ActorSerialization._
|
||||
|
||||
actorUuidsForActorAddress(actorAddress) map { uuid ⇒
|
||||
EventHandler.debug(this,
|
||||
"Checking out actor with UUID [%s] to be used on node [%s] as local actor"
|
||||
.format(uuid, nodeAddress.nodeName))
|
||||
|
||||
ignore[ZkNodeExistsException](zkClient.createPersistent(actorAtNodePathFor(nodeAddress.nodeName, uuid), true))
|
||||
ignore[ZkNodeExistsException](zkClient.createEphemeral(actorLocationsPathFor(uuid, nodeAddress)))
|
||||
|
|
@ -685,15 +775,15 @@ class DefaultClusterNode private[akka] (
|
|||
}) match {
|
||||
case Left(bytes) ⇒
|
||||
locallyCheckedOutActors += (uuid -> bytes)
|
||||
// FIXME switch to ReplicatedActorRef here
|
||||
// val actor = new ReplicatedActorRef(fromBinary[T](bytes, remoteServerAddress)(format))
|
||||
val actor = fromBinary[T](bytes, remoteServerAddress)(format)
|
||||
remoteService.register(UUID_PREFIX + uuid, actor) // clustered refs are always registered and looked up by UUID
|
||||
val actor = fromBinary[T](bytes, remoteServerAddress)
|
||||
EventHandler.debug(this,
|
||||
"Checking out actor [%s] to be used on node [%s] as local actor"
|
||||
.format(actor, nodeAddress.nodeName))
|
||||
actor.start()
|
||||
actor.asInstanceOf[LocalActorRef]
|
||||
actor
|
||||
case Right(exception) ⇒ throw exception
|
||||
}
|
||||
} headOption // FIXME should not be an array at all coming here
|
||||
} headOption // FIXME should not be an array at all coming here but an Option[ActorRef]
|
||||
} else None
|
||||
|
||||
/**
|
||||
|
|
@ -703,14 +793,15 @@ class DefaultClusterNode private[akka] (
|
|||
isConnected ifOn {
|
||||
EventHandler.debug(this,
|
||||
"Using (checking out) all actors with UUID [%s] on all nodes in cluster".format(uuid))
|
||||
|
||||
val command = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(USE)
|
||||
.setActorUuid(uuidToUuidProtocol(uuid))
|
||||
.build
|
||||
|
||||
membershipNodes foreach { node ⇒
|
||||
replicaConnections.get(node) foreach {
|
||||
case (_, connection) ⇒
|
||||
connection ! command
|
||||
case (_, connection) ⇒ connection ! command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -774,8 +865,8 @@ class DefaultClusterNode private[akka] (
|
|||
def ref(actorAddress: String, router: RouterType): ActorRef = if (isConnected.isOn) {
|
||||
val addresses = addressesForActor(actorAddress)
|
||||
EventHandler.debug(this,
|
||||
"Checking out cluster actor ref with address [%s] and router [%s] connected to [\n\t%s]"
|
||||
.format(actorAddress, router, addresses.mkString("\n\t")))
|
||||
"Checking out cluster actor ref with address [%s] and router [%s] on [%s] connected to [\n\t%s]"
|
||||
.format(actorAddress, router, remoteServerAddress, addresses.map(_._2).mkString("\n\t")))
|
||||
|
||||
val actorRef = Router newRouter (router, addresses, actorAddress, Actor.TIMEOUT)
|
||||
addresses foreach { case (_, address) ⇒ clusterActorRefs.put(address, actorRef) }
|
||||
|
|
@ -953,11 +1044,15 @@ class DefaultClusterNode private[akka] (
|
|||
* Send a function 'Function0[Unit]' to be invoked on a random number of nodes (defined by 'replicationFactor' argument).
|
||||
*/
|
||||
def send(f: Function0[Unit], replicationFactor: Int) {
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN0_UNIT)
|
||||
.setPayload(ByteString.copyFrom(Serializers.Java.toBinary(f)))
|
||||
.build
|
||||
replicaConnectionsForReplicationFactor(replicationFactor) foreach (_ ! message)
|
||||
Serialization.serialize(f) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(bytes) ⇒
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN0_UNIT)
|
||||
.setPayload(ByteString.copyFrom(bytes))
|
||||
.build
|
||||
replicaConnectionsForReplicationFactor(replicationFactor) foreach (_ ! message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -965,12 +1060,16 @@ class DefaultClusterNode private[akka] (
|
|||
* Returns an 'Array' with all the 'Future's from the computation.
|
||||
*/
|
||||
def send(f: Function0[Any], replicationFactor: Int): List[Future[Any]] = {
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN0_ANY)
|
||||
.setPayload(ByteString.copyFrom(Serializers.Java.toBinary(f)))
|
||||
.build
|
||||
val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ !!! message)
|
||||
results.toList.asInstanceOf[List[Future[Any]]]
|
||||
Serialization.serialize(f) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(bytes) ⇒
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN0_ANY)
|
||||
.setPayload(ByteString.copyFrom(bytes))
|
||||
.build
|
||||
val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ ? message)
|
||||
results.toList.asInstanceOf[List[Future[Any]]]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -978,11 +1077,15 @@ class DefaultClusterNode private[akka] (
|
|||
* with the argument speficied.
|
||||
*/
|
||||
def send(f: Function1[Any, Unit], arg: Any, replicationFactor: Int) {
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN1_ARG_UNIT)
|
||||
.setPayload(ByteString.copyFrom(Serializers.Java.toBinary((f, arg))))
|
||||
.build
|
||||
replicaConnectionsForReplicationFactor(replicationFactor) foreach (_ ! message)
|
||||
Serialization.serialize((f, arg)) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(bytes) ⇒
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN1_ARG_UNIT)
|
||||
.setPayload(ByteString.copyFrom(bytes))
|
||||
.build
|
||||
replicaConnectionsForReplicationFactor(replicationFactor) foreach (_ ! message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -991,12 +1094,16 @@ class DefaultClusterNode private[akka] (
|
|||
* Returns an 'Array' with all the 'Future's from the computation.
|
||||
*/
|
||||
def send(f: Function1[Any, Any], arg: Any, replicationFactor: Int): List[Future[Any]] = {
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN1_ARG_ANY)
|
||||
.setPayload(ByteString.copyFrom(Serializers.Java.toBinary((f, arg))))
|
||||
.build
|
||||
val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ !!! message)
|
||||
results.toList.asInstanceOf[List[Future[Any]]]
|
||||
Serialization.serialize((f, arg)) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(bytes) ⇒
|
||||
val message = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FUNCTION_FUN1_ARG_ANY)
|
||||
.setPayload(ByteString.copyFrom(bytes))
|
||||
.build
|
||||
val results = replicaConnectionsForReplicationFactor(replicationFactor) map (_ ? message)
|
||||
results.toList.asInstanceOf[List[Future[Any]]]
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================
|
||||
|
|
@ -1086,9 +1193,9 @@ class DefaultClusterNode private[akka] (
|
|||
.format(nodeAddress.clusterName, nodeAddress.nodeName, nodeAddress.port, zkServerAddresses, serializer))
|
||||
EventHandler.info(this, "Starting up remote server [%s]".format(remoteServerAddress.toString))
|
||||
createRootClusterNode()
|
||||
val isLeader = joinLeaderElection
|
||||
val isLeader = joinLeaderElection()
|
||||
if (isLeader) createNodeStructureIfNeeded()
|
||||
registerListeners
|
||||
registerListeners()
|
||||
joinMembershipNode()
|
||||
joinActorsAtAddressNode()
|
||||
fetchMembershipChildrenNodes()
|
||||
|
|
@ -1125,7 +1232,8 @@ class DefaultClusterNode private[akka] (
|
|||
|
||||
if (numberOfReplicas < replicationFactor) {
|
||||
throw new IllegalArgumentException(
|
||||
"Replication factor [" + replicationFactor + "] is greater than the number of available nodes [" + numberOfReplicas + "]")
|
||||
"Replication factor [" + replicationFactor +
|
||||
"] is greater than the number of available nodes [" + numberOfReplicas + "]")
|
||||
} else if (numberOfReplicas == replicationFactor) {
|
||||
replicas = replicas ++ replicaConnectionsAsArray
|
||||
} else {
|
||||
|
|
@ -1164,7 +1272,7 @@ class DefaultClusterNode private[akka] (
|
|||
} catch {
|
||||
case e: ZkNodeExistsException ⇒
|
||||
val error = new ClusterException("Can't join the cluster. The node name [" + nodeAddress.nodeName + "] is already in by another node")
|
||||
EventHandler.error(error, this, "")
|
||||
EventHandler.error(error, this, error.toString)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
@ -1173,7 +1281,7 @@ class DefaultClusterNode private[akka] (
|
|||
ignore[ZkNodeExistsException](zkClient.createPersistent(actorsAtNodePathFor(nodeAddress.nodeName)))
|
||||
}
|
||||
|
||||
private[cluster] def joinLeaderElection: Boolean = {
|
||||
private[cluster] def joinLeaderElection(): Boolean = {
|
||||
EventHandler.info(this, "Node [%s] is joining leader election".format(nodeAddress.nodeName))
|
||||
leaderLock.lock
|
||||
}
|
||||
|
|
@ -1217,22 +1325,26 @@ class DefaultClusterNode private[akka] (
|
|||
homeAddress.setAccessible(true)
|
||||
homeAddress.set(actor, Some(remoteServerAddress))
|
||||
|
||||
remoteService.register(uuid, actor)
|
||||
remoteService.register(actorAddress, actor)
|
||||
}
|
||||
}
|
||||
|
||||
// notify all available nodes that they should fail-over all connections from 'from' to 'to'
|
||||
val from = nodeNameToAddress.get(failedNodeName)
|
||||
val to = remoteServerAddress
|
||||
val command = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FAIL_OVER_CONNECTIONS)
|
||||
.setPayload(ByteString.copyFrom(Serializers.Java.toBinary((from, to))))
|
||||
.build
|
||||
membershipNodes foreach { node ⇒
|
||||
replicaConnections.get(node) foreach {
|
||||
case (_, connection) ⇒
|
||||
connection ! command
|
||||
}
|
||||
Serialization.serialize((from, to)) match {
|
||||
case Left(error) ⇒ throw error
|
||||
case Right(bytes) ⇒
|
||||
val command = RemoteDaemonMessageProtocol.newBuilder
|
||||
.setMessageType(FAIL_OVER_CONNECTIONS)
|
||||
.setPayload(ByteString.copyFrom(bytes))
|
||||
.build
|
||||
membershipNodes foreach { node ⇒
|
||||
replicaConnections.get(node) foreach {
|
||||
case (_, connection) ⇒
|
||||
connection ! command
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1302,7 +1414,7 @@ class DefaultClusterNode private[akka] (
|
|||
}
|
||||
}
|
||||
|
||||
private def registerListeners = {
|
||||
private def registerListeners() = {
|
||||
zkClient.subscribeStateChanges(stateListener)
|
||||
zkClient.subscribeChildChanges(MEMBERSHIP_NODE, membershipListener)
|
||||
}
|
||||
|
|
@ -1456,12 +1568,10 @@ trait ErrorHandler {
|
|||
object RemoteClusterDaemon {
|
||||
val ADDRESS = "akka-cluster-daemon".intern
|
||||
|
||||
// FIXME configure functionServerDispatcher to what?
|
||||
val functionServerDispatcher = Dispatchers.newDispatcher("akka:cloud:cluster:function:server").build
|
||||
// FIXME configure computeGridDispatcher to what?
|
||||
val computeGridDispatcher = Dispatchers.newDispatcher("akka:cloud:cluster:compute-grid").build
|
||||
}
|
||||
|
||||
// FIXME supervise RemoteClusterDaemon
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
|
|
@ -1472,6 +1582,10 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
|
||||
self.dispatcher = Dispatchers.newPinnedDispatcher(self)
|
||||
|
||||
override def preRestart(reason: Throwable) {
|
||||
EventHandler.debug(this, "RemoteClusterDaemon failed due to [%s] restarting...".format(reason))
|
||||
}
|
||||
|
||||
def receive: Receive = {
|
||||
case message: RemoteDaemonMessageProtocol ⇒
|
||||
EventHandler.debug(this, "Received command to RemoteClusterDaemon [%s]".format(message))
|
||||
|
|
@ -1528,7 +1642,7 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
|
||||
case FUNCTION_FUN0_UNIT ⇒
|
||||
actorOf(new Actor() {
|
||||
self.dispatcher = functionServerDispatcher
|
||||
self.dispatcher = computeGridDispatcher
|
||||
|
||||
def receive = {
|
||||
case f: Function0[Unit] ⇒ try {
|
||||
|
|
@ -1541,7 +1655,7 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
|
||||
case FUNCTION_FUN0_ANY ⇒
|
||||
actorOf(new Actor() {
|
||||
self.dispatcher = functionServerDispatcher
|
||||
self.dispatcher = computeGridDispatcher
|
||||
|
||||
def receive = {
|
||||
case f: Function0[Any] ⇒ try {
|
||||
|
|
@ -1554,7 +1668,7 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
|
||||
case FUNCTION_FUN1_ARG_UNIT ⇒
|
||||
actorOf(new Actor() {
|
||||
self.dispatcher = functionServerDispatcher
|
||||
self.dispatcher = computeGridDispatcher
|
||||
|
||||
def receive = {
|
||||
case (fun: Function[Any, Unit], param: Any) ⇒ try {
|
||||
|
|
@ -1567,7 +1681,7 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
|
||||
case FUNCTION_FUN1_ARG_ANY ⇒
|
||||
actorOf(new Actor() {
|
||||
self.dispatcher = functionServerDispatcher
|
||||
self.dispatcher = computeGridDispatcher
|
||||
|
||||
def receive = {
|
||||
case (fun: Function[Any, Unit], param: Any) ⇒ try {
|
||||
|
|
@ -1583,6 +1697,6 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor {
|
|||
}
|
||||
|
||||
private def payloadFor[T](message: RemoteDaemonMessageProtocol, clazz: Class[T]): T = {
|
||||
Serializers.Java.fromBinary(message.getPayload.toByteArray, Some(clazz)).asInstanceOf[T]
|
||||
Serialization.serialize(message.getPayload.toByteArray, Some(clazz)).asInstanceOf[T]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,17 @@ package akka.cluster
|
|||
import Cluster._
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import Actor._
|
||||
import akka.dispatch._
|
||||
import akka.util._
|
||||
import ReflectiveAccess._
|
||||
import ClusterModule._
|
||||
import akka.event.EventHandler
|
||||
import akka.dispatch.Promise
|
||||
import akka.dispatch.Future
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.{ Map ⇒ JMap }
|
||||
|
||||
import com.eaio.uuid.UUID
|
||||
|
||||
|
|
@ -20,32 +25,39 @@ import com.eaio.uuid.UUID
|
|||
*/
|
||||
class ClusterActorRef private[akka] (
|
||||
inetSocketAddresses: Array[Tuple2[UUID, InetSocketAddress]],
|
||||
actorAddress: String,
|
||||
timeout: Long,
|
||||
val replicationStrategy: ReplicationStrategy)
|
||||
extends RemoteActorRef(null, actorAddress, timeout, None) { // FIXME UGLY HACK - should not extend RemoteActorRef
|
||||
this: ClusterActorRef with Router.Router ⇒
|
||||
val address: String,
|
||||
_timeout: Long)
|
||||
extends ActorRef with ScalaActorRef { this: Router.Router ⇒
|
||||
|
||||
EventHandler.debug(this,
|
||||
"Creating a ClusterActorRef for actor with address [%s] with connections [\n\t%s]"
|
||||
.format(actorAddress, inetSocketAddresses.mkString("\n\t")))
|
||||
timeout = _timeout
|
||||
|
||||
private[akka] val inetSocketAddressToActorRefMap = new AtomicReference[Map[InetSocketAddress, ActorRef]](
|
||||
(Map[InetSocketAddress, ActorRef]() /: inetSocketAddresses) {
|
||||
case (map, (uuid, inetSocketAddress)) ⇒ map + (inetSocketAddress -> createRemoteActorRef(actorAddress, inetSocketAddress))
|
||||
case (map, (uuid, inetSocketAddress)) ⇒ map + (inetSocketAddress -> createRemoteActorRef(address, inetSocketAddress))
|
||||
})
|
||||
|
||||
ClusterModule.ensureEnabled()
|
||||
start()
|
||||
|
||||
def connections: Map[InetSocketAddress, ActorRef] = inetSocketAddressToActorRefMap.get
|
||||
|
||||
override def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit =
|
||||
route(message)(senderOption)
|
||||
override def postMessageToMailbox(message: Any, channel: UntypedChannel): Unit = {
|
||||
val sender = channel match {
|
||||
case ref: ActorRef ⇒ Some(ref)
|
||||
case _ ⇒ None
|
||||
}
|
||||
route(message)(sender)
|
||||
}
|
||||
|
||||
override def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
override def postMessageToMailboxAndCreateFutureResultWithTimeout(
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[Promise[T]]): Promise[T] = {
|
||||
route[T](message, timeout)(senderOption).asInstanceOf[Promise[T]]
|
||||
channel: UntypedChannel): Future[Any] = {
|
||||
val sender = channel match {
|
||||
case ref: ActorRef ⇒ Some(ref)
|
||||
case _ ⇒ None
|
||||
}
|
||||
route[Any](message, timeout)(sender)
|
||||
}
|
||||
|
||||
private[akka] def failOver(fromInetSocketAddress: InetSocketAddress, toInetSocketAddress: InetSocketAddress) {
|
||||
|
|
@ -60,4 +72,53 @@ class ClusterActorRef private[akka] (
|
|||
private def createRemoteActorRef(actorAddress: String, inetSocketAddress: InetSocketAddress) = {
|
||||
RemoteActorRef(inetSocketAddress, actorAddress, Actor.TIMEOUT, None)
|
||||
}
|
||||
|
||||
def start(): ActorRef = synchronized {
|
||||
_status = ActorRefInternals.RUNNING
|
||||
this
|
||||
}
|
||||
|
||||
def stop() {
|
||||
synchronized {
|
||||
if (_status == ActorRefInternals.RUNNING) {
|
||||
_status = ActorRefInternals.SHUTDOWN
|
||||
postMessageToMailbox(RemoteActorSystemMessage.Stop, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==== NOT SUPPORTED ====
|
||||
// FIXME move these methods and the same ones in RemoteActorRef to a base class - now duplicated
|
||||
def dispatcher_=(md: MessageDispatcher) {
|
||||
unsupported
|
||||
}
|
||||
def dispatcher: MessageDispatcher = unsupported
|
||||
def link(actorRef: ActorRef) {
|
||||
unsupported
|
||||
}
|
||||
def unlink(actorRef: ActorRef) {
|
||||
unsupported
|
||||
}
|
||||
def startLink(actorRef: ActorRef): ActorRef = unsupported
|
||||
def supervisor: Option[ActorRef] = unsupported
|
||||
def linkedActors: JMap[Uuid, ActorRef] = unsupported
|
||||
protected[akka] def mailbox: AnyRef = unsupported
|
||||
protected[akka] def mailbox_=(value: AnyRef): AnyRef = unsupported
|
||||
protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable) {
|
||||
unsupported
|
||||
}
|
||||
protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) {
|
||||
unsupported
|
||||
}
|
||||
protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) {
|
||||
unsupported
|
||||
}
|
||||
protected[akka] def invoke(messageHandle: MessageInvocation) {
|
||||
unsupported
|
||||
}
|
||||
protected[akka] def supervisor_=(sup: Option[ActorRef]) {
|
||||
unsupported
|
||||
}
|
||||
protected[akka] def actorInstance: AtomicReference[Actor] = unsupported
|
||||
private def unsupported = throw new UnsupportedOperationException("Not supported for RemoteActorRef")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import java.util.concurrent.atomic.AtomicReference
|
|||
/**
|
||||
* A ClusterDeployer is responsible for deploying a Deploy.
|
||||
*
|
||||
* big question is: what does Deploy mean?
|
||||
* FIXME Document: what does Deploy mean?
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
|
|
@ -35,11 +35,16 @@ object ClusterDeployer {
|
|||
val clusterName = Cluster.name
|
||||
val nodeName = Config.nodename
|
||||
val clusterPath = "/%s" format clusterName
|
||||
val clusterDeploymentLockPath = clusterPath + "/deployment-lock"
|
||||
|
||||
val deploymentPath = clusterPath + "/deployment"
|
||||
val baseNodes = List(clusterPath, clusterDeploymentLockPath, deploymentPath)
|
||||
val deploymentAddressPath = deploymentPath + "/%s"
|
||||
|
||||
val deploymentCoordinationPath = clusterPath + "/deployment-coordination"
|
||||
val deploymentInProgressLockPath = deploymentCoordinationPath + "/in-progress"
|
||||
val isDeploymentCompletedInClusterLockPath = deploymentCoordinationPath + "/completed" // should not be part of baseNodes
|
||||
|
||||
val baseNodes = List(clusterPath, deploymentPath, deploymentCoordinationPath, deploymentInProgressLockPath)
|
||||
|
||||
private val isConnected = new Switch(false)
|
||||
private val deploymentCompleted = new CountDownLatch(1)
|
||||
|
||||
|
|
@ -49,7 +54,7 @@ object ClusterDeployer {
|
|||
Cluster.connectionTimeout,
|
||||
Cluster.defaultSerializer)
|
||||
|
||||
private val clusterDeploymentLockListener = new LockListener {
|
||||
private val deploymentInProgressLockListener = new LockListener {
|
||||
def lockAcquired() {
|
||||
EventHandler.debug(this, "Clustered deployment started")
|
||||
}
|
||||
|
|
@ -60,13 +65,11 @@ object ClusterDeployer {
|
|||
}
|
||||
}
|
||||
|
||||
private val deploymentLock = new WriteLock(
|
||||
zkClient.connection.getZookeeper, clusterDeploymentLockPath, null, clusterDeploymentLockListener) {
|
||||
private val ownerIdField = classOf[WriteLock].getDeclaredField("ownerId")
|
||||
ownerIdField.setAccessible(true)
|
||||
|
||||
def leader: String = ownerIdField.get(this).asInstanceOf[String]
|
||||
}
|
||||
private val deploymentInProgressLock = new WriteLock(
|
||||
zkClient.connection.getZookeeper,
|
||||
deploymentInProgressLockPath,
|
||||
null,
|
||||
deploymentInProgressLockListener)
|
||||
|
||||
private val systemDeployments: List[Deploy] = Nil
|
||||
|
||||
|
|
@ -79,6 +82,7 @@ object ClusterDeployer {
|
|||
deployment ← zkClient.readData(deploymentAddressPath.format(child)).asInstanceOf[Deploy]
|
||||
} zkClient.delete(deploymentAddressPath.format(deployment.address))
|
||||
|
||||
invalidateDeploymentInCluster()
|
||||
} catch {
|
||||
case e: Exception ⇒
|
||||
handleError(new DeploymentException("Could not undeploy all deployment data in ZooKeeper due to: " + e))
|
||||
|
|
@ -124,8 +128,6 @@ object ClusterDeployer {
|
|||
}
|
||||
|
||||
private[akka] def init(deployments: List[Deploy]) {
|
||||
println("===============================================================")
|
||||
println("------------ INIT 1")
|
||||
isConnected switchOn {
|
||||
EventHandler.info(this, "Initializing cluster deployer")
|
||||
|
||||
|
|
@ -141,31 +143,21 @@ object ClusterDeployer {
|
|||
}
|
||||
}
|
||||
|
||||
println("------------ INIT 2")
|
||||
val allDeployments = deployments ::: systemDeployments
|
||||
|
||||
///===========================================================
|
||||
// FIXME need a flag 'deploymentDone' in ZK and to wrap the deployment in 'if (!deploymentDone) { .. }', since now the deployment is only protected by lock during the actual deployment, if node comes in later then deployment is repeated on that node again
|
||||
///===========================================================
|
||||
if (!isDeploymentCompletedInCluster) {
|
||||
if (deploymentInProgressLock.lock()) {
|
||||
// try to be the one doing the clustered deployment
|
||||
EventHandler.info(this, "Deploying to cluster [\n" + allDeployments.mkString("\n\t") + "\n]")
|
||||
allDeployments foreach (deploy(_)) // deploy
|
||||
markDeploymentCompletedInCluster()
|
||||
deploymentInProgressLock.unlock() // signal deployment complete
|
||||
|
||||
if (deploymentLock.lock()) {
|
||||
println("------------ INIT 3")
|
||||
// try to be the one doing the clustered deployment
|
||||
EventHandler.info(this, "Deploying to cluster [\n" + allDeployments.mkString("\n\t") + "\n]")
|
||||
|
||||
println("------------ INIT 4")
|
||||
allDeployments foreach (deploy(_)) // deploy
|
||||
println("------------ INIT 5")
|
||||
|
||||
// FIXME need to set deployment done flag
|
||||
|
||||
deploymentLock.unlock() // signal deployment complete
|
||||
} else {
|
||||
println("------------ INIT WAITING")
|
||||
deploymentCompleted.await() // wait until deployment is completed by other "master" node
|
||||
} else {
|
||||
deploymentCompleted.await() // wait until deployment is completed by other "master" node
|
||||
}
|
||||
}
|
||||
|
||||
println("------------ INIT 6")
|
||||
// fetch clustered deployments and deploy them locally
|
||||
fetchDeploymentsFromCluster foreach (LocalDeployer.deploy(_))
|
||||
}
|
||||
|
|
@ -183,14 +175,29 @@ object ClusterDeployer {
|
|||
zkClient.writeData(path, deployment)
|
||||
} catch {
|
||||
case e: NullPointerException ⇒
|
||||
handleError(new DeploymentException("Could not store deployment data [" + deployment + "] in ZooKeeper since client session is closed"))
|
||||
handleError(new DeploymentException(
|
||||
"Could not store deployment data [" + deployment +
|
||||
"] in ZooKeeper since client session is closed"))
|
||||
case e: Exception ⇒
|
||||
handleError(new DeploymentException("Could not store deployment data [" + deployment + "] in ZooKeeper due to: " + e))
|
||||
handleError(new DeploymentException(
|
||||
"Could not store deployment data [" +
|
||||
deployment + "] in ZooKeeper due to: " + e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def markDeploymentCompletedInCluster() {
|
||||
ignore[ZkNodeExistsException](zkClient.create(isDeploymentCompletedInClusterLockPath, null, CreateMode.PERSISTENT))
|
||||
}
|
||||
|
||||
private def isDeploymentCompletedInCluster = zkClient.exists(isDeploymentCompletedInClusterLockPath)
|
||||
|
||||
// FIXME in future - add watch to this path to be able to trigger redeployment, and use this method to trigger redeployment
|
||||
private def invalidateDeploymentInCluster() {
|
||||
ignore[ZkNoNodeException](zkClient.delete(isDeploymentCompletedInClusterLockPath))
|
||||
}
|
||||
|
||||
private def ensureRunning[T](body: ⇒ T): T = {
|
||||
if (isConnected.isOn) body
|
||||
else throw new IllegalStateException("ClusterDeployer is not running")
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
package akka.cluster
|
||||
|
||||
import zookeeper.AkkaZkClient
|
||||
import akka.AkkaException
|
||||
import org.apache.zookeeper.{ KeeperException, CreateMode }
|
||||
import org.apache.zookeeper.data.Stat
|
||||
import scala.Some
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.apache.zookeeper.KeeperException.NoNodeException
|
||||
|
||||
/**
|
||||
* Simple abstraction to store an Array of bytes based on some String key.
|
||||
*
|
||||
* Nothing is being said about ACID, transactions etc. It depends on the implementation
|
||||
* of this Storage interface of what is and isn't done on the lowest level.
|
||||
*
|
||||
* TODO: Perhaps add a version to the store to prevent lost updates using optimistic locking.
|
||||
* (This is supported by ZooKeeper).
|
||||
* TODO: Class is up for better names.
|
||||
* TODO: Instead of a String as key, perhaps also a byte-array.
|
||||
*/
|
||||
trait RawStorage {
|
||||
|
||||
/**
|
||||
* Inserts a byte-array based on some key.
|
||||
*
|
||||
* @throws NodeExistsException when a Node with the given Key already exists.
|
||||
*/
|
||||
def insert(key: String, bytes: Array[Byte]): Unit
|
||||
|
||||
/**
|
||||
* Stores a array of bytes based on some key.
|
||||
*
|
||||
* @throws MissingNodeException when the Node with the given key doesn't exist.
|
||||
*/
|
||||
def update(key: String, bytes: Array[Byte]): Unit
|
||||
|
||||
/**
|
||||
* Loads the given entry. If it exists, a 'Some[Array[Byte]]' will be returned, else a None.
|
||||
*/
|
||||
def load(key: String): Option[Array[Byte]]
|
||||
}
|
||||
|
||||
/**
|
||||
* An AkkaException thrown by the RawStorage module.
|
||||
*/
|
||||
class RawStorageException(msg: String = null, cause: java.lang.Throwable = null) extends AkkaException(msg, cause)
|
||||
|
||||
/**
|
||||
* *
|
||||
* A RawStorageException thrown when an operation is done on a non existing node.
|
||||
*/
|
||||
class MissingNodeException(msg: String = null, cause: java.lang.Throwable = null) extends RawStorageException(msg, cause)
|
||||
|
||||
/**
|
||||
* A RawStorageException thrown when an operation is done on an existing node, but no node was expected.
|
||||
*/
|
||||
class NodeExistsException(msg: String = null, cause: java.lang.Throwable = null) extends RawStorageException(msg, cause)
|
||||
|
||||
/**
|
||||
* A RawStorage implementation based on ZooKeeper.
|
||||
*
|
||||
* The store method is atomic:
|
||||
* - so everything is written or nothing is written
|
||||
* - is isolated, so threadsafe,
|
||||
* but it will not participate in any transactions.
|
||||
* //todo: unclear, is only a single connection used in the JVM??
|
||||
*
|
||||
*/
|
||||
class ZooKeeperRawStorage(zkClient: AkkaZkClient) extends RawStorage {
|
||||
|
||||
override def load(key: String) = try {
|
||||
Some(zkClient.connection.readData(key, new Stat, false))
|
||||
} catch {
|
||||
case e: KeeperException.NoNodeException ⇒ None
|
||||
case e: KeeperException ⇒ throw new RawStorageException("failed to load key" + key, e)
|
||||
}
|
||||
|
||||
override def insert(key: String, bytes: Array[Byte]) {
|
||||
try {
|
||||
zkClient.connection.create(key, bytes, CreateMode.PERSISTENT);
|
||||
} catch {
|
||||
case e: KeeperException.NodeExistsException ⇒ throw new NodeExistsException("failed to insert key" + key, e)
|
||||
case e: KeeperException ⇒ throw new RawStorageException("failed to insert key" + key, e)
|
||||
}
|
||||
}
|
||||
|
||||
override def update(key: String, bytes: Array[Byte]) {
|
||||
try {
|
||||
zkClient.connection.writeData(key, bytes)
|
||||
} catch {
|
||||
case e: KeeperException.NoNodeException ⇒ throw new MissingNodeException("failed to update key", e)
|
||||
case e: KeeperException ⇒ throw new RawStorageException("failed to update key", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An in memory {@link RawStore} implementation. Useful for testing purposes.
|
||||
*/
|
||||
class InMemoryRawStorage extends RawStorage {
|
||||
|
||||
private val map = new ConcurrentHashMap[String, Array[Byte]]()
|
||||
|
||||
def load(key: String) = Option(map.get(key))
|
||||
|
||||
def insert(key: String, bytes: Array[Byte]) {
|
||||
val previous = map.putIfAbsent(key, bytes)
|
||||
if (previous != null) throw new NodeExistsException("failed to insert key " + key)
|
||||
}
|
||||
|
||||
def update(key: String, bytes: Array[Byte]) {
|
||||
val previous = map.put(key, bytes)
|
||||
if (previous == null) throw new NoNodeException("failed to update key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: To minimize the number of dependencies, should the RawStorage not be placed in a seperate module?
|
||||
//class VoldemortRawStorage(storeClient: StoreClient) extends RawStorage {
|
||||
//
|
||||
// def load(Key: String) = {
|
||||
// try {
|
||||
//
|
||||
// } catch {
|
||||
// case
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override def insert(key: String, bytes: Array[Byte]) {
|
||||
// throw new UnsupportedOperationException()
|
||||
// }
|
||||
//
|
||||
// def update(key: String, bytes: Array[Byte]) {
|
||||
// throw new UnsupportedOperationException()
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package akka.cluster
|
||||
|
||||
/**
|
||||
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
import Cluster._
|
||||
|
||||
import akka.actor._
|
||||
import akka.remote.MessageSerializer
|
||||
import akka.event.EventHandler
|
||||
import akka.config.Supervision._
|
||||
import akka.dispatch._
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.{ Map ⇒ JMap }
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait Replicable { this: Actor ⇒
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
sealed trait ReplicationStrategy
|
||||
|
||||
object ReplicationStrategy {
|
||||
case object Transient extends ReplicationStrategy
|
||||
case object WriteThrough extends ReplicationStrategy
|
||||
case object WriteBehind extends ReplicationStrategy
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ReplicatedActorRef private[akka] (actorRef: ActorRef, val address: String) extends ActorRef with ScalaActorRef {
|
||||
|
||||
private lazy val txLog = {
|
||||
EventHandler.debug(this, "Creating a ReplicatedActorRef for Actor [%s]".format(address))
|
||||
TransactionLog.newLogFor(uuid.toString)
|
||||
}
|
||||
|
||||
def invoke(messageHandle: MessageInvocation) {
|
||||
actorRef.invoke(messageHandle)
|
||||
txLog.recordEntry(MessageSerializer.serialize(messageHandle.message).toByteArray)
|
||||
}
|
||||
|
||||
def start(): ActorRef = {
|
||||
EventHandler.debug(this, "Starting ReplicatedActorRef for Actor [%s] with transaction log [%s]"
|
||||
.format(address, txLog.logId))
|
||||
actorRef.start()
|
||||
}
|
||||
|
||||
def stop() {
|
||||
txLog.delete()
|
||||
actorRef.stop()
|
||||
}
|
||||
|
||||
override def setFaultHandler(handler: FaultHandlingStrategy) {
|
||||
actorRef.setFaultHandler(handler)
|
||||
}
|
||||
override def getFaultHandler: FaultHandlingStrategy = actorRef.getFaultHandler()
|
||||
override def setLifeCycle(lifeCycle: LifeCycle) {
|
||||
actorRef.setLifeCycle(lifeCycle)
|
||||
}
|
||||
override def getLifeCycle: LifeCycle = actorRef.getLifeCycle
|
||||
def dispatcher_=(md: MessageDispatcher) {
|
||||
actorRef.dispatcher_=(md)
|
||||
}
|
||||
def dispatcher: MessageDispatcher = actorRef.dispatcher
|
||||
def link(actorRef: ActorRef) {
|
||||
actorRef.link(actorRef)
|
||||
}
|
||||
def unlink(actorRef: ActorRef) {
|
||||
actorRef.unlink(actorRef)
|
||||
}
|
||||
def startLink(actorRef: ActorRef): ActorRef = actorRef.startLink(actorRef)
|
||||
def supervisor: Option[ActorRef] = actorRef.supervisor
|
||||
def linkedActors: JMap[Uuid, ActorRef] = actorRef.linkedActors
|
||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) {
|
||||
actorRef.postMessageToMailbox(message, senderOption)
|
||||
}
|
||||
protected[akka] def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[Promise[T]]): Promise[T] = actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, senderOption, senderFuture)
|
||||
protected[akka] def actorInstance: AtomicReference[Actor] = actorRef.actorInstance
|
||||
protected[akka] def supervisor_=(sup: Option[ActorRef]) {
|
||||
actorRef.supervisor_=(sup)
|
||||
}
|
||||
protected[akka] def mailbox: AnyRef = actorRef.mailbox
|
||||
protected[akka] def mailbox_=(value: AnyRef): AnyRef = actorRef.mailbox_=(value)
|
||||
protected[akka] def handleTrapExit(dead: ActorRef, reason: Throwable) {
|
||||
actorRef.handleTrapExit(dead, reason)
|
||||
}
|
||||
protected[akka] def restart(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) {
|
||||
actorRef.restart(reason, maxNrOfRetries, withinTimeRange)
|
||||
}
|
||||
protected[akka] def restartLinkedActors(reason: Throwable, maxNrOfRetries: Option[Int], withinTimeRange: Option[Int]) {
|
||||
actorRef.restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,12 +27,11 @@ object Router {
|
|||
routerType: RouterType,
|
||||
inetSocketAddresses: Array[Tuple2[UUID, InetSocketAddress]],
|
||||
actorAddress: String,
|
||||
timeout: Long,
|
||||
replicationStrategy: ReplicationStrategy = ReplicationStrategy.WriteThrough): ClusterActorRef = {
|
||||
timeout: Long): ClusterActorRef = {
|
||||
routerType match {
|
||||
case Direct ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout, replicationStrategy) with Direct
|
||||
case Random ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout, replicationStrategy) with Random
|
||||
case RoundRobin ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout, replicationStrategy) with RoundRobin
|
||||
case Direct ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout) with Direct
|
||||
case Random ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout) with Random
|
||||
case RoundRobin ⇒ new ClusterActorRef(inetSocketAddresses, actorAddress, timeout) with RoundRobin
|
||||
case LeastCPU ⇒ sys.error("Router LeastCPU not supported yet")
|
||||
case LeastRAM ⇒ sys.error("Router LeastRAM not supported yet")
|
||||
case LeastMessages ⇒ sys.error("Router LeastMessages not supported yet")
|
||||
|
|
@ -57,7 +56,7 @@ object Router {
|
|||
}
|
||||
|
||||
def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = next match {
|
||||
case Some(actor) ⇒ actor.!!!(message, timeout)(sender)
|
||||
case Some(actor) ⇒ actor.?(message, timeout)(sender).asInstanceOf[Future[T]]
|
||||
case _ ⇒ throwNoConnectionsError()
|
||||
}
|
||||
|
||||
|
|
|
|||
320
akka-cluster/src/main/scala/akka/cluster/Storage.scala
Executable file
320
akka-cluster/src/main/scala/akka/cluster/Storage.scala
Executable file
|
|
@ -0,0 +1,320 @@
|
|||
package akka.cluster
|
||||
|
||||
import zookeeper.AkkaZkClient
|
||||
import akka.AkkaException
|
||||
import org.apache.zookeeper.{ KeeperException, CreateMode }
|
||||
import org.apache.zookeeper.data.Stat
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import annotation.tailrec
|
||||
import java.lang.{ UnsupportedOperationException, RuntimeException }
|
||||
|
||||
/**
|
||||
* Simple abstraction to store an Array of bytes based on some String key.
|
||||
*
|
||||
* Nothing is being said about ACID, transactions etc. It depends on the implementation
|
||||
* of this Storage interface of what is and isn't done on the lowest level.
|
||||
*
|
||||
* The amount of data that is allowed to be insert/updated is implementation specific. The InMemoryStorage
|
||||
* has no limits, but the ZooKeeperStorage has a maximum size of 1 mb.
|
||||
*
|
||||
* TODO: Class is up for better names.
|
||||
* TODO: Instead of a String as key, perhaps also a byte-array.
|
||||
*/
|
||||
trait Storage {
|
||||
|
||||
/**
|
||||
* Loads the VersionedData for the given key.
|
||||
*
|
||||
* @param key: the key of the VersionedData to load.
|
||||
* @return the VersionedData for the given entry.
|
||||
* @throws MissingDataException if the entry with the given key doesn't exist.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def load(key: String): VersionedData
|
||||
|
||||
/**
|
||||
* Loads the VersionedData for the given key and version.
|
||||
*
|
||||
* @param key: the key of the VersionedData to load
|
||||
* @param version the version of the VersionedData to load
|
||||
* @throws MissingDataException if the data with the given key doesn't exist.
|
||||
* @throws VersioningException if the version of the data is not the same as the given data.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def load(key: String, version: Long): VersionedData
|
||||
|
||||
/**
|
||||
* Checks if a VersionedData with the given key exists.
|
||||
*
|
||||
* @param key the key to check the existence for.
|
||||
* @return true if exists, false if not.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def exists(key: String): Boolean
|
||||
|
||||
/**
|
||||
* Inserts a byte-array based on some key.
|
||||
*
|
||||
* @param key the key of the Data to insert.
|
||||
* @param bytes the data to insert.
|
||||
* @return the VersionedData
|
||||
* @throws DataExistsException when VersionedData with the given Key already exists.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def insert(key: String, bytes: Array[Byte]): VersionedData
|
||||
|
||||
/**
|
||||
* Inserts the data if there is no data for that key, or overwrites it if it is there.
|
||||
*
|
||||
* This is the method you want to call if you just want to save something and don't
|
||||
* care about any lost update issues.
|
||||
*
|
||||
* @param key the key of the data
|
||||
* @param bytes the data to insert
|
||||
* @return the VersionedData that was stored.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def insertOrOverwrite(key: String, bytes: Array[Byte]): VersionedData
|
||||
|
||||
/**
|
||||
* Overwrites the current data for the given key.
|
||||
*
|
||||
* @param key the key of the data to overwrite
|
||||
* @param bytes the data to insert.
|
||||
* @throws ` when the entry with the given key doesn't exist.
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def overwrite(key: String, bytes: Array[Byte]): VersionedData
|
||||
|
||||
/**
|
||||
* @throws StorageException if anything goes wrong while accessing the storage
|
||||
*/
|
||||
def update(key: String, versionedData: VersionedData): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* The VersionedData is a container of data (some bytes) and a version (a Long).
|
||||
*/
|
||||
class VersionedData(val data: Array[Byte], val version: Long) {
|
||||
|
||||
/**
|
||||
* Creates an updated VersionedData. What happens is that a new VersionedData object is created with the newData
|
||||
* and a version that is one higher than the current version.
|
||||
*/
|
||||
def createUpdate(newData: Array[Byte]): VersionedData = new VersionedData(newData, version + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* An AkkaException thrown by the Storage module.
|
||||
*/
|
||||
class StorageException(msg: String = null, cause: java.lang.Throwable = null) extends AkkaException(msg, cause)
|
||||
|
||||
/**
|
||||
* *
|
||||
* A StorageException thrown when an operation is done on a non existing node.
|
||||
*/
|
||||
class MissingDataException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause)
|
||||
|
||||
/**
|
||||
* A StorageException thrown when an operation is done on an existing node, but no node was expected.
|
||||
*/
|
||||
class DataExistsException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause)
|
||||
|
||||
/**
|
||||
* A StorageException thrown when an operation causes an optimistic locking failure.
|
||||
*/
|
||||
class VersioningException(msg: String = null, cause: java.lang.Throwable = null) extends StorageException(msg, cause)
|
||||
|
||||
/**
|
||||
* A Storage implementation based on ZooKeeper.
|
||||
*
|
||||
* The store method is atomic:
|
||||
* - so everything is written or nothing is written
|
||||
* - is isolated, so threadsafe,
|
||||
* but it will not participate in any transactions.
|
||||
*
|
||||
*/
|
||||
class ZooKeeperStorage(zkClient: AkkaZkClient) extends Storage {
|
||||
|
||||
def load(key: String) = try {
|
||||
val stat = new Stat
|
||||
val arrayOfBytes = zkClient.connection.readData(key, stat, false)
|
||||
new VersionedData(arrayOfBytes, stat.getVersion)
|
||||
} catch {
|
||||
case e: KeeperException.NoNodeException ⇒ throw new MissingDataException(
|
||||
String.format("Failed to load key [%s]: no data was found", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to load key [%s]", key), e)
|
||||
}
|
||||
|
||||
def load(key: String, expectedVersion: Long) = try {
|
||||
val stat = new Stat
|
||||
val arrayOfBytes = zkClient.connection.readData(key, stat, false)
|
||||
|
||||
if (stat.getVersion != expectedVersion) throw new VersioningException(
|
||||
"Failed to update key [" + key + "]: version mismatch, expected [" + expectedVersion + "]" +
|
||||
" but found [" + stat.getVersion + "]")
|
||||
|
||||
new VersionedData(arrayOfBytes, stat.getVersion)
|
||||
} catch {
|
||||
case e: KeeperException.NoNodeException ⇒ throw new MissingDataException(
|
||||
String.format("Failed to load key [%s]: no data was found", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to load key [%s]", key), e)
|
||||
}
|
||||
|
||||
def insertOrOverwrite(key: String, bytes: Array[Byte]) = {
|
||||
try {
|
||||
throw new UnsupportedOperationException()
|
||||
} catch {
|
||||
case e: KeeperException.NodeExistsException ⇒ throw new DataExistsException(
|
||||
String.format("Failed to insert key [%s]: an entry already exists with the same key", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to insert key [%s]", key), e)
|
||||
}
|
||||
}
|
||||
|
||||
def insert(key: String, bytes: Array[Byte]): VersionedData = {
|
||||
try {
|
||||
zkClient.connection.create(key, bytes, CreateMode.PERSISTENT)
|
||||
//todo: how to get hold of the reference.
|
||||
val version: Long = 0
|
||||
new VersionedData(bytes, version)
|
||||
} catch {
|
||||
case e: KeeperException.NodeExistsException ⇒ throw new DataExistsException(
|
||||
String.format("Failed to insert key [%s]: an entry already exists with the same key", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to insert key [%s]", key), e)
|
||||
}
|
||||
}
|
||||
|
||||
def exists(key: String) = try {
|
||||
zkClient.connection.exists(key, false)
|
||||
} catch {
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to check existance for key [%s]", key), e)
|
||||
}
|
||||
|
||||
def update(key: String, versionedData: VersionedData) {
|
||||
try {
|
||||
zkClient.connection.writeData(key, versionedData.data, versionedData.version.asInstanceOf[Int])
|
||||
} catch {
|
||||
case e: KeeperException.BadVersionException ⇒ throw new VersioningException(
|
||||
String.format("Failed to update key [%s]: version mismatch", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to update key [%s]", key), e)
|
||||
}
|
||||
}
|
||||
|
||||
def overwrite(key: String, bytes: Array[Byte]): VersionedData = {
|
||||
try {
|
||||
zkClient.connection.writeData(key, bytes)
|
||||
throw new RuntimeException()
|
||||
} catch {
|
||||
case e: KeeperException.NoNodeException ⇒ throw new MissingDataException(
|
||||
String.format("Failed to overwrite key [%s]: a previous entry already exists", key), e)
|
||||
case e: KeeperException ⇒ throw new StorageException(
|
||||
String.format("Failed to overwrite key [%s]", key), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object InMemoryStorage {
|
||||
val InitialVersion = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* An in memory {@link RawStore} implementation. Useful for testing purposes.
|
||||
*/
|
||||
final class InMemoryStorage extends Storage {
|
||||
|
||||
private val map = new ConcurrentHashMap[String, VersionedData]()
|
||||
|
||||
def load(key: String) = {
|
||||
val result = map.get(key)
|
||||
|
||||
if (result == null) throw new MissingDataException(
|
||||
String.format("Failed to load key [%s]: no data was found", key))
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
def load(key: String, expectedVersion: Long) = {
|
||||
val result = load(key)
|
||||
|
||||
if (result.version != expectedVersion) throw new VersioningException(
|
||||
"Failed to load key [" + key + "]: version mismatch, expected [" + result.version + "] " +
|
||||
"but found [" + expectedVersion + "]")
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
def exists(key: String) = map.containsKey(key)
|
||||
|
||||
def insert(key: String, bytes: Array[Byte]): VersionedData = {
|
||||
val version: Long = InMemoryStorage.InitialVersion
|
||||
val result = new VersionedData(bytes, version)
|
||||
|
||||
val previous = map.putIfAbsent(key, result)
|
||||
if (previous != null) throw new DataExistsException(
|
||||
String.format("Failed to insert key [%s]: the key already has been inserted previously", key))
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def update(key: String, updatedData: VersionedData) {
|
||||
val currentData = map.get(key)
|
||||
|
||||
if (currentData == null) throw new MissingDataException(
|
||||
String.format("Failed to update key [%s], no previous entry exist", key))
|
||||
|
||||
val expectedVersion = currentData.version + 1
|
||||
if (expectedVersion != updatedData.version) throw new VersioningException(
|
||||
"Failed to update key [" + key + "]: version mismatch, expected [" + expectedVersion + "]" +
|
||||
" but found [" + updatedData.version + "]")
|
||||
|
||||
if (!map.replace(key, currentData, updatedData)) update(key, updatedData)
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def overwrite(key: String, bytes: Array[Byte]): VersionedData = {
|
||||
val currentData = map.get(key)
|
||||
|
||||
if (currentData == null) throw new MissingDataException(
|
||||
String.format("Failed to overwrite key [%s], no previous entry exist", key))
|
||||
|
||||
val newData = currentData.createUpdate(bytes)
|
||||
if (map.replace(key, currentData, newData)) newData else overwrite(key, bytes)
|
||||
}
|
||||
|
||||
def insertOrOverwrite(key: String, bytes: Array[Byte]): VersionedData = {
|
||||
val version = InMemoryStorage.InitialVersion
|
||||
val result = new VersionedData(bytes, version)
|
||||
|
||||
val previous = map.putIfAbsent(key, result)
|
||||
|
||||
if (previous == null) result
|
||||
else overwrite(key, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: To minimize the number of dependencies, should the Storage not be placed in a seperate module?
|
||||
//class VoldemortRawStorage(storeClient: StoreClient) extends Storage {
|
||||
//
|
||||
// def load(Key: String) = {
|
||||
// try {
|
||||
//
|
||||
// } catch {
|
||||
// case
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override def insert(key: String, bytes: Array[Byte]) {
|
||||
// throw new UnsupportedOperationException()
|
||||
// }
|
||||
//
|
||||
// def update(key: String, bytes: Array[Byte]) {
|
||||
// throw new UnsupportedOperationException()
|
||||
// }
|
||||
//}
|
||||
|
|
@ -9,16 +9,23 @@ import org.apache.zookeeper.CreateMode
|
|||
|
||||
import org.I0Itec.zkclient.exception._
|
||||
|
||||
import akka.AkkaException
|
||||
import akka.config._
|
||||
import Config._
|
||||
import akka.util._
|
||||
import akka.actor._
|
||||
import DeploymentConfig.{ ReplicationScheme, ReplicationStrategy, Transient, WriteThrough, WriteBehind }
|
||||
import akka.event.EventHandler
|
||||
import akka.dispatch.{ DefaultPromise, Promise }
|
||||
import akka.AkkaException
|
||||
|
||||
import akka.dispatch.{ DefaultPromise, Promise, MessageInvocation }
|
||||
import akka.remote.MessageSerializer
|
||||
import akka.serialization.ActorSerialization._
|
||||
import akka.cluster.zookeeper._
|
||||
import akka.serialization.{ Serializer, Compression }
|
||||
import Compression.LZF
|
||||
import akka.serialization.ActorSerialization._
|
||||
|
||||
import java.util.Enumeration
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
// FIXME allow user to choose dynamically between 'async' and 'sync' tx logging (asyncAddEntry(byte[] data, AddCallback cb, Object ctx))
|
||||
// FIXME clean up old entries in log after doing a snapshot
|
||||
|
|
@ -41,25 +48,47 @@ class ReplicationException(message: String) extends AkkaException(message)
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync: Boolean) {
|
||||
class TransactionLog private (
|
||||
ledger: LedgerHandle,
|
||||
val id: String,
|
||||
val isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer) {
|
||||
|
||||
import TransactionLog._
|
||||
|
||||
val logId = ledger.getId
|
||||
val txLogPath = transactionLogNode + "/" + id
|
||||
val snapshotPath = txLogPath + "/snapshot"
|
||||
val nrOfEntries = new AtomicLong(0)
|
||||
|
||||
private val isOpen = new Switch(true)
|
||||
|
||||
/**
|
||||
* TODO document method
|
||||
*/
|
||||
def recordEntry(messageHandle: MessageInvocation, actorRef: ActorRef) {
|
||||
if (nrOfEntries.incrementAndGet % snapshotFrequency == 0) {
|
||||
val snapshot =
|
||||
// FIXME ReplicationStrategy Transient is always used
|
||||
if (Cluster.shouldCompressData) LZF.compress(toBinary(actorRef, false, replicationScheme))
|
||||
else toBinary(actorRef, false, replicationScheme)
|
||||
recordSnapshot(snapshot)
|
||||
}
|
||||
recordEntry(MessageSerializer.serialize(messageHandle.message.asInstanceOf[AnyRef]).toByteArray)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO document method
|
||||
*/
|
||||
def recordEntry(entry: Array[Byte]) {
|
||||
if (isOpen.isOn) {
|
||||
val bytes = if (Cluster.shouldCompressData) LZF.compress(entry)
|
||||
else entry
|
||||
try {
|
||||
if (isAsync) {
|
||||
ledger.asyncAddEntry(
|
||||
entry,
|
||||
bytes,
|
||||
new AsyncCallback.AddCallback {
|
||||
def addComplete(
|
||||
returnCode: Int,
|
||||
|
|
@ -73,7 +102,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
},
|
||||
null)
|
||||
} else {
|
||||
handleReturnCode(ledger.addEntry(entry))
|
||||
handleReturnCode(ledger.addEntry(bytes))
|
||||
val entryId = ledger.getLastAddPushed
|
||||
EventHandler.debug(this, "Writing entry [%s] to log [%s]".format(entryId, logId))
|
||||
}
|
||||
|
|
@ -88,10 +117,12 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
*/
|
||||
def recordSnapshot(snapshot: Array[Byte]) {
|
||||
if (isOpen.isOn) {
|
||||
val bytes = if (Cluster.shouldCompressData) LZF.compress(snapshot)
|
||||
else snapshot
|
||||
try {
|
||||
if (isAsync) {
|
||||
ledger.asyncAddEntry(
|
||||
snapshot,
|
||||
bytes,
|
||||
new AsyncCallback.AddCallback {
|
||||
def addComplete(
|
||||
returnCode: Int,
|
||||
|
|
@ -104,7 +135,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
},
|
||||
null)
|
||||
} else {
|
||||
handleReturnCode(ledger.addEntry(snapshot))
|
||||
handleReturnCode(ledger.addEntry(bytes))
|
||||
storeSnapshotMetaDataInZooKeeper(ledger.getLastAddPushed)
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -121,7 +152,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
/**
|
||||
* TODO document method
|
||||
*/
|
||||
def entriesFromLatestSnapshot: Tuple2[Array[Byte], Vector[Array[Byte]]] = {
|
||||
def toByteArraysLatestSnapshot: (Array[Byte], Vector[Array[Byte]]) = {
|
||||
val snapshotId = latestSnapshotId
|
||||
EventHandler.debug(this,
|
||||
"Reading entries from snapshot id [%s] for log [%s]".format(snapshotId, logId))
|
||||
|
|
@ -133,9 +164,9 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
*/
|
||||
def entriesInRange(from: Long, to: Long): Vector[Array[Byte]] = if (isOpen.isOn) {
|
||||
try {
|
||||
if (from < 0) throw new IllegalArgumentException("'from' can't be negative [" + from + "]")
|
||||
if (to < 0) throw new IllegalArgumentException("'to' can't be negative [" + from + "]")
|
||||
if (to < from) throw new IllegalArgumentException("'to' can't be smaller than 'from' [" + from + "," + to + "]")
|
||||
if (from < 0) throw new IllegalArgumentException("'from' index can't be negative [" + from + "]")
|
||||
if (to < 0) throw new IllegalArgumentException("'to' index can't be negative [" + from + "]")
|
||||
if (to < from) throw new IllegalArgumentException("'to' index can't be smaller than 'from' index [" + from + "," + to + "]")
|
||||
EventHandler.debug(this,
|
||||
"Reading entries [%s -> %s] for log [%s]".format(from, to, logId))
|
||||
|
||||
|
|
@ -150,10 +181,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
enumeration: Enumeration[LedgerEntry],
|
||||
ctx: AnyRef) {
|
||||
val future = ctx.asInstanceOf[Promise[Vector[Array[Byte]]]]
|
||||
var entries = Vector[Array[Byte]]()
|
||||
while (enumeration.hasMoreElements) {
|
||||
entries = entries :+ enumeration.nextElement.getEntry
|
||||
}
|
||||
val entries = toByteArrays(enumeration)
|
||||
if (returnCode == BKException.Code.OK) future.completeWithResult(entries)
|
||||
else future.completeWithException(BKException.create(returnCode))
|
||||
}
|
||||
|
|
@ -161,12 +189,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
future)
|
||||
await(future)
|
||||
} else {
|
||||
val enumeration = ledger.readEntries(from, to)
|
||||
var entries = Vector[Array[Byte]]()
|
||||
while (enumeration.hasMoreElements) {
|
||||
entries = entries :+ enumeration.nextElement.getEntry
|
||||
}
|
||||
entries
|
||||
toByteArrays(ledger.readEntries(from, to))
|
||||
}
|
||||
} catch {
|
||||
case e ⇒ handleError(e)
|
||||
|
|
@ -190,8 +213,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
} catch {
|
||||
case e: ZkNoNodeException ⇒
|
||||
handleError(new ReplicationException(
|
||||
"Transaction log for UUID [" + id +
|
||||
"] does not have a snapshot recorded in ZooKeeper"))
|
||||
"Transaction log for UUID [" + id + "] does not have a snapshot recorded in ZooKeeper"))
|
||||
case e ⇒ handleError(e)
|
||||
}
|
||||
}
|
||||
|
|
@ -208,7 +230,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
logId,
|
||||
new AsyncCallback.DeleteCallback {
|
||||
def deleteComplete(returnCode: Int, ctx: AnyRef) {
|
||||
handleReturnCode(returnCode)
|
||||
(returnCode)
|
||||
}
|
||||
},
|
||||
null)
|
||||
|
|
@ -248,6 +270,18 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
}
|
||||
}
|
||||
|
||||
private def toByteArrays(enumeration: Enumeration[LedgerEntry]): Vector[Array[Byte]] = {
|
||||
var entries = Vector[Array[Byte]]()
|
||||
while (enumeration.hasMoreElements) {
|
||||
val bytes = enumeration.nextElement.getEntry
|
||||
val entry =
|
||||
if (Cluster.shouldCompressData) LZF.uncompress(bytes)
|
||||
else bytes
|
||||
entries = entries :+ entry
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
private def storeSnapshotMetaDataInZooKeeper(snapshotId: Long) {
|
||||
if (isOpen.isOn) {
|
||||
try {
|
||||
|
|
@ -265,8 +299,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync:
|
|||
"Could not store transaction log snapshot meta-data in ZooKeeper for UUID [" +
|
||||
id + "]"))
|
||||
}
|
||||
EventHandler.debug(this,
|
||||
"Writing snapshot [%s] to log [%s]".format(snapshotId, logId))
|
||||
EventHandler.debug(this, "Writing snapshot [%s] to log [%s]".format(snapshotId, logId))
|
||||
} else transactionClosedError
|
||||
}
|
||||
|
||||
|
|
@ -292,12 +325,13 @@ object TransactionLog {
|
|||
case "CRC32" ⇒ BookKeeper.DigestType.CRC32
|
||||
case "MAC" ⇒ BookKeeper.DigestType.MAC
|
||||
case unknown ⇒ throw new ConfigurationException(
|
||||
"akka.cluster.replication.digest-type is invalid [" + unknown + "]")
|
||||
"akka.cluster.replication.digest-type is invalid [" + unknown + "], must be either 'CRC32' or 'MAC'")
|
||||
}
|
||||
val password = config.getString("akka.cluster.replication.password", "secret").getBytes("UTF-8")
|
||||
val ensembleSize = config.getInt("akka.cluster.replication.ensemble-size", 3)
|
||||
val quorumSize = config.getInt("akka.cluster.replication.quorum-size", 2)
|
||||
val timeout = 5000 // FIXME make configurable
|
||||
val snapshotFrequency = config.getInt("akka.cluster.replication.snapshot-frequency", 1000)
|
||||
val timeout = Duration(config.getInt("akka.cluster.replication.timeout", 30), TIME_UNIT).toMillis
|
||||
|
||||
private[akka] val transactionLogNode = "/transaction-log-ids"
|
||||
|
||||
|
|
@ -333,8 +367,13 @@ object TransactionLog {
|
|||
(bk, zk)
|
||||
}
|
||||
|
||||
private[akka] def apply(ledger: LedgerHandle, id: String, isAsync: Boolean = false) =
|
||||
new TransactionLog(ledger, id, isAsync)
|
||||
private[akka] def apply(
|
||||
ledger: LedgerHandle,
|
||||
id: String,
|
||||
isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer) =
|
||||
new TransactionLog(ledger, id, isAsync, replicationScheme, format)
|
||||
|
||||
/**
|
||||
* Shuts down the transaction log.
|
||||
|
|
@ -355,7 +394,12 @@ object TransactionLog {
|
|||
/**
|
||||
* TODO document method
|
||||
*/
|
||||
def newLogFor(id: String, isAsync: Boolean = false): TransactionLog = {
|
||||
def newLogFor(
|
||||
id: String,
|
||||
isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer): TransactionLog = {
|
||||
|
||||
val txLogPath = transactionLogNode + "/" + id
|
||||
|
||||
val ledger = try {
|
||||
|
|
@ -399,13 +443,18 @@ object TransactionLog {
|
|||
}
|
||||
|
||||
EventHandler.info(this, "Created new transaction log [%s] for UUID [%s]".format(logId, id))
|
||||
TransactionLog(ledger, id, isAsync)
|
||||
TransactionLog(ledger, id, isAsync, replicationScheme, format)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO document method
|
||||
*/
|
||||
def logFor(id: String, isAsync: Boolean = false): TransactionLog = {
|
||||
def logFor(
|
||||
id: String,
|
||||
isAsync: Boolean,
|
||||
replicationScheme: ReplicationScheme,
|
||||
format: Serializer): TransactionLog = {
|
||||
|
||||
val txLogPath = transactionLogNode + "/" + id
|
||||
|
||||
val logId = try {
|
||||
|
|
@ -444,7 +493,7 @@ object TransactionLog {
|
|||
case e ⇒ handleError(e)
|
||||
}
|
||||
|
||||
TransactionLog(ledger, id, isAsync)
|
||||
TransactionLog(ledger, id, isAsync, replicationScheme, format)
|
||||
}
|
||||
|
||||
private[akka] def await[T](future: Promise[T]): T = {
|
||||
|
|
@ -489,15 +538,10 @@ object LocalBookKeeperEnsemble {
|
|||
def shutdown() {
|
||||
isRunning switchOff {
|
||||
EventHandler.info(this, "Shutting down LocalBookKeeperEnsemble...")
|
||||
println("***************************** 1")
|
||||
localBookKeeper.bs.foreach(_.shutdown()) // stop bookies
|
||||
println("***************************** 2")
|
||||
localBookKeeper.zkc.close() // stop zk client
|
||||
println("***************************** 3")
|
||||
localBookKeeper.zks.shutdown() // stop zk server
|
||||
println("***************************** 4")
|
||||
localBookKeeper.serverFactory.shutdown() // stop zk NIOServer
|
||||
println("***************************** 5")
|
||||
EventHandler.info(this, "LocalBookKeeperEnsemble shut down successfully")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class ClusterDeployerSpec extends WordSpec with MustMatchers with BeforeAndAfter
|
|||
|
||||
val deployments2 = ClusterDeployer.fetchDeploymentsFromCluster
|
||||
deployments2.size must equal(1)
|
||||
deployments2.first must equal(deployments1.first)
|
||||
deployments2.head must equal(deployments1.head)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
package akka.cluster
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
|
|
@ -33,7 +34,6 @@ object BinaryFormatMyJavaSerializableActor {
|
|||
val serializer = Serializers.Java
|
||||
}
|
||||
}
|
||||
/*
|
||||
class ClusterSpec extends WordSpec with MustMatchers with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
import Cluster._
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import akka.cluster._
|
|||
|
||||
import akka.actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.serialization.{ Serializers, SerializerBasedActorFormat }
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
|
|
@ -42,7 +41,7 @@ object PingPong {
|
|||
count += 1
|
||||
self reply Ball
|
||||
} else {
|
||||
self.sender.foreach(_ !! Stop)
|
||||
self.sender.foreach(s ⇒ (s ? Stop).await)
|
||||
gameOverLatch.countDown
|
||||
self.stop
|
||||
}
|
||||
|
|
@ -60,20 +59,6 @@ object PingPong {
|
|||
self.stop
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// Serialization
|
||||
// ------------------------
|
||||
|
||||
object BinaryFormats {
|
||||
implicit object PingActorFormat extends SerializerBasedActorFormat[PingActor] with Serializable {
|
||||
val serializer = Serializers.Java
|
||||
}
|
||||
|
||||
implicit object PongActorFormat extends SerializerBasedActorFormat[PongActor] with Serializable {
|
||||
val serializer = Serializers.Java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
251
akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala
Executable file
251
akka-cluster/src/test/scala/akka/cluster/InMemoryStorageSpec.scala
Executable file
|
|
@ -0,0 +1,251 @@
|
|||
package akka.cluster
|
||||
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.WordSpec
|
||||
import akka.cluster.StorageTestUtils._
|
||||
|
||||
class InMemoryStorageSpec extends WordSpec with MustMatchers {
|
||||
|
||||
"unversioned load" must {
|
||||
"throw MissingDataException if non existing key" in {
|
||||
val store = new InMemoryStorage()
|
||||
|
||||
try {
|
||||
store.load("foo")
|
||||
fail()
|
||||
} catch {
|
||||
case e: MissingDataException ⇒
|
||||
}
|
||||
}
|
||||
|
||||
"return VersionedData if key existing" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
storage.insert(key, value)
|
||||
|
||||
val result = storage.load(key)
|
||||
//todo: strange that the implicit store is not found
|
||||
assertContent(key, value, result.version)(storage)
|
||||
}
|
||||
}
|
||||
|
||||
"exist" must {
|
||||
"return true if value exists" in {
|
||||
val store = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
store.insert(key, "somevalue".getBytes)
|
||||
store.exists(key) must be(true)
|
||||
}
|
||||
|
||||
"return false if value not exists" in {
|
||||
val store = new InMemoryStorage()
|
||||
store.exists("somekey") must be(false)
|
||||
}
|
||||
}
|
||||
|
||||
"versioned load" must {
|
||||
"throw MissingDataException if non existing key" in {
|
||||
val store = new InMemoryStorage()
|
||||
|
||||
try {
|
||||
store.load("foo", 1)
|
||||
fail()
|
||||
} catch {
|
||||
case e: MissingDataException ⇒
|
||||
}
|
||||
}
|
||||
|
||||
"return VersionedData if key existing and exact version match" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
val stored = storage.insert(key, value)
|
||||
|
||||
val result = storage.load(key, stored.version)
|
||||
assert(result.version == stored.version)
|
||||
assert(result.data == stored.data)
|
||||
}
|
||||
|
||||
"throw VersioningException is version too new" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
val stored = storage.insert(key, value)
|
||||
|
||||
try {
|
||||
storage.load(key, stored.version + 1)
|
||||
fail()
|
||||
} catch {
|
||||
case e: VersioningException ⇒
|
||||
}
|
||||
}
|
||||
|
||||
"throw VersioningException is version too old" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
val stored = storage.insert(key, value)
|
||||
|
||||
try {
|
||||
storage.load(key, stored.version - 1)
|
||||
fail()
|
||||
} catch {
|
||||
case e: VersioningException ⇒
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"insert" must {
|
||||
|
||||
"place a new value when non previously existed" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val oldValue = "oldvalue".getBytes
|
||||
storage.insert(key, oldValue)
|
||||
|
||||
val result = storage.load(key)
|
||||
assertContent(key, oldValue)(storage)
|
||||
assert(InMemoryStorage.InitialVersion == result.version)
|
||||
}
|
||||
|
||||
"throw MissingDataException when there already exists an entry with the same key" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val oldValue = "oldvalue".getBytes
|
||||
|
||||
val oldVersionedData = storage.insert(key, oldValue)
|
||||
|
||||
val newValue = "newValue".getBytes
|
||||
|
||||
try {
|
||||
storage.insert(key, newValue)
|
||||
fail()
|
||||
} catch {
|
||||
case e: DataExistsException ⇒
|
||||
}
|
||||
|
||||
//make sure that the old value was not changed
|
||||
assert(oldVersionedData == storage.load(key))
|
||||
}
|
||||
}
|
||||
|
||||
"update" must {
|
||||
|
||||
"throw MissingDataException when no node exists" in {
|
||||
val storage = new InMemoryStorage()
|
||||
|
||||
val key = "somekey"
|
||||
|
||||
try {
|
||||
storage.update(key, new VersionedData("somevalue".getBytes, 1))
|
||||
fail()
|
||||
} catch {
|
||||
case e: MissingDataException ⇒
|
||||
}
|
||||
}
|
||||
|
||||
"replace if previous value exists and no other updates have been done" in {
|
||||
val storage = new InMemoryStorage()
|
||||
|
||||
//do the initial insert
|
||||
val key = "foo"
|
||||
val oldValue = "insert".getBytes
|
||||
val insert = storage.insert(key, oldValue)
|
||||
|
||||
//do the update the will be the cause of the conflict.
|
||||
val updateValue = "update".getBytes
|
||||
val update = insert.createUpdate(updateValue)
|
||||
storage.update(key, update)
|
||||
|
||||
assertContent(key, update.data, update.version)(storage)
|
||||
}
|
||||
|
||||
"throw VersioningException when already overwritten" in {
|
||||
val storage = new InMemoryStorage()
|
||||
|
||||
//do the initial insert
|
||||
val key = "foo"
|
||||
val oldValue = "insert".getBytes
|
||||
val insert = storage.insert(key, oldValue)
|
||||
|
||||
//do the update the will be the cause of the conflict.
|
||||
val otherUpdateValue = "otherupdate".getBytes
|
||||
val otherUpdate = insert.createUpdate(otherUpdateValue)
|
||||
storage.update(key, otherUpdate)
|
||||
|
||||
val update = insert.createUpdate("update".getBytes)
|
||||
|
||||
try {
|
||||
storage.update(key, update)
|
||||
fail()
|
||||
} catch {
|
||||
case e: VersioningException ⇒
|
||||
}
|
||||
|
||||
assertContent(key, otherUpdate.data, otherUpdate.version)(storage)
|
||||
}
|
||||
}
|
||||
|
||||
"overwrite" must {
|
||||
|
||||
"throw MissingDataException when no node exists" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
|
||||
try {
|
||||
storage.overwrite(key, "somevalue".getBytes)
|
||||
fail()
|
||||
} catch {
|
||||
case e: MissingDataException ⇒
|
||||
}
|
||||
|
||||
storage.exists(key) must be(false)
|
||||
}
|
||||
|
||||
"succeed if previous value exist" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val oldValue = "oldvalue".getBytes
|
||||
val newValue: Array[Byte] = "somevalue".getBytes
|
||||
|
||||
val initialInsert: VersionedData = storage.insert(key, oldValue)
|
||||
|
||||
val result: VersionedData = storage.overwrite(key, newValue)
|
||||
|
||||
assert(result.version == initialInsert.version + 1)
|
||||
assert(result.data == newValue)
|
||||
storage.load(key) must be eq (result)
|
||||
}
|
||||
}
|
||||
|
||||
"insertOrOverwrite" must {
|
||||
"insert if nothing was inserted before" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
|
||||
val result = storage.insertOrOverwrite(key, value)
|
||||
|
||||
assert(result.version == InMemoryStorage.InitialVersion)
|
||||
assert(result.data == value)
|
||||
storage.load(key) must be eq (result)
|
||||
}
|
||||
|
||||
"overwrite of something existed before" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val oldValue = "oldvalue".getBytes
|
||||
val newValue = "somevalue".getBytes
|
||||
|
||||
val initialInsert = storage.insert(key, oldValue)
|
||||
|
||||
val result = storage.insertOrOverwrite(key, newValue)
|
||||
|
||||
assert(result.version == initialInsert.version + 1)
|
||||
assert(result.data == newValue)
|
||||
storage.load(key) must be eq (result)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ package example.cluster
|
|||
import akka.cluster._
|
||||
|
||||
import akka.actor._
|
||||
import akka.serialization.{ Serializers, SerializerBasedActorFormat }
|
||||
import akka.util.duration._
|
||||
|
||||
object PingPong {
|
||||
|
|
@ -62,20 +61,6 @@ object PingPong {
|
|||
self reply Pong
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// Serialization
|
||||
// -----------------------------------------------
|
||||
|
||||
object BinaryFormats {
|
||||
implicit object PingActorFormat extends SerializerBasedActorFormat[PingActor] with Serializable {
|
||||
val serializer = Serializers.Java
|
||||
}
|
||||
|
||||
implicit object PongActorFormat extends SerializerBasedActorFormat[PongActor] with Serializable {
|
||||
val serializer = Serializers.Java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -32,31 +32,31 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
"A Transaction Log" should {
|
||||
"be able to record entries - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog = TransactionLog.newLogFor(uuid)
|
||||
val txlog = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog.recordEntry(entry)
|
||||
}
|
||||
|
||||
"be able to record and delete entries - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.delete
|
||||
txlog1.close
|
||||
intercept[BKNoSuchLedgerExistsException](TransactionLog.logFor(uuid))
|
||||
intercept[BKNoSuchLedgerExistsException](TransactionLog.logFor(uuid, false, null, JavaSerializer))
|
||||
}
|
||||
|
||||
"be able to record entries and read entries with 'entriesInRange' - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid)
|
||||
val txlog2 = TransactionLog.logFor(uuid, false, null, JavaSerializer)
|
||||
val entries = txlog2.entriesInRange(0, 1).map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
entries.size must equal(2)
|
||||
entries(0) must equal("hello")
|
||||
|
|
@ -66,7 +66,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record entries and read entries with 'entries' - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
|
|
@ -74,7 +74,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
txlog1.recordEntry(entry)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid)
|
||||
val txlog2 = TransactionLog.logFor(uuid, false, null, JavaSerializer)
|
||||
val entries = txlog2.entries.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
entries.size must equal(4)
|
||||
entries(0) must equal("hello")
|
||||
|
|
@ -86,7 +86,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record a snapshot - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val snapshot = "snapshot".getBytes("UTF-8")
|
||||
txlog1.recordSnapshot(snapshot)
|
||||
txlog1.close
|
||||
|
|
@ -94,7 +94,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record and read a snapshot and following entries - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
val snapshot = "snapshot".getBytes("UTF-8")
|
||||
txlog1.recordSnapshot(snapshot)
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
txlog1.recordEntry(entry)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.entriesFromLatestSnapshot
|
||||
val txlog2 = TransactionLog.logFor(uuid, false, null, JavaSerializer)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.toByteArraysLatestSnapshot
|
||||
new String(snapshotAsBytes, "UTF-8") must equal("snapshot")
|
||||
|
||||
val entries = entriesAsBytes.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
|
|
@ -120,7 +120,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record entries then a snapshot then more entries - and then read from the snapshot and the following entries - synchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, false, null, JavaSerializer)
|
||||
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
|
|
@ -134,8 +134,8 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
txlog1.recordEntry(entry)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.entriesFromLatestSnapshot
|
||||
val txlog2 = TransactionLog.logFor(uuid, false, null, JavaSerializer)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.toByteArraysLatestSnapshot
|
||||
new String(snapshotAsBytes, "UTF-8") must equal("snapshot")
|
||||
|
||||
val entries = entriesAsBytes.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
|
|
@ -149,7 +149,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
"A Transaction Log" should {
|
||||
"be able to record entries - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog.recordEntry(entry)
|
||||
Thread.sleep(100)
|
||||
|
|
@ -158,24 +158,24 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record and delete entries - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.delete
|
||||
Thread.sleep(100)
|
||||
intercept[BKNoSuchLedgerExistsException](TransactionLog.logFor(uuid, true))
|
||||
intercept[BKNoSuchLedgerExistsException](TransactionLog.logFor(uuid, true, null, JavaSerializer))
|
||||
}
|
||||
"be able to record entries and read entries with 'entriesInRange' - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
Thread.sleep(100)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid, true)
|
||||
val txlog2 = TransactionLog.logFor(uuid, true, null, JavaSerializer)
|
||||
val entries = txlog2.entriesInRange(0, 1).map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
entries.size must equal(2)
|
||||
entries(0) must equal("hello")
|
||||
|
|
@ -186,7 +186,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record entries and read entries with 'entries' - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
txlog1.recordEntry(entry)
|
||||
|
|
@ -195,7 +195,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
Thread.sleep(100)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid, true)
|
||||
val txlog2 = TransactionLog.logFor(uuid, true, null, JavaSerializer)
|
||||
val entries = txlog2.entries.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
entries.size must equal(4)
|
||||
entries(0) must equal("hello")
|
||||
|
|
@ -208,7 +208,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record a snapshot - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val snapshot = "snapshot".getBytes("UTF-8")
|
||||
txlog1.recordSnapshot(snapshot)
|
||||
Thread.sleep(100)
|
||||
|
|
@ -217,7 +217,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record and read a snapshot and following entries - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
val snapshot = "snapshot".getBytes("UTF-8")
|
||||
txlog1.recordSnapshot(snapshot)
|
||||
|
||||
|
|
@ -229,8 +229,8 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
Thread.sleep(100)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid, true)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.entriesFromLatestSnapshot
|
||||
val txlog2 = TransactionLog.logFor(uuid, true, null, JavaSerializer)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.toByteArraysLatestSnapshot
|
||||
new String(snapshotAsBytes, "UTF-8") must equal("snapshot")
|
||||
|
||||
val entries = entriesAsBytes.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
|
|
@ -245,7 +245,7 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
|
||||
"be able to record entries then a snapshot then more entries - and then read from the snapshot and the following entries - asynchronous" in {
|
||||
val uuid = (new UUID).toString
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true)
|
||||
val txlog1 = TransactionLog.newLogFor(uuid, true, null, JavaSerializer)
|
||||
|
||||
val entry = "hello".getBytes("UTF-8")
|
||||
txlog1.recordEntry(entry)
|
||||
|
|
@ -258,8 +258,8 @@ class ReplicationSpec extends WordSpec with MustMatchers with BeforeAndAfterAll
|
|||
Thread.sleep(100)
|
||||
txlog1.close
|
||||
|
||||
val txlog2 = TransactionLog.logFor(uuid, true)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.entriesFromLatestSnapshot
|
||||
val txlog2 = TransactionLog.logFor(uuid, true, null, JavaSerializer)
|
||||
val (snapshotAsBytes, entriesAsBytes) = txlog2.toByteArraysLatestSnapshot
|
||||
new String(snapshotAsBytes, "UTF-8") must equal("snapshot")
|
||||
val entries = entriesAsBytes.map(bytes ⇒ new String(bytes, "UTF-8"))
|
||||
entries.size must equal(2)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package akka.cluster
|
||||
|
||||
object StorageTestUtils {
|
||||
|
||||
def assertContent(key: String, expectedData: Array[Byte], expectedVersion: Long)(implicit storage: InMemoryStorage) {
|
||||
val found = storage.load(key)
|
||||
assert(found.version == expectedVersion)
|
||||
assert(expectedData == found.data) //todo: structural equals
|
||||
}
|
||||
|
||||
def assertContent(key: String, expectedData: Array[Byte])(implicit storage: InMemoryStorage) {
|
||||
val found = storage.load(key)
|
||||
assert(expectedData == found.data) //todo: structural equals
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package akka.cluster
|
||||
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.actor.Actor
|
||||
import org.scalatest.{ BeforeAndAfterEach, BeforeAndAfterAll, WordSpec }
|
||||
import org.I0Itec.zkclient.ZkServer
|
||||
import zookeeper.AkkaZkClient
|
||||
|
||||
class ZooKeeperStorageSpec extends WordSpec with MustMatchers with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
val dataPath = "_akka_cluster/data"
|
||||
val logPath = "_akka_cluster/log"
|
||||
var zkServer: ZkServer = _
|
||||
var zkClient: AkkaZkClient = _
|
||||
/*
|
||||
override def beforeAll() {
|
||||
try {
|
||||
zkServer = Cluster.startLocalCluster(dataPath, logPath)
|
||||
Thread.sleep(5000)
|
||||
Actor.cluster.start()
|
||||
zkClient = Cluster.newZkClient()
|
||||
} catch {
|
||||
case e ⇒ e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override def afterAll() {
|
||||
zkClient.close()
|
||||
Actor.cluster.shutdown()
|
||||
ClusterDeployer.shutdown()
|
||||
Cluster.shutdownLocalCluster()
|
||||
Actor.registry.local.shutdownAll()
|
||||
}
|
||||
*/
|
||||
"unversioned load" must {
|
||||
"throw MissingDataException if non existing key" in {
|
||||
// val store = new ZooKeeperStorage(zkClient)
|
||||
|
||||
//try {
|
||||
// store.load("foo")
|
||||
// fail()
|
||||
//} catch {
|
||||
// case e: MissingDataException ⇒
|
||||
//}
|
||||
}
|
||||
|
||||
/*
|
||||
"return VersionedData if key existing" in {
|
||||
val storage = new InMemoryStorage()
|
||||
val key = "somekey"
|
||||
val value = "somevalue".getBytes
|
||||
storage.insert(key, value)
|
||||
|
||||
val result = storage.load(key)
|
||||
//todo: strange that the implicit store is not found
|
||||
assertContent(key, value, result.version)(storage)
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
akka.event-handler-level = "DEBUG"
|
||||
akka.actor.deployment.service-hello.router = "round-robin"
|
||||
akka.actor.deployment.service-hello.clustered.home = "node:node1"
|
||||
akka.actor.deployment.service-hello.clustered.replicas = 1
|
||||
akka.actor.deployment.service-hello.clustered.stateless = on
|
||||
akka.actor.deployment.service-hello.clustered.replicas = 1
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
akka.event-handler-level = "DEBUG"
|
||||
akka.actor.deployment.service-hello.router = "round-robin"
|
||||
akka.actor.deployment.service-hello.clustered.home = "node:node1"
|
||||
akka.actor.deployment.service-hello.clustered.replicas = 1
|
||||
akka.actor.deployment.service-hello.clustered.stateless = on
|
||||
akka.actor.deployment.service-hello.clustered.replicas = 1
|
||||
|
|
@ -8,9 +8,12 @@ import org.scalatest.WordSpec
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
|
||||
import org.apache.bookkeeper.client.{ BookKeeper, BKException }
|
||||
import BKException._
|
||||
|
||||
import akka.cluster._
|
||||
import akka.actor._
|
||||
import Actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.config.Config
|
||||
|
||||
object RoundRobin1ReplicaMultiJvmSpec {
|
||||
|
|
@ -27,6 +30,9 @@ object RoundRobin1ReplicaMultiJvmSpec {
|
|||
class RoundRobin1ReplicaMultiJvmNode1 extends WordSpec with MustMatchers with BeforeAndAfterAll {
|
||||
import RoundRobin1ReplicaMultiJvmSpec._
|
||||
|
||||
private var bookKeeper: BookKeeper = _
|
||||
// private var localBookKeeper: LocalBookKeeper = _
|
||||
|
||||
"A cluster" must {
|
||||
|
||||
"create clustered actor, get a 'local' actor on 'home' node and a 'ref' to actor on remote node" in {
|
||||
|
|
@ -49,10 +55,13 @@ class RoundRobin1ReplicaMultiJvmNode1 extends WordSpec with MustMatchers with Be
|
|||
|
||||
override def beforeAll() = {
|
||||
Cluster.startLocalCluster()
|
||||
// LocalBookKeeperEnsemble.start()
|
||||
}
|
||||
|
||||
override def afterAll() = {
|
||||
Cluster.shutdownLocalCluster()
|
||||
// TransactionLog.shutdown()
|
||||
// LocalBookKeeperEnsemble.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +90,7 @@ class RoundRobin1ReplicaMultiJvmNode2 extends WordSpec with MustMatchers {
|
|||
|
||||
Cluster.barrier("send-message-from-node2-to-node1", NrOfNodes) {
|
||||
hello must not equal (null)
|
||||
val reply = (hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))
|
||||
val reply = (hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1"))
|
||||
reply must equal("World from node [node1]")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ import org.scalatest.WordSpec
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
|
||||
import org.apache.bookkeeper.client.{ BookKeeper, BKException }
|
||||
import BKException._
|
||||
|
||||
import akka.cluster._
|
||||
import akka.actor._
|
||||
import Actor._
|
||||
import akka.actor.Actor._
|
||||
import akka.config.Config
|
||||
|
||||
object RoundRobin2ReplicasMultiJvmSpec {
|
||||
|
|
@ -28,6 +31,9 @@ object RoundRobin2ReplicasMultiJvmSpec {
|
|||
class RoundRobin2ReplicasMultiJvmNode1 extends WordSpec with MustMatchers with BeforeAndAfterAll {
|
||||
import RoundRobin2ReplicasMultiJvmSpec._
|
||||
|
||||
private var bookKeeper: BookKeeper = _
|
||||
private var localBookKeeper: LocalBookKeeper = _
|
||||
|
||||
"A cluster" must {
|
||||
|
||||
"create clustered actor, get a 'local' actor on 'home' node and a 'ref' to actor on remote node" in {
|
||||
|
|
@ -52,10 +58,13 @@ class RoundRobin2ReplicasMultiJvmNode1 extends WordSpec with MustMatchers with B
|
|||
|
||||
override def beforeAll() = {
|
||||
Cluster.startLocalCluster()
|
||||
LocalBookKeeperEnsemble.start()
|
||||
}
|
||||
|
||||
override def afterAll() = {
|
||||
Cluster.shutdownLocalCluster()
|
||||
TransactionLog.shutdown()
|
||||
LocalBookKeeperEnsemble.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,14 +102,14 @@ class RoundRobin2ReplicasMultiJvmNode2 extends WordSpec with MustMatchers {
|
|||
else replies.put(reply, replies(reply) + 1)
|
||||
}
|
||||
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello !! "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node1")))
|
||||
count((hello ? "Hello").as[String].getOrElse(fail("Should have recieved reply from node3")))
|
||||
|
||||
replies("World from node [node1]") must equal(4)
|
||||
replies("World from node [node3]") must equal(4)
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
.. _add-on-modules:
|
||||
|
||||
Add-on Modules
|
||||
==============
|
||||
|
||||
Akka Modules consist of add-on modules outside the core of Akka:
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
|
||||
Documentation for Akka Modules is located `here <http://akka.io/docs/akka-modules/snapshot/>`_.
|
||||
|
|
@ -4,7 +4,6 @@ Additional Information
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
add-on-modules
|
||||
articles
|
||||
benchmarks
|
||||
recipies
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ in its mailbox.
|
|||
.. sidebar:: **IMPORTANT**
|
||||
|
||||
None of these mailboxes work with blocking message send, e.g. the message
|
||||
send operations that are relying on futures; ``!!``, ``!!!``,
|
||||
``sendRequestReply`` and ``sendRequestReplyFuture``. If the node has crashed
|
||||
send operations that are relying on futures; ``!!``, ``?``,
|
||||
``sendRequestReply`` and ``ask``. If the node has crashed
|
||||
and then restarted, the thread that was blocked waiting for the reply is gone
|
||||
and there is no way we can deliver the message.
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ The durable dispatchers and their configuration options reside in the
|
|||
You configure durable mailboxes through the "Akka"-only durable dispatchers, the
|
||||
actor is oblivious to which type of mailbox it is using. Here is an example::
|
||||
|
||||
val dispatcher = DurableEventBasedDispatcher(
|
||||
val dispatcher = DurableDispatcher(
|
||||
"my:service",
|
||||
FileDurableMailboxStorage)
|
||||
// Then set the actors dispatcher to this dispatcher
|
||||
|
|
@ -63,7 +63,7 @@ or for a thread-based durable dispatcher::
|
|||
self,
|
||||
FileDurableMailboxStorage)
|
||||
|
||||
There are 2 different durable dispatchers, ``DurableEventBasedDispatcher`` and
|
||||
There are 2 different durable dispatchers, ``DurableDispatcher`` and
|
||||
``DurablePinnedDispatcher``, which are durable versions of
|
||||
``Dispatcher`` and ``PinnedDispatcher``.
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ mailboxes. Read more in the Redis documentation on how to do that.
|
|||
|
||||
Here is an example of how you can configure your dispatcher to use this mailbox::
|
||||
|
||||
val dispatcher = DurableEventBasedDispatcher(
|
||||
val dispatcher = DurableDispatcher(
|
||||
"my:service",
|
||||
RedisDurableMailboxStorage)
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ there will not be that much more work to set up this durable mailbox.
|
|||
|
||||
Here is an example of how you can configure your dispatcher to use this mailbox::
|
||||
|
||||
val dispatcher = DurableEventBasedDispatcher(
|
||||
val dispatcher = DurableDispatcher(
|
||||
"my:service",
|
||||
ZooKeeperDurableMailboxStorage)
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ Beanstalk is a simple, fast work queue. This means that you have to start up a
|
|||
Beanstalk server that can host these durable mailboxes. Read more in the
|
||||
Beanstalk documentation on how to do that. ::
|
||||
|
||||
val dispatcher = DurableEventBasedDispatcher(
|
||||
val dispatcher = DurableDispatcher(
|
||||
"my:service",
|
||||
BeanstalkDurableMailboxStorage)
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ like the following::
|
|||
When you call ``multi-jvm-run Test`` at the sbt prompt, three JVMs will be
|
||||
spawned, one for each node. It will look like this:
|
||||
|
||||
.. code-block:: shell
|
||||
.. code-block:: none
|
||||
|
||||
> multi-jvm-run Test
|
||||
...
|
||||
|
|
@ -252,7 +252,7 @@ something in coordination::
|
|||
|
||||
An example output from this would be:
|
||||
|
||||
.. code-block:: shell
|
||||
.. code-block:: none
|
||||
|
||||
> multi-jvm-run Test
|
||||
...
|
||||
|
|
|
|||
|
|
@ -1,175 +1,6 @@
|
|||
Getting Started Tutorial: First Chapter
|
||||
=======================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Welcome to the first tutorial on how to get started with Akka and Scala. We assume that you already know what Akka and Scala are and will now focus on the steps necessary to start your first project.
|
||||
|
||||
There are two variations of this first tutorial:
|
||||
|
||||
- creating a standalone project and run it from the command line
|
||||
- creating a SBT (Simple Build Tool) project and running it from within SBT
|
||||
|
||||
Since they are so similar we will present them both.
|
||||
|
||||
The sample application that we will create is using actors to calculate the value of Pi. Calculating Pi is a CPU intensive operation and we will utilize Akka Actors to write a concurrent solution that scales out to multi-core processors. This sample will be extended in future tutorials to use Akka Remote Actors to scale out on multiple machines in a cluster.
|
||||
|
||||
We will be using an algorithm that is called "embarrassingly parallel" which just means that each job is completely isolated and not coupled with any other job. Since this algorithm is so parallelizable it suits the actor model very well.
|
||||
|
||||
Here is the formula for the algorithm we will use:
|
||||
|
||||
.. image:: ../images/pi-formula.png
|
||||
|
||||
In this particular algorithm the master splits the series into chunks which are sent out to each worker actor to be processed. When each worker has processed its chunk it sends a result back to the master which aggregates the total result.
|
||||
|
||||
Tutorial source code
|
||||
--------------------
|
||||
|
||||
If you want don't want to type in the code and/or set up an SBT project then you can check out the full tutorial from the Akka GitHub repository. It is in the ``akka-tutorials/akka-tutorial-first`` module. You can also browse it online `here`__, with the actual source code `here`__.
|
||||
|
||||
__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first
|
||||
__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
This tutorial assumes that you have Java 1.6 or later installed on you machine and ``java`` on your ``PATH``. You also need to know how to run commands in a shell (ZSH, Bash, DOS etc.) and a decent text editor or IDE to type in the Scala code.
|
||||
|
||||
Downloading and installing Akka
|
||||
-------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to download Akka. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-1.1`` distribution of Akka core (not Akka Modules) from `http://akka.io/downloads <http://akka.io/downloads/>`_. Once you have downloaded the distribution unzip it in the folder you would like to have Akka installed in, in my case I choose to install it in ``/Users/jboner/tools/``, simply by unzipping it to this directory.
|
||||
|
||||
You need to do one more thing in order to install Akka properly: set the ``AKKA_HOME`` environment variable to the root of the distribution. In my case I'm opening up a shell, navigating down to the distribution, and setting the ``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-1.1
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-1.1
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
$ ls -l
|
||||
total 16944
|
||||
drwxr-xr-x 7 jboner staff 238 Apr 6 11:15 .
|
||||
drwxr-xr-x 28 jboner staff 952 Apr 6 11:16 ..
|
||||
drwxr-xr-x 17 jboner staff 578 Apr 6 11:16 deploy
|
||||
drwxr-xr-x 26 jboner staff 884 Apr 6 11:16 dist
|
||||
drwxr-xr-x 3 jboner staff 102 Apr 6 11:15 lib_managed
|
||||
-rwxr-xr-x 1 jboner staff 8674105 Apr 6 11:15 scala-library.jar
|
||||
drwxr-xr-x 4 jboner staff 136 Apr 6 11:16 scripts
|
||||
|
||||
- In the ``dist`` directory we have the Akka JARs, including sources and docs.
|
||||
- In the ``lib_managed/compile`` directory we have Akka's dependency JARs.
|
||||
- In the ``deploy`` directory we have the sample JARs.
|
||||
- In the ``scripts`` directory we have scripts for running Akka.
|
||||
- Finally ``scala-library.jar`` is the JAR for the latest Scala distribution that Akka depends on.
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the ``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``dist`` directory. This is a self-contained JAR with zero dependencies and contains everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
|
||||
- ``akka-actor-1.1.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-1.1.jar`` -- Typed Actors
|
||||
- ``akka-remote-1.1.jar`` -- Remote Actors
|
||||
- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener
|
||||
- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors
|
||||
|
||||
We also have Akka Modules containing add-on modules outside the core of Akka. You can download the Akka Modules distribution from `<http://akka.io/downloads/>`_. It contains Akka core as well. We will not be needing any modules there today, but for your information the module JARs are these:
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
|
||||
Downloading and installing Scala
|
||||
--------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to install the Scala distribution. If you prefer to use SBT to build and run the sample then you can skip this section and jump to the next one.
|
||||
|
||||
Scala can be downloaded from `http://www.scala-lang.org/downloads <http://www.scala-lang.org/downloads>`_. Browse there and download the Scala 2.9.0 release. If you pick the ``tgz`` or ``zip`` distribution then just unzip it where you want it installed. If you pick the IzPack Installer then double click on it and follow the instructions.
|
||||
|
||||
You also need to make sure that the ``scala-2.9.0/bin`` (if that is the directory where you installed Scala) is on your ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:scala-2.9.0/bin
|
||||
|
||||
You can test your installation by invoking scala::
|
||||
|
||||
$ scala -version
|
||||
Scala code runner version 2.9.0.final -- Copyright 2002-2011, LAMP/EPFL
|
||||
|
||||
Looks like we are all good. Finally let's create a source file ``Pi.scala`` for the tutorial and put it in the root of the Akka distribution in the ``tutorial`` directory (you have to create it first).
|
||||
|
||||
Some tools require you to set the ``SCALA_HOME`` environment variable to the root of the Scala distribution, however Akka does not require that.
|
||||
|
||||
Downloading and installing SBT
|
||||
------------------------------
|
||||
|
||||
SBT, short for 'Simple Build Tool' is an excellent build system written in Scala. It uses Scala to write the build scripts which gives you a lot of power. It has a plugin architecture with many plugins available, something that we will take advantage of soon. SBT is the preferred way of building software in Scala and is probably the easiest way of getting through this tutorial. If you want to use SBT for this tutorial then follow the following instructions, if not you can skip this section and the next.
|
||||
|
||||
First browse to the `SBT download page <http://code.google.com/p/simple-build-tool/downloads/list>`_ and download the ``0.7.6.RC0`` distribution.
|
||||
|
||||
To install SBT and create a project for this tutorial it is easiest to follow the instructions on `this page <http://code.google.com/p/simple-build-tool/wiki/Setup>`_.
|
||||
|
||||
If you have created an SBT project then step into the newly created SBT project, create a source file ``Pi.scala`` for the tutorial sample and put it in the ``src/main/scala`` directory.
|
||||
|
||||
So far we only have a standard Scala project but now we need to make our project an Akka project. You could add the dependencies manually to the build script, but the easiest way is to use Akka's SBT Plugin, covered in the next section.
|
||||
|
||||
Creating an Akka SBT project
|
||||
----------------------------
|
||||
|
||||
If you have not already done so, now is the time to create an SBT project for our tutorial. You do that by stepping into the directory you want to create your project in and invoking the ``sbt`` command answering the questions for setting up your project (just pressing ENTER will choose the default in square brackets)::
|
||||
|
||||
$ sbt
|
||||
Project does not exist, create new project? (y/N/s) y
|
||||
Name: Tutorial 1
|
||||
Organization: Hakkers Inc
|
||||
Version [1.0]:
|
||||
Scala version [2.9.0]:
|
||||
sbt version [0.7.6.RC0]:
|
||||
|
||||
Now we have the basis for an SBT project. Akka has an SBT Plugin making it very easy to use Akka is an SBT-based project so let's use that.
|
||||
|
||||
To use the plugin, first add a plugin definition to your SBT project by creating a ``Plugins.scala`` file in the ``project/plugins`` directory containing::
|
||||
|
||||
import sbt._
|
||||
|
||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1"
|
||||
}
|
||||
|
||||
Now we need to create a project definition using our Akka SBT plugin. We do that by creating a ``project/build/Project.scala`` file containing::
|
||||
|
||||
import sbt._
|
||||
|
||||
class TutorialOneProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
}
|
||||
|
||||
The magic is in mixing in the ``AkkaProject`` trait.
|
||||
|
||||
Not needed in this tutorial, but if you would like to use additional Akka modules beyond ``akka-actor``, you can add these as "module configurations" in the project file. Here is an example adding ``akka-remote`` and ``akka-stm``::
|
||||
|
||||
class AkkaSampleProject(info: ProjectInfo) extends DefaultProject(info) with AkkaProject {
|
||||
val akkaSTM = akkaModule("stm")
|
||||
val akkaRemote = akkaModule("remote")
|
||||
}
|
||||
|
||||
So, now we are all set. Just one final thing to do; make SBT download the dependencies it needs. That is done by invoking::
|
||||
|
||||
> update
|
||||
|
||||
SBT itself needs a whole bunch of dependencies but our project will only need one; ``akka-actor-1.1.jar``. SBT downloads that as well.
|
||||
|
||||
Start writing the code
|
||||
----------------------
|
||||
|
||||
|
|
@ -236,7 +67,7 @@ Here is the master actor:
|
|||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achive the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achive the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done.
|
||||
|
||||
|
|
@ -268,69 +99,3 @@ That's it. Now we are done.
|
|||
But before we package it up and run it, let's take a look at the full code now, with package declaration, imports and all:
|
||||
|
||||
.. includecode:: examples/Pi.scala
|
||||
|
||||
Run it as a command line application
|
||||
------------------------------------
|
||||
|
||||
If you have not typed in (or copied) the code for the tutorial as ``$AKKA_HOME/tutorial/Pi.scala`` then now is the time. When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
||||
|
||||
First we need to compile the source file. That is done with Scala's compiler ``scalac``. Our application depends on the ``akka-actor-1.1.jar`` JAR file, so let's add that to the compiler classpath when we compile the source::
|
||||
|
||||
$ scalac -cp dist/akka-actor-1.1.jar tutorial/Pi.scala
|
||||
|
||||
When we have compiled the source file we are ready to run the application. This is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar`` JAR file to the classpath, and this time we also need to add the Scala runtime library ``scala-library.jar`` and the classes we compiled ourselves::
|
||||
|
||||
$ java -cp dist/akka-actor-1.1.jar:scala-library.jar:tutorial akka.tutorial.scala.first.Pi
|
||||
AKKA_HOME is defined as [/Users/jboner/src/akka-stuff/akka-core], loading config from \
|
||||
[/Users/jboner/src/akka-stuff/akka-core/config/akka.conf].
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 858 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
If you have not defined an the ``AKKA_HOME`` environment variable then Akka can't find the ``akka.conf`` configuration file and will print out a ``Can’t load akka.conf`` warning. This is ok since it will then just use the defaults.
|
||||
|
||||
Run it inside SBT
|
||||
-----------------
|
||||
|
||||
If you used SBT, then you can run the application directly inside SBT. First you need to compile the project::
|
||||
|
||||
$ sbt
|
||||
> update
|
||||
...
|
||||
> compile
|
||||
...
|
||||
|
||||
When this in done we can run our application directly inside SBT::
|
||||
|
||||
> run
|
||||
...
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 942 millis
|
||||
|
||||
Yippee! It is working.
|
||||
|
||||
If you have not defined an the ``AKKA_HOME`` environment variable then Akka can't find the ``akka.conf`` configuration file and will print out a ``Can’t load akka.conf`` warning. This is ok since it will then just use the defaults.
|
||||
|
||||
The implementation in more detail
|
||||
---------------------------------
|
||||
|
||||
To create our actors we used a method called ``actorOf`` in the ``Actor`` object. We used it in two different ways, one of them taking a actor type and the other one an instance of an actor. The former one (``actorOf[Worker]``) is used when the actor class has a no-argument constructor while the second one (``actorOf(new Master(..))``) is used when the actor class has a constructor that takes arguments. This is the only way to create an instance of an Actor and the ``actorOf`` method ensures this. The latter version is using call-by-name and lazily creates the actor within the scope of the ``actorOf`` method. The ``actorOf`` method instantiates the actor and returns, not an instance to the actor, but an instance to an ``ActorRef``. This reference is the handle through which you communicate with the actor. It is immutable, serializable and location-aware meaning that it "remembers" its original actor even if it is sent to other nodes across the network and can be seen as the equivalent to the Erlang actor's PID.
|
||||
|
||||
The actor's life-cycle is:
|
||||
|
||||
- Created -- ``Actor.actorOf[MyActor]`` -- can **not** receive messages
|
||||
- Started -- ``actorRef.start()`` -- can receive messages
|
||||
- Stopped -- ``actorRef.stop()`` -- can **not** receive messages
|
||||
|
||||
Once the actor has been stopped it is dead and can not be started again.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
We have learned how to create our first Akka project using Akka's actors to speed up a computation-intensive problem by scaling out on multi-core processors (also known as scaling up). We have also learned to compile and run an Akka project using either the tools on the command line or the SBT build system.
|
||||
|
||||
Now we are ready to take on more advanced problems. In the next tutorial we will build on this one, refactor it into more idiomatic Akka and Scala code, and introduce a few new concepts and abstractions. Whenever you feel ready, join me in the `Getting Started Tutorial: Second Chapter <TODO>`_.
|
||||
|
||||
Happy hakking.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Using Akka as a stand alone microkernel
|
|||
---------------------------------------
|
||||
|
||||
Akka can also be run as a stand-alone microkernel. It implements a full
|
||||
enterprise stack. See the :ref:`add-on-modules` for more information.
|
||||
enterprise stack. See the :ref:`microkernel` for more information.
|
||||
|
||||
Using the Akka sbt plugin to package your application
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -56,7 +56,7 @@ To use the plugin, first add a plugin definition to your SBT project by creating
|
|||
|
||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "2.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
Then mix the ``AkkaKernelProject`` trait into your project definition. For
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ To build and run the tutorial sample from the command line, you have to download
|
|||
Akka. If you prefer to use SBT to build and run the sample then you can skip
|
||||
this section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-1.1.zip`` distribution of Akka from
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -77,10 +77,10 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-1.1
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-1.1
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
|
|
@ -98,32 +98,26 @@ The distribution looks like this::
|
|||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka``
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
|
||||
- ``akka-actor-1.1.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-1.1.jar`` -- Typed Actors
|
||||
- ``akka-remote-1.1.jar`` -- Remote Actors
|
||||
- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-2.0-SNAPSHOT.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
|
||||
We also have Akka Modules containing add-on modules outside the core of
|
||||
Akka. You can download the Akka Modules distribution from `<http://akka.io/downloads/>`_. It contains Akka
|
||||
core as well. We will not be needing any modules there today, but for your
|
||||
information the module JARs are these:
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing Maven
|
||||
|
|
@ -186,7 +180,7 @@ We also need to edit the ``pom.xml`` build file. Let's add the dependency we nee
|
|||
<dependency>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<artifactId>akka-actor</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
@ -441,7 +435,7 @@ Here is the master actor::
|
|||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
|
||||
|
|
@ -721,22 +715,22 @@ time. When that's done open up a shell and step in to the Akka distribution
|
|||
(``cd $AKKA_HOME``).
|
||||
|
||||
First we need to compile the source file. That is done with Java's compiler
|
||||
``javac``. Our application depends on the ``akka-actor-1.1.jar`` and the
|
||||
``javac``. Our application depends on the ``akka-actor-2.0-SNAPSHOT.jar`` and the
|
||||
``scala-library.jar`` JAR files, so let's add them to the compiler classpath
|
||||
when we compile the source::
|
||||
|
||||
$ javac -cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar tutorial/akka/tutorial/first/java/Pi.java
|
||||
$ javac -cp lib/scala-library.jar:lib/akka/akka-actor-2.0-SNAPSHOT.jar tutorial/akka/tutorial/first/java/Pi.java
|
||||
|
||||
When we have compiled the source file we are ready to run the application. This
|
||||
is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar``
|
||||
is done with ``java`` but yet again we need to add the ``akka-actor-2.0-SNAPSHOT.jar``
|
||||
and the ``scala-library.jar`` JAR files to the classpath as well as the classes
|
||||
we compiled ourselves::
|
||||
|
||||
$ java \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar:tutorial \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-2.0-SNAPSHOT.jar:tutorial \
|
||||
akka.tutorial.java.first.Pi
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1]
|
||||
loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf].
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT]
|
||||
loading config from [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT/config/akka.conf].
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 822 millis
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ To build and run the tutorial sample from the command line, you have to download
|
|||
Akka. If you prefer to use SBT to build and run the sample then you can skip
|
||||
this section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-1.1.zip`` distribution of Akka from
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -62,10 +62,10 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-1.1
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-1.1
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
|
|
@ -83,32 +83,26 @@ The distribution looks like this::
|
|||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka``
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
|
||||
- ``akka-actor-1.1.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-1.1.jar`` -- Typed Actors
|
||||
- ``akka-remote-1.1.jar`` -- Remote Actors
|
||||
- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-2.0-SNAPSHOT.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
|
||||
We also have Akka Modules containing add-on modules outside the core of
|
||||
Akka. You can download the Akka Modules distribution from `<http://akka.io/downloads/>`_. It contains Akka
|
||||
core as well. We will not be needing any modules there today, but for your
|
||||
information the module JARs are these:
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing the Scala IDE for Eclipse
|
||||
|
|
@ -173,7 +167,7 @@ If you are an `SBT <http://code.google.com/p/simple-build-tool/>`_ user, you can
|
|||
lazy val eclipse = "de.element34" % "sbt-eclipsify" % "0.7.0"
|
||||
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "2.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
and then update your SBT project definition by mixing in ``Eclipsify`` in your project definition::
|
||||
|
|
@ -341,7 +335,7 @@ Here is the master actor::
|
|||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done.
|
||||
|
||||
|
|
@ -412,8 +406,8 @@ Run it from Eclipse
|
|||
|
||||
Eclipse builds your project on every save when ``Project/Build Automatically`` is set. If not, bring you project up to date by clicking ``Project/Build Project``. If there are no compilation errors, you can right-click in the editor where ``Pi`` is defined, and choose ``Run as.. /Scala application``. If everything works fine, you should see::
|
||||
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1]
|
||||
loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf].
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT]
|
||||
loading config from [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT/config/akka.conf].
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 858 millis
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ To check out the code using Git invoke the following::
|
|||
|
||||
$ git clone git://github.com/jboner/akka.git
|
||||
|
||||
Then you can navigate down to the tutorial::
|
||||
Then you can navigate down to the tutorial::
|
||||
|
||||
$ cd akka/akka-tutorials/akka-tutorial-first
|
||||
|
||||
|
|
@ -63,10 +63,9 @@ Downloading and installing Akka
|
|||
-------------------------------
|
||||
|
||||
To build and run the tutorial sample from the command line, you have to download
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skip
|
||||
this section and jump to the next one.
|
||||
Akka. If you prefer to use SBT to build and run the sample then you can skipthis section and jump to the next one.
|
||||
|
||||
Let's get the ``akka-actors-1.1.zip`` distribution of Akka from
|
||||
Let's get the ``akka-actors-2.0-SNAPSHOT.zip`` distribution of Akka from
|
||||
http://akka.io/downloads/ which includes everything we need for this
|
||||
tutorial. Once you have downloaded the distribution unzip it in the folder you
|
||||
would like to have Akka installed in. In my case I choose to install it in
|
||||
|
|
@ -77,10 +76,10 @@ You need to do one more thing in order to install Akka properly: set the
|
|||
I'm opening up a shell, navigating down to the distribution, and setting the
|
||||
``AKKA_HOME`` variable::
|
||||
|
||||
$ cd /Users/jboner/tools/akka-actors-1.1
|
||||
$ cd /Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
$ export AKKA_HOME=`pwd`
|
||||
$ echo $AKKA_HOME
|
||||
/Users/jboner/tools/akka-actors-1.1
|
||||
/Users/jboner/tools/akka-actors-2.0-SNAPSHOT
|
||||
|
||||
The distribution looks like this::
|
||||
|
||||
|
|
@ -98,32 +97,26 @@ The distribution looks like this::
|
|||
|
||||
|
||||
The only JAR we will need for this tutorial (apart from the
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-1.1.jar`` JAR in the ``lib/akka``
|
||||
``scala-library.jar`` JAR) is the ``akka-actor-2.0-SNAPSHOT.jar`` JAR in the ``lib/akka``
|
||||
directory. This is a self-contained JAR with zero dependencies and contains
|
||||
everything we need to write a system using Actors.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
|
||||
- ``akka-actor-1.1.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-1.1.jar`` -- Typed Actors
|
||||
- ``akka-remote-1.1.jar`` -- Remote Actors
|
||||
- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-2.0-SNAPSHOT.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener for logging with SLF4J
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
|
||||
We also have Akka Modules containing add-on modules outside the core of
|
||||
Akka. You can download the Akka Modules distribution from `<http://akka.io/downloads/>`_. It contains Akka
|
||||
core as well. We will not be needing any modules there today, but for your
|
||||
information the module JARs are these:
|
||||
The Akka Microkernel distribution also includes these jars:
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
|
||||
|
||||
Downloading and installing Scala
|
||||
|
|
@ -180,7 +173,7 @@ To use the plugin, first add a plugin definition to your SBT project by creating
|
|||
|
||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "2.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
Now we need to create a project definition using our Akka SBT plugin. We do that by creating a ``project/build/Project.scala`` file containing::
|
||||
|
|
@ -205,7 +198,7 @@ So, now we are all set. Just one final thing to do; make SBT download the depend
|
|||
|
||||
The first reload command is needed because we have changed the project definition since the sbt session started.
|
||||
|
||||
SBT itself needs a whole bunch of dependencies but our project will only need one; ``akka-actor-1.1.jar``. SBT downloads that as well.
|
||||
SBT itself needs a whole bunch of dependencies but our project will only need one; ``akka-actor-2.0-SNAPSHOT.jar``. SBT downloads that as well.
|
||||
|
||||
Start writing the code
|
||||
----------------------
|
||||
|
|
@ -336,7 +329,7 @@ Here is the master actor::
|
|||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``!!!`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the ``Master`` actor. This latch is only used for plumbing (in this specific tutorial), to have a simple way of letting the outside world knowing when the master can deliver the result and shut down. In more idiomatic Akka code, as we will see in part two of this tutorial series, we would not use a latch but other abstractions and functions like ``Channel``, ``Future`` and ``?`` to achieve the same thing in a non-blocking way. But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and ``postStop``. In the ``preStart`` callback we are recording the time when the actor is started and in the ``postStop`` callback we are printing out the result (the approximation of Pi) and the time it took to calculate it. In this call we also invoke ``latch.countDown`` to tell the outside world that we are done.
|
||||
|
||||
|
|
@ -519,17 +512,17 @@ Run it as a command line application
|
|||
|
||||
If you have not typed in (or copied) the code for the tutorial as ``$AKKA_HOME/tutorial/Pi.scala`` then now is the time. When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
||||
|
||||
First we need to compile the source file. That is done with Scala's compiler ``scalac``. Our application depends on the ``akka-actor-1.1.jar`` JAR file, so let's add that to the compiler classpath when we compile the source::
|
||||
First we need to compile the source file. That is done with Scala's compiler ``scalac``. Our application depends on the ``akka-actor-2.0-SNAPSHOT.jar`` JAR file, so let's add that to the compiler classpath when we compile the source::
|
||||
|
||||
$ scalac -cp lib/akka/akka-actor-1.1.jar tutorial/Pi.scala
|
||||
$ scalac -cp lib/akka/akka-actor-2.0-SNAPSHOT.jar tutorial/Pi.scala
|
||||
|
||||
When we have compiled the source file we are ready to run the application. This is done with ``java`` but yet again we need to add the ``akka-actor-1.1.jar`` JAR file to the classpath, and this time we also need to add the Scala runtime library ``scala-library.jar`` and the classes we compiled ourselves::
|
||||
When we have compiled the source file we are ready to run the application. This is done with ``java`` but yet again we need to add the ``akka-actor-2.0-SNAPSHOT.jar`` JAR file to the classpath, and this time we also need to add the Scala runtime library ``scala-library.jar`` and the classes we compiled ourselves::
|
||||
|
||||
$ java \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-1.1.jar:. \
|
||||
-cp lib/scala-library.jar:lib/akka/akka-actor-2.0-SNAPSHOT.jar:. \
|
||||
akka.tutorial.first.scala.Pi
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-1.1]
|
||||
loading config from [/Users/jboner/tools/akka-actors-1.1/config/akka.conf].
|
||||
AKKA_HOME is defined as [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT]
|
||||
loading config from [/Users/jboner/tools/akka-actors-2.0-SNAPSHOT/config/akka.conf].
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 858 millis
|
||||
|
|
|
|||
|
|
@ -40,31 +40,19 @@ dependencies from the Akka Maven repository.
|
|||
Modules
|
||||
-------
|
||||
|
||||
Akka is split up into two different parts:
|
||||
|
||||
* Akka - The core modules. Reflects all the sections under :ref:`scala-api` and :ref:`java-api`.
|
||||
* Akka Modules - The microkernel and add-on modules, described in :ref:`add-on-modules`.
|
||||
|
||||
Akka is very modular and has many JARs for containing different features. The core distribution has seven modules:
|
||||
|
||||
- ``akka-actor-1.1.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-1.1.jar`` -- Typed Actors
|
||||
- ``akka-remote-1.1.jar`` -- Remote Actors
|
||||
- ``akka-stm-1.1.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-1.1.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-1.1.jar`` -- SLF4J Event Handler Listener
|
||||
- ``akka-testkit-1.1.jar`` -- Toolkit for testing Actors
|
||||
|
||||
We also have Akka Modules containing add-on modules outside the core of Akka.
|
||||
|
||||
- ``akka-kernel-1.1.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
- ``akka-amqp-1.1.jar`` -- AMQP integration
|
||||
- ``akka-camel-1.1.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-1.1.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-scalaz-1.1.jar`` -- Support for the Scalaz library
|
||||
- ``akka-spring-1.1.jar`` -- Spring framework integration
|
||||
- ``akka-osgi-dependencies-bundle-1.1.jar`` -- OSGi support
|
||||
Akka is very modular and has many JARs for containing different features.
|
||||
|
||||
- ``akka-actor-2.0-SNAPSHOT.jar`` -- Standard Actors
|
||||
- ``akka-typed-actor-2.0-SNAPSHOT.jar`` -- Typed Actors
|
||||
- ``akka-remote-2.0-SNAPSHOT.jar`` -- Remote Actors
|
||||
- ``akka-stm-2.0-SNAPSHOT.jar`` -- STM (Software Transactional Memory), transactors and transactional datastructures
|
||||
- ``akka-http-2.0-SNAPSHOT.jar`` -- Akka Mist for continuation-based asynchronous HTTP and also Jersey integration
|
||||
- ``akka-slf4j-2.0-SNAPSHOT.jar`` -- SLF4J Event Handler Listener
|
||||
- ``akka-testkit-2.0-SNAPSHOT.jar`` -- Toolkit for testing Actors
|
||||
- ``akka-camel-2.0-SNAPSHOT.jar`` -- Apache Camel Actors integration (it's the best way to have your Akka application communicate with the rest of the world)
|
||||
- ``akka-camel-typed-2.0-SNAPSHOT.jar`` -- Apache Camel Typed Actors integration
|
||||
- ``akka-spring-2.0-SNAPSHOT.jar`` -- Spring framework integration
|
||||
- ``akka-kernel-2.0-SNAPSHOT.jar`` -- Akka microkernel for running a bare-bones mini application server (embeds Jetty etc.)
|
||||
|
||||
How to see the JARs dependencies of each Akka module is described in the :ref:`dependencies` section. Worth noting
|
||||
is that ``akka-actor`` has zero external dependencies (apart from the ``scala-library.jar`` JAR).
|
||||
|
|
@ -82,7 +70,7 @@ The Akka Modules distribution includes the microkernel. To run the microkernel:
|
|||
* Set the AKKA_HOME environment variable to the root of the Akka distribution.
|
||||
* To start the kernel use the scripts in the ``bin`` directory and deploy all samples applications from ``./deploy`` dir.
|
||||
|
||||
More information is available in the documentation of the Microkernel in :ref:`add-on-modules`.
|
||||
More information is available in the documentation of the :ref:`microkernel`.
|
||||
|
||||
Using a build tool
|
||||
------------------
|
||||
|
|
@ -107,14 +95,14 @@ Summary of the essential parts for using Akka with Maven:
|
|||
<url>http://akka.io/repository/ </url>
|
||||
</repository>
|
||||
|
||||
2) Add the Akka dependencies. For example, here is the dependency for Akka Actor 1.1:
|
||||
2) Add the Akka dependencies. For example, here is the dependency for Akka Actor 2.0-SNAPSHOT:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<dependency>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<artifactId>akka-actor</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
|
@ -129,7 +117,7 @@ Summary of the essential parts for using Akka with SBT:
|
|||
|
||||
1) Akka has an SBT plugin which makes it very easy to get started with Akka and SBT.
|
||||
|
||||
The Scala version in your SBT project needs to match the version that Akka is built against. For Akka 1.1 this is
|
||||
The Scala version in your SBT project needs to match the version that Akka is built against. For Akka 2.0-SNAPSHOT this is
|
||||
Scala version 2.9.0.
|
||||
|
||||
To use the plugin, first add a plugin definition to your SBT project by creating project/plugins/Plugins.scala with:
|
||||
|
|
@ -140,10 +128,10 @@ To use the plugin, first add a plugin definition to your SBT project by creating
|
|||
|
||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
||||
val akkaRepo = "Akka Repo" at "http://akka.io/repository"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "1.1"
|
||||
val akkaPlugin = "se.scalablesolutions.akka" % "akka-sbt-plugin" % "2.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
*Note: the plugin version matches the Akka version provided. The current release is 1.1.*
|
||||
*Note: the plugin version matches the Akka version provided. The current release is 2.0-SNAPSHOT.*
|
||||
|
||||
2) Then mix the AkkaProject trait into your project definition. For example:
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ The Dispatcher is an important piece that allows you to configure the right sema
|
|||
|
||||
Akka supports dispatchers for both event-driven lightweight threads, allowing creation of millions threads on a single workstation, and thread-based Actors, where each dispatcher is bound to a dedicated OS thread.
|
||||
|
||||
The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 G RAM.
|
||||
The event-based Actors currently consume ~600 bytes per Actor which means that you can create more than 6.5 million Actors on 4 GB RAM.
|
||||
|
||||
Default dispatcher
|
||||
------------------
|
||||
|
|
@ -84,6 +84,7 @@ The 'Dispatcher' binds a set of Actors to a thread pool backed up by a 'Blocking
|
|||
The event-driven dispatchers **must be shared** between multiple Typed Actors and/or Actors. One best practice is to let each top-level Actor, e.g. the Actors you define in the declarative supervisor config, to get their own dispatcher but reuse the dispatcher for each new Actor that the top-level Actor creates. But you can also share dispatcher between multiple top-level Actors. This is very use-case specific and needs to be tried out on a case by case basis. The important thing is that Akka tries to provide you with the freedom you need to design and implement your system in the most efficient way in regards to performance, throughput and latency.
|
||||
|
||||
It comes with many different predefined BlockingQueue configurations:
|
||||
|
||||
* Bounded LinkedBlockingQueue
|
||||
* Unbounded LinkedBlockingQueue
|
||||
* Bounded ArrayBlockingQueue
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ Use with Actors
|
|||
|
||||
There are generally two ways of getting a reply from an ``UntypedActor``: the first is by a sent message (``actorRef.sendOneWay(msg);``), which only works if the original sender was an ``UntypedActor``) and the second is through a ``Future``.
|
||||
|
||||
Using the ``ActorRef``\'s ``sendRequestReplyFuture`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is:
|
||||
Using the ``ActorRef``\'s ``ask`` method to send a message will return a Future. To wait for and retrieve the actual result the simplest method is:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
Future[Object] future = actorRef.sendRequestReplyFuture[Object](msg);
|
||||
Future[Object] future = actorRef.ask[Object](msg);
|
||||
Object result = future.get(); //Block until result is available, usually bad practice
|
||||
|
||||
This will cause the current thread to block and wait for the ``UntypedActor`` to 'complete' the ``Future`` with it's reply. Due to the dynamic nature of Akka's ``UntypedActor``\s this result can be anything. The safest way to deal with this is to specify the result to an ``Object`` as is shown in the above example. You can also use the expected result type instead of ``Any``, but if an unexpected type were to be returned you will get a ``ClassCastException``. For more elegant ways to deal with this and to use the result without blocking, refer to `Functional Futures`_.
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ Methods that return something (e.g. non-void methods) are turned into ‘send-an
|
|||
User user = service.getUser(username);
|
||||
|
||||
Generally it is preferred to use fire-forget messages as much as possible since they will never block, e.g. consume a resource by waiting. But sometimes they are neat to use since they:
|
||||
|
||||
# Simulates standard Java method dispatch, which is more intuitive for most Java developers
|
||||
# Are a neat to model request-reply
|
||||
# Are useful when you need to do things in a defined order
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ Send messages
|
|||
Messages are sent to an Actor through one of the 'send' methods.
|
||||
* 'sendOneWay' means “fire-and-forget”, e.g. send a message asynchronously and return immediately.
|
||||
* 'sendRequestReply' means “send-and-reply-eventually”, e.g. send a message asynchronously and wait for a reply through a Future. Here you can specify a timeout. Using timeouts is very important. If no timeout is specified then the actor’s default timeout (set by the 'getContext().setTimeout(..)' method in the 'ActorRef') is used. This method throws an 'ActorTimeoutException' if the call timed out.
|
||||
* 'sendRequestReplyFuture' sends a message asynchronously and returns a 'Future'.
|
||||
* 'ask' sends a message asynchronously and returns a 'Future'.
|
||||
|
||||
In all these methods you have the option of passing along your 'ActorRef' context variable. Make it a practice of doing so because it will allow the receiver actors to be able to respond to your message, since the sender reference is sent along with the message.
|
||||
|
||||
|
|
@ -158,11 +158,11 @@ Here are some examples:
|
|||
Send-And-Receive-Future
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Using 'sendRequestReplyFuture' will send a message to the receiving Actor asynchronously and will immediately return a 'Future'.
|
||||
Using 'ask' will send a message to the receiving Actor asynchronously and will immediately return a 'Future'.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
Future future = actorRef.sendRequestReplyFuture("Hello", getContext(), 1000);
|
||||
Future future = actorRef.ask("Hello", getContext(), 1000);
|
||||
|
||||
The 'Future' interface looks like this:
|
||||
|
||||
|
|
@ -170,7 +170,6 @@ The 'Future' interface looks like this:
|
|||
|
||||
interface Future<T> {
|
||||
void await();
|
||||
void awaitBlocking();
|
||||
boolean isCompleted();
|
||||
boolean isExpired();
|
||||
long timeoutInNanos();
|
||||
|
|
@ -183,7 +182,7 @@ So the normal way of working with futures is something like this:
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
Future future = actorRef.sendRequestReplyFuture("Hello", getContext(), 1000);
|
||||
Future future = actorRef.ask("Hello", getContext(), 1000);
|
||||
future.await();
|
||||
if (future.isCompleted()) {
|
||||
Option resultOption = future.result();
|
||||
|
|
@ -306,7 +305,7 @@ On this 'Option' you can invoke 'boolean isDefined()' or 'boolean isEmpty()' to
|
|||
Reply using the sender future
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If a message was sent with the 'sendRequestReply' or 'sendRequestReplyFuture' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it.
|
||||
If a message was sent with the 'sendRequestReply' or 'ask' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it.
|
||||
|
||||
The reference to the Future resides in the 'ActorRef' instance and can be retrieved using 'Option<Promise> getSenderFuture()'.
|
||||
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ The Akka configuration can be made available as property placeholders by using a
|
|||
<akka:property-placeholder location="akka.conf"/>
|
||||
|
||||
<akka:untyped-actor id="actor-1" implementation="com.biz.MyActor" timeout="${akka.actor.timeout}">
|
||||
<akka:remote host="${akka.remote.server.hostname}" port="${akka.remote.server.port}"/>
|
||||
<akka:remote host="${akka.cluster.server.hostname}" port="${akka.cluster.server.port}"/>
|
||||
</akka:untyped-actor>
|
||||
|
||||
Camel configuration
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue